Update Auctions page to include more info and be easier to read #1

Merged
nathanwoodburn merged 7 commits from dev into main 2025-07-12 15:10:58 +10:00
5 changed files with 148 additions and 66 deletions

Binary file not shown.

View File

@@ -109,8 +109,7 @@ def createWallet(account: str, password: str):
}
# Create the account
# Python wrapper doesn't support this yet
response = requests.put(
f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}")
response = requests.put(get_wallet_api_url(f"wallet/{account}"))
if response.status_code != 200:
return {
"error": {
@@ -123,7 +122,7 @@ def createWallet(account: str, password: str):
seed = seed['mnemonic']['phrase']
# Encrypt the wallet (python wrapper doesn't support this yet)
response = requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/passphrase",
response = requests.post(get_wallet_api_url(f"/wallet/{account}/passphrase"),
json={"passphrase": password})
return {
@@ -147,8 +146,7 @@ def importWallet(account: str, password: str, seed: str):
"mnemonic": seed,
}
response = requests.put(
f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}", json=data)
response = requests.put(get_wallet_api_url(f"/wallet/{account}"), json=data)
if response.status_code != 200:
return {
"error": {
@@ -186,7 +184,6 @@ def selectWallet(account: str):
}
}
def getBalance(account: str):
# Get the total balance
info = hsw.getBalance('default', account)
@@ -251,11 +248,9 @@ def getPendingTX(account: str):
def getDomains(account, own=True):
if own:
response = requests.get(
f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/name?own=true")
response = requests.get(get_wallet_api_url(f"/wallet/{account}/name?own=true"))
else:
response = requests.get(
f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/name")
response = requests.get(get_wallet_api_url(f"/wallet/{account}/name"))
info = response.json()
if SHOW_EXPIRED:
@@ -339,11 +334,9 @@ def getTransactions(account, page=1, limit=100):
lastTX = getTXFromPage(account, page-1, limit)
if lastTX:
response = requests.get(
f'http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/tx/history?reverse=true&limit={limit}&after={lastTX}')
response = requests.get(get_wallet_api_url(f"/wallet/{account}/tx/history?reverse=true&limit={limit}&after={lastTX}"))
elif page == 1:
response = requests.get(
f'http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account}/tx/history?reverse=true&limit={limit}')
response = requests.get(get_wallet_api_url(f"/wallet/{account}/tx/history?reverse=true&limit={limit}"))
else:
return []
@@ -383,7 +376,7 @@ def check_address(address: str, allow_name: bool = True, return_address: bool =
return check_hip2(address[1:])
# Check if the address is a valid HNS address
response = requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_NODE_PORT}", json={
response = requests.post(get_node_api_url(), json={
"method": "validateaddress",
"params": [address]
}).json()
@@ -431,8 +424,6 @@ def send(account, address, amount):
response = hsw.rpc_walletPassphrase(password, 10)
# Unlock the account
# response = requests.post(f"http://x:{APIKEY}@{ip}:{HSD_WALLET_PORT}/wallet/{account_name}/unlock",
# json={"passphrase": password,"timeout": 10})
if response['error'] is not None:
if response['error']['message'] != "Wallet is not encrypted.":
return {
@@ -454,9 +445,18 @@ def send(account, address, amount):
def isOwnDomain(account, name: str):
domains = getDomains(account)
for domain in domains:
if domain['name'] == name:
# Get domain
domain_info = getDomain(name)
owner = getAddressFromCoin(domain_info['info']['owner']['hash'],domain_info['info']['owner']['index'])
# Select the account
hsw.rpc_selectWallet(account)
account = hsw.rpc_getAccount(owner)
if 'error' in account and account['error'] is not None:
return False
if 'result' not in account:
return False
if account['result'] == 'default':
return True
return False
@@ -474,20 +474,14 @@ def getDomain(domain: str):
def getAddressFromCoin(coinhash: str, coinindex = 0):
# Get the address from the hash
response = requests.get(f"http://x:{HSD_API}@{HSD_IP}:{HSD_NODE_PORT}/coin/{coinhash}/{coinindex}")
response = requests.get(get_node_api_url(f"coin/{coinhash}/{coinindex}"))
if response.status_code != 200:
return {
"error": {
"message": "Error getting address from coin"
}
}
print(f"Error getting address from coin: {response.text}")
return "No Owner"
data = response.json()
if 'address' not in data:
return {
"error": {
"message": "Error getting address from coin"
}
}
print(json.dumps(data, indent=4))
return "No Owner"
return data['address']
@@ -783,7 +777,7 @@ def revealAll(account):
}
}
return requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}", json={"method": "sendbatch", "params": [[["REVEAL"]]]}).json()
return requests.post(get_wallet_api_url(), json={"method": "sendbatch", "params": [[["REVEAL"]]]}).json()
except Exception as e:
return {
"error": {
@@ -817,7 +811,7 @@ def redeemAll(account):
}
}
return requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}", json={"method": "sendbatch", "params": [[["REDEEM"]]]}).json()
return requests.post(get_wallet_api_url(), json={"method": "sendbatch", "params": [[["REDEEM"]]]}).json()
except Exception as e:
return {
"error": {
@@ -1089,7 +1083,7 @@ def sendBatch(account, batch):
"message": response['error']['message']
}
}
response = requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}", json={
response = requests.post(get_wallet_api_url(), json={
"method": "sendbatch",
"params": [batch]
}).json()
@@ -1138,7 +1132,7 @@ def createBatch(account, batch):
"message": response['error']['message']
}
}
response = requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}", json={
response = requests.post(get_wallet_api_url(), json={
"method": "createbatch",
"params": [batch]
}).json()
@@ -1198,7 +1192,7 @@ def zapTXs(account):
}
try:
response = requests.post(f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}/wallet/{account_name}/zap",
response = requests.post(get_wallet_api_url(f"/wallet/{account_name}/zap"),
json={"age": age,
"account": "default"
})
@@ -1327,3 +1321,25 @@ def generateReport(account, format="{name},{expiry},{value},{maxBid}"):
def convertHNS(value: int):
return value/1000000
return value/1000000
def get_node_api_url(path=''):
"""Construct a URL for the HSD node API."""
base_url = f"http://x:{HSD_API}@{HSD_IP}:{HSD_NODE_PORT}"
if path:
# Ensure path starts with a slash if it's not empty
if not path.startswith('/'):
path = f'/{path}'
return f"{base_url}{path}"
return base_url
def get_wallet_api_url(path=''):
"""Construct a URL for the HSD wallet API."""
base_url = f"http://x:{HSD_API}@{HSD_IP}:{HSD_WALLET_PORT}"
if path:
# Ensure path starts with a slash if it's not empty
if not path.startswith('/'):
path = f'/{path}'
return f"{base_url}{path}"
return base_url

61
main.py
View File

@@ -32,6 +32,30 @@ revokeCheck = random.randint(100000,999999)
THEME = os.getenv("THEME")
def blocks_to_time(blocks: int) -> str:
"""
Convert blocks to time in a human-readable format.
Blocks are mined approximately every 10 minutes.
"""
if blocks < 0:
return "Invalid time"
if blocks < 6:
return f"{blocks * 10} mins"
elif blocks < 144:
hours = blocks // 6
minutes = (blocks % 6) * 10
return f"{hours} hrs {minutes} mins"
else:
days = blocks // 144
hours = (blocks % 144) // 6
return f"{days} days {hours} hrs"
@app.route('/')
def index():
# Check if the user is logged in
@@ -445,10 +469,11 @@ def search():
state="AVAILABLE", next="Available Now",plugins=plugins)
state = domain['info']['state']
stats = domain['info']['stats']
if state == 'CLOSED':
if domain['info']['registered']:
state = 'REGISTERED'
expires = domain['info']['stats']['daysUntilExpire']
expires = stats['daysUntilExpire']
next = f"Expires in ~{expires} days"
else:
state = 'AVAILABLE'
@@ -456,11 +481,11 @@ def search():
elif state == "REVOKED":
next = "Available Now"
elif state == 'OPENING':
next = "Bidding opens in ~" + str(domain['info']['stats']['blocksUntilBidding']) + " blocks"
next = f"Bidding opens in {str(stats['blocksUntilBidding'])} blocks (~{blocks_to_time(stats['blocksUntilBidding'])})"
elif state == 'BIDDING':
next = "Reveal in ~" + str(domain['info']['stats']['blocksUntilReveal']) + " blocks"
next = f"Reveal in {str(stats['blocksUntilReveal'])} blocks (~{blocks_to_time(stats['blocksUntilReveal'])})"
elif state == 'REVEAL':
next = "Reveal ends in ~" + str(domain['info']['stats']['blocksUntilClose']) + " blocks"
next = f"Reveal ends in {str(stats['blocksUntilClose'])} blocks (~{blocks_to_time(stats['blocksUntilClose'])})"
@@ -502,10 +527,7 @@ def manage(domain: str):
domain = domain.lower()
own_domains = account_module.getDomains(account)
own_domains = [x['name'] for x in own_domains]
own_domains = [x.lower() for x in own_domains]
if domain not in own_domains:
if not account_module.isOwnDomain(account, domain):
return redirect("/search?q=" + domain)
domain_info = account_module.getDomain(domain)
@@ -514,7 +536,10 @@ def manage(domain: str):
rendered=renderDomain(domain),
domain=domain, error=domain_info['error'])
if domain_info['info'] is not None and 'stats' in domain_info['info'] and 'daysUntilExpire' in domain_info['info']['stats']:
expiry = domain_info['info']['stats']['daysUntilExpire']
else:
expiry = "Unknown"
dns = account_module.getDNS(domain)
raw_dns = str(dns).replace("'",'"')
dns = render.dns(dns)
@@ -900,7 +925,7 @@ def auction(domain):
reveal['bid'] = revealInfo
bids = render.bids(bids,reveals)
stats = domainInfo['info']['stats'] if 'stats' in domainInfo['info'] else {}
if state == 'CLOSED':
if not domainInfo['info']['registered']:
if account_module.isOwnDomain(account,domain):
@@ -928,11 +953,21 @@ def auction(domain):
next = "Available Now"
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
elif state == 'OPENING':
next = "Bidding opens in ~" + str(domainInfo['info']['stats']['blocksUntilBidding']) + " blocks"
next = f"Bidding opens in {str(stats['blocksUntilBidding'])} blocks (~{blocks_to_time(stats['blocksUntilBidding'])})"
elif state == 'BIDDING':
next = "Reveal in ~" + str(domainInfo['info']['stats']['blocksUntilReveal']) + " blocks"
next = f"Reveal in {stats['blocksUntilReveal']} blocks (~{blocks_to_time(stats['blocksUntilReveal'])})"
if stats['blocksUntilReveal'] == 1:
next += "<br>Bidding no longer possible"
elif stats['blocksUntilReveal'] == 2:
next += "<br>LAST CHANCE TO BID"
elif stats['blocksUntilReveal'] == 3:
next += f"<br>Next block is last chance to bid"
elif stats['blocksUntilReveal'] < 6:
next += f"<br>Last chance to bid in {stats['blocksUntilReveal']-2} blocks"
elif state == 'REVEAL':
next = "Reveal ends in ~" + str(domainInfo['info']['stats']['blocksUntilClose']) + " blocks"
next = f"Reveal ends in {str(stats['blocksUntilClose'])} blocks (~{blocks_to_time(stats['blocksUntilClose'])})"
next_action = f'<a href="/auction/{domain}/reveal">Reveal All</a>'
message = ''
@@ -1070,7 +1105,7 @@ def reveal_auction(domain):
return redirect("/logout")
domain = domain.lower()
response = account_module(request.cookies.get("account"),domain)
response = account_module.revealAuction(request.cookies.get("account"),domain)
if 'error' in response:
return redirect("/auction/" + domain + "?message=" + response['error']['message'])
return redirect("/success?tx=" + response['hash'])

View File

@@ -210,7 +210,6 @@ def dns(data, edit=False):
html_output = ""
index = 0
for entry in data:
print(entry, flush=True)
html_output += f"<tr><td>{entry['type']}</td>\n"
if entry['type'] != 'DS' and not entry['type'].startswith("GLUE") and not entry['type'].startswith("SYNTH"):
@@ -279,30 +278,61 @@ def timestamp_to_readable_time(timestamp):
return readable_time
def bids(bids,reveals):
html = ''
# Create a list to hold bid data for sorting
bid_data = []
# Prepare data for sorting
for bid in bids:
lockup = bid['lockup']
lockup = lockup / 1000000
html += "<tr>"
html += f"<td>{lockup:,.2f} HNS</td>"
lockup = bid['lockup'] / 1000000
revealed = False
value = 0
# Check if this bid has been revealed
for reveal in reveals:
if reveal['bid'] == bid['prevout']['hash']:
revealed = True
value = reveal['value']
value = value / 1000000
html += f"<td>{value:,.2f} HNS</td>"
bidValue = lockup - value
html += f"<td>{bidValue:,.2f} HNS</td>"
value = reveal['value'] / 1000000
break
if not revealed:
# Store all relevant information for sorting and display
bid_data.append({
'bid': bid,
'lockup': lockup,
'revealed': revealed,
'value': value,
'sort_value': value if revealed else lockup # Use value for sorting if revealed, otherwise lockup
})
# Sort by the sort_value in descending order (highest first)
bid_data.sort(key=lambda x: x['sort_value'], reverse=True)
# Generate HTML from sorted data
html = ''
for data in bid_data:
bid = data['bid']
lockup = data['lockup']
revealed = data['revealed']
value = data['value']
html += "<tr>"
html += f"<td>{lockup:,.2f} HNS</td>"
if revealed:
bidValue = lockup - value
html += f"<td>{value:,.2f} HNS</td>"
html += f"<td>{bidValue:,.2f} HNS</td>"
else:
html += f"<td>Hidden until reveal</td>"
html += f"<td>Hidden until reveal</td>"
if bid['own']:
html += "<td>You</td>"
else:
html += "<td>Unknown</td>"
html += f"<td>Unknown</td>"
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' href='{TX_EXPLORER_URL}{bid['prevout']['hash']}'>Bid TX 🔗</a></td>"
html += "</tr>"
return html

View File

@@ -68,7 +68,7 @@
<div class="card-body">
<div class="stick-right">{{next_action|safe}}</div>
<h4 class="card-title">{{rendered}}</h4>
<h6 class="text-muted mb-2 card-subtitle">{{next}}</h6>
<h6 class="text-muted mb-2 card-subtitle">{{next | safe}}</h6>
</div>
</div>
</div>
@@ -93,6 +93,7 @@
<th>Bid</th>
<th>Blind</th>
<th>Owner</th>
<th></th>
</tr>
</thead>
<tbody>