feat: Add send page
This commit is contained in:
parent
f421f4f692
commit
c0b407a6a2
89
account.py
89
account.py
@ -2,6 +2,9 @@ from handywrapper import api
|
||||
import os
|
||||
import dotenv
|
||||
import requests
|
||||
import re
|
||||
import domainLookup
|
||||
import json
|
||||
|
||||
|
||||
dotenv.load_dotenv()
|
||||
@ -16,6 +19,9 @@ response = hsd.getInfo()
|
||||
|
||||
|
||||
def check_account(cookie: str):
|
||||
if cookie is None:
|
||||
return False
|
||||
|
||||
# Check the account
|
||||
if cookie.count(":") < 1:
|
||||
return False
|
||||
@ -81,4 +87,85 @@ def getTransactions(account):
|
||||
info = hsw.getWalletTxHistory(account)
|
||||
if 'error' in info:
|
||||
return []
|
||||
return info
|
||||
return info
|
||||
|
||||
|
||||
def check_address(address: str, allow_name: bool = True, return_address: bool = False):
|
||||
# Check if the address is valid
|
||||
if address.startswith('@'):
|
||||
# Check if the address is a name
|
||||
if not allow_name and not return_address:
|
||||
return 'Invalid address'
|
||||
elif not allow_name and return_address:
|
||||
return False
|
||||
return check_hip2(address[1:])
|
||||
|
||||
# Check if the address is a valid HNS address
|
||||
response = requests.post(f"http://x:{APIKEY}@127.0.0.1:12037",json={
|
||||
"method": "validateaddress",
|
||||
"params": [address]
|
||||
}).json()
|
||||
if response['error'] is not None:
|
||||
if return_address:
|
||||
return False
|
||||
return 'Invalid address'
|
||||
|
||||
if response['result']['isvalid'] == True:
|
||||
if return_address:
|
||||
return address
|
||||
return 'Valid address'
|
||||
|
||||
if return_address:
|
||||
return False
|
||||
return 'Invalid address'
|
||||
|
||||
|
||||
def check_hip2(domain: str):
|
||||
# Check if the domain is valid
|
||||
domain = domain.lower()
|
||||
|
||||
if re.match(r'^[a-zA-Z0-9\-\.]{1,63}$', domain) is None:
|
||||
return 'Invalid address'
|
||||
|
||||
address = domainLookup.hip2(domain)
|
||||
if not check_address(address, False,True):
|
||||
return 'Hip2: Lookup succeeded but address is invalid'
|
||||
return address
|
||||
|
||||
|
||||
|
||||
def send(account,address,amount):
|
||||
account_name = check_account(account)
|
||||
password = ":".join(account.split(":")[1:])
|
||||
|
||||
|
||||
# Unlock the account
|
||||
response = requests.post(f"http://x:{APIKEY}@127.0.0.1:12039/wallet/{account_name}/unlock",
|
||||
json={"passphrase": password,"timeout": 10})
|
||||
|
||||
if response.status_code != 200:
|
||||
return {
|
||||
"error": "Failed to unlock account"
|
||||
}
|
||||
if 'success' not in response.json():
|
||||
return {
|
||||
"error": "Failed to unlock account"
|
||||
}
|
||||
|
||||
# Send the transaction
|
||||
response = requests.post(f"http://x:{APIKEY}@127.0.0.1:12039",json={
|
||||
"method": "sendtoaddress",
|
||||
"params": [address,amount]
|
||||
})
|
||||
if response.status_code != 200:
|
||||
return {
|
||||
"error": "Failed to send transaction"
|
||||
}
|
||||
response = response.json()
|
||||
if 'error' in response:
|
||||
return {
|
||||
"error": json.dumps(response['error'])
|
||||
}
|
||||
return {
|
||||
"tx": response['result']
|
||||
}
|
136
domainLookup.py
Normal file
136
domainLookup.py
Normal file
@ -0,0 +1,136 @@
|
||||
import dns.resolver
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import tempfile
|
||||
import subprocess
|
||||
import binascii
|
||||
import datetime
|
||||
import dns.asyncresolver
|
||||
import httpx
|
||||
from requests_doh import DNSOverHTTPSSession, add_dns_provider, remove_dns_provider
|
||||
|
||||
|
||||
def hip2(domain: str):
|
||||
domain_check = False
|
||||
try:
|
||||
# Get the IP
|
||||
ip = resolve_with_doh(domain)
|
||||
|
||||
# Run the openssl s_client command
|
||||
s_client_command = ["openssl","s_client","-showcerts","-connect",f"{ip}:443","-servername",domain,]
|
||||
|
||||
s_client_process = subprocess.Popen(s_client_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
s_client_output, _ = s_client_process.communicate(input=b"\n")
|
||||
|
||||
certificates = []
|
||||
current_cert = ""
|
||||
for line in s_client_output.split(b"\n"):
|
||||
current_cert += line.decode("utf-8") + "\n"
|
||||
if "-----END CERTIFICATE-----" in line.decode("utf-8"):
|
||||
certificates.append(current_cert)
|
||||
current_cert = ""
|
||||
|
||||
# Remove anything before -----BEGIN CERTIFICATE-----
|
||||
certificates = [cert[cert.find("-----BEGIN CERTIFICATE-----"):] for cert in certificates]
|
||||
|
||||
if certificates:
|
||||
cert = certificates[0]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_cert_file:
|
||||
temp_cert_file.write(cert)
|
||||
temp_cert_file.seek(0) # Move back to the beginning of the temporary file
|
||||
|
||||
tlsa_command = ["openssl","x509","-in",temp_cert_file.name,"-pubkey","-noout","|","openssl","pkey","-pubin","-outform","der","|","openssl","dgst","-sha256","-binary",]
|
||||
|
||||
tlsa_process = subprocess.Popen(" ".join(tlsa_command), shell=True, stdout=subprocess.PIPE)
|
||||
tlsa_output, _ = tlsa_process.communicate()
|
||||
|
||||
tlsa_server = "3 1 1 " + binascii.hexlify(tlsa_output).decode("utf-8")
|
||||
|
||||
|
||||
# Get domains
|
||||
cert_obj = x509.load_pem_x509_certificate(cert.encode("utf-8"), default_backend())
|
||||
|
||||
domains = []
|
||||
for ext in cert_obj.extensions:
|
||||
if ext.oid == x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME:
|
||||
san_list = ext.value.get_values_for_type(x509.DNSName)
|
||||
domains.extend(san_list)
|
||||
|
||||
# Extract the common name (CN) from the subject
|
||||
common_name = cert_obj.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
||||
if common_name:
|
||||
if common_name[0].value not in domains:
|
||||
domains.append(common_name[0].value)
|
||||
|
||||
if domains:
|
||||
if domain in domains:
|
||||
domain_check = True
|
||||
else:
|
||||
# Check if matching wildcard domain exists
|
||||
for d in domains:
|
||||
if d.startswith("*"):
|
||||
if domain.split(".")[1:] == d.split(".")[1:]:
|
||||
domain_check = True
|
||||
break
|
||||
|
||||
|
||||
expiry_date = cert_obj.not_valid_after
|
||||
# Check if expiry date is past
|
||||
if expiry_date < datetime.datetime.now():
|
||||
return "Hip2: Certificate is expired"
|
||||
|
||||
|
||||
else:
|
||||
return "Hip2: No certificate found"
|
||||
|
||||
try:
|
||||
# Check for TLSA record
|
||||
tlsa = resolve_TLSA_with_doh(domain)
|
||||
|
||||
|
||||
if not tlsa:
|
||||
return "Hip2: TLSA lookup failed"
|
||||
else:
|
||||
if tlsa_server == str(tlsa):
|
||||
if domain_check:
|
||||
# Get the Hip2 addresss from /.well-known/wallets/HNS
|
||||
add_dns_provider("HNSDoH", "https://hnsdoh.com/dns-query")
|
||||
|
||||
session = DNSOverHTTPSSession("HNSDoH")
|
||||
r = session.get(f"https://{domain}/.well-known/wallets/HNS",verify=False)
|
||||
return r.text
|
||||
else:
|
||||
return "Hip2: TLSA record matches certificate, but domain does not match certificate"
|
||||
|
||||
else:
|
||||
return "Hip2: TLSA record does not match certificate"
|
||||
|
||||
except Exception as e:
|
||||
return "Hip2: TLSA lookup failed with error: " + str(e)
|
||||
|
||||
|
||||
|
||||
|
||||
# Catch all exceptions
|
||||
except Exception as e:
|
||||
return "Hip2: " + str(e)
|
||||
|
||||
|
||||
def resolve_with_doh(query_name, doh_url="https://hnsdoh.com/dns-query"):
|
||||
with httpx.Client() as client:
|
||||
q = dns.message.make_query(query_name, dns.rdatatype.A)
|
||||
r = dns.query.https(q, doh_url, session=client)
|
||||
|
||||
ip = r.answer[0][0].address
|
||||
return ip
|
||||
|
||||
def resolve_TLSA_with_doh(query_name, doh_url="https://hnsdoh.com/dns-query"):
|
||||
query_name = "_443._tcp." + query_name
|
||||
with httpx.Client() as client:
|
||||
q = dns.message.make_query(query_name, dns.rdatatype.TLSA)
|
||||
r = dns.query.https(q, doh_url, session=client)
|
||||
|
||||
tlsa = r.answer[0][0]
|
||||
return tlsa
|
||||
|
45
grant.md
Normal file
45
grant.md
Normal file
@ -0,0 +1,45 @@
|
||||
What have you built previously?
|
||||
- [HNSHosting](https://hnshosting.au)
|
||||
- [ShakeCities](https://shakecities.com)
|
||||
- [FireWallet](https://firewallet.au)
|
||||
- [Git Profile](https://github.com/nathanwoodburn)
|
||||
|
||||
Project summary
|
||||
A Handshake wallet web ui. This will be a HSD wallet web ui that will allow users to manage their Handshake domains via a web interface. This will allow users to easily manage their domains without having to use the command line or bob. One benefit of this is that it will allow users to easily manage their domains from their mobile devices that don't have access to any HNS wallet. This could be done in a secure way by only allowing connections on local network devices (in addition to requiring the wallet credentials).
|
||||
|
||||
Features:
|
||||
- Login with HSD wallet name + password (by default don't show a list of wallets to login to as this could be a security risk)
|
||||
- View account information in a dashboard
|
||||
- Available balance
|
||||
- Total balance
|
||||
- Pending Transactions
|
||||
- List of domains
|
||||
- List of transactions
|
||||
- Manage domains
|
||||
- Transfer domains
|
||||
- Finalize domains
|
||||
- Edit domains
|
||||
- Revoke domains (with a warning and requiring the account password)
|
||||
- Manage wallet
|
||||
- Send HNS
|
||||
- Receive HNS
|
||||
- Auctions
|
||||
- View bids on domain
|
||||
- Open auction
|
||||
- Bid on auction
|
||||
- Reveal bid
|
||||
- Redeem bid
|
||||
- Register domain
|
||||
|
||||
Completion requirements:
|
||||
- Basic functionality including
|
||||
- View info
|
||||
- Send/Receive HNS
|
||||
- Manage domains
|
||||
|
||||
After the initial version is completed I will be looking to add more features including the above mentioned features.
|
||||
|
||||
|
||||
The initial version will be completed in 2-3 weeks with a fully fledged version released later as the features are developed and tested.
|
||||
|
||||
You can contact me at handshake @ nathan.woodburn.au
|
112
main.py
112
main.py
@ -4,6 +4,7 @@ import dotenv
|
||||
import requests
|
||||
import account as account_module
|
||||
import render
|
||||
import re
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
@ -17,6 +18,9 @@ def index():
|
||||
return redirect("/login")
|
||||
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
if not account:
|
||||
return redirect("/logout")
|
||||
|
||||
balance = account_module.getBalance(account)
|
||||
available = balance['available']
|
||||
total = balance['total']
|
||||
@ -27,11 +31,13 @@ def index():
|
||||
|
||||
pending = account_module.getPendingTX(account)
|
||||
domains = account_module.getDomains(account)
|
||||
domain_count = len(domains)
|
||||
domains = render.domains(domains)
|
||||
|
||||
|
||||
|
||||
return render_template("index.html", account=account, available=available,
|
||||
total=total, pending=pending, domains=domains)
|
||||
total=total, pending=pending, domains=domains, domain_count=domain_count)
|
||||
|
||||
|
||||
@app.route('/tx')
|
||||
@ -49,15 +55,93 @@ def transactions():
|
||||
return render_template("tx.html", account=account, tx=transactions)
|
||||
|
||||
|
||||
@app.route('/send')
|
||||
def send_page():
|
||||
# Check if the user is logged in
|
||||
if request.cookies.get("account") is None:
|
||||
return redirect("/login")
|
||||
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
max = account_module.getBalance(account)['available']
|
||||
# Subtract approx fee of 0.02
|
||||
max = max - 0.02
|
||||
|
||||
message = ''
|
||||
address = ''
|
||||
amount = ''
|
||||
|
||||
if 'message' in request.args:
|
||||
message = request.args.get("message")
|
||||
if 'address' in request.args:
|
||||
address = request.args.get("address")
|
||||
if 'amount' in request.args:
|
||||
amount = request.args.get("amount")
|
||||
|
||||
|
||||
return render_template("send.html", account=account,max=max,message=message,
|
||||
address=address,amount=amount)
|
||||
|
||||
@app.route('/send', methods=["POST"])
|
||||
def send():
|
||||
if request.cookies.get("account") is None:
|
||||
return redirect("/login")
|
||||
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
if not account:
|
||||
return redirect("/logout")
|
||||
|
||||
# Get the address and amount
|
||||
address = request.form.get("address")
|
||||
amount = request.form.get("amount")
|
||||
|
||||
if address is None or amount is None:
|
||||
return redirect("/send?message=Invalid address or amount&address=" + address + "&amount=" + amount)
|
||||
|
||||
address_check = account_module.check_address(address,True,True)
|
||||
if not address_check:
|
||||
return redirect("/send?message=Invalid address&address=" + address + "&amount=" + amount)
|
||||
|
||||
address = address_check
|
||||
# Check if the amount is valid
|
||||
if re.match(r"^\d+(\.\d+)?$", amount) is None:
|
||||
return redirect("/send?message=Invalid amount&address=" + address + "&amount=" + amount)
|
||||
|
||||
# Check if the amount is valid
|
||||
amount = float(amount)
|
||||
if amount <= 0:
|
||||
return redirect("/send?message=Invalid amount&address=" + address + "&amount=" + str(amount))
|
||||
|
||||
if amount > account_module.getBalance(account)['available'] - 0.02:
|
||||
return redirect("/send?message=Not enough funds to transfer&address=" + address + "&amount=" + str(amount))
|
||||
|
||||
# Send the transaction
|
||||
response = account_module.send(request.cookies.get("account"),address,amount)
|
||||
if 'error' in response:
|
||||
return redirect("/send?message=" + response['error'] + "&address=" + address + "&amount=" + str(amount))
|
||||
|
||||
return redirect("/success?tx=" + response['tx'])
|
||||
|
||||
@app.route('/success')
|
||||
def success():
|
||||
# Check if the user is logged in
|
||||
if request.cookies.get("account") is None:
|
||||
return redirect("/login")
|
||||
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
if not account:
|
||||
return redirect("/logout")
|
||||
|
||||
tx = request.args.get("tx")
|
||||
return render_template("success.html", account=account, tx=tx)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route('/checkaddress')
|
||||
def check_address():
|
||||
address = request.args.get("address")
|
||||
if address is None:
|
||||
return jsonify({"result": "Invalid address"})
|
||||
|
||||
return jsonify({"result": account_module.check_address(address)})
|
||||
|
||||
#region Account
|
||||
@app.route('/login')
|
||||
@ -65,9 +149,8 @@ def login():
|
||||
if 'message' in request.args:
|
||||
return render_template("login.html", error=request.args.get("message"))
|
||||
|
||||
accounts = account_module.getAccounts()
|
||||
|
||||
return render_template("login.html", accounts=accounts)
|
||||
return render_template("login.html")
|
||||
|
||||
@app.route('/login', methods=["POST"])
|
||||
def login_post():
|
||||
@ -103,6 +186,15 @@ def logout():
|
||||
def send_assets(path):
|
||||
return send_from_directory('templates/assets', path)
|
||||
|
||||
# Try path
|
||||
@app.route('/<path:path>')
|
||||
def try_path(path):
|
||||
if os.path.isfile("templates/" + path + ".html"):
|
||||
return render_template(path + ".html")
|
||||
else:
|
||||
return page_not_found(404)
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
|
@ -2,4 +2,7 @@ handywrapper
|
||||
flask
|
||||
python-dotenv
|
||||
requests
|
||||
gunicorn
|
||||
gunicorn
|
||||
dnspython
|
||||
cryptography
|
||||
requests-doh
|
@ -5,13 +5,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Page Not Found - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
@ -21,13 +19,17 @@
|
||||
<div id="wrapper">
|
||||
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="#">
|
||||
<div class="sidebar-brand-icon rotate-n-15"><i class="fas fa-laugh-wink"></i></div>
|
||||
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||
</a>
|
||||
<hr class="sidebar-divider my-0">
|
||||
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||
<li class="nav-item"><a class="nav-link" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/send"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="margin-right: 4px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
|
||||
</svg><span>Send HNS</span></a></li>
|
||||
</ul>
|
||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||
</div>
|
||||
|
BIN
templates/assets/img/favicon.png
Normal file
BIN
templates/assets/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
@ -5,13 +5,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Dashboard - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
@ -21,13 +19,17 @@
|
||||
<div id="wrapper">
|
||||
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="#">
|
||||
<div class="sidebar-brand-icon rotate-n-15"><i class="fas fa-laugh-wink"></i></div>
|
||||
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||
</a>
|
||||
<hr class="sidebar-divider my-0">
|
||||
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||
<li class="nav-item"><a class="nav-link active" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/send"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="margin-right: 4px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
|
||||
</svg><span>Send HNS</span></a></li>
|
||||
</ul>
|
||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||
</div>
|
||||
@ -93,19 +95,13 @@
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center no-gutters">
|
||||
<div class="col me-2">
|
||||
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Tasks</span></div>
|
||||
<div class="text-uppercase text-info fw-bold text-xs mb-1"><span>Domains</span></div>
|
||||
<div class="row g-0 align-items-center">
|
||||
<div class="col-auto">
|
||||
<div class="text-dark fw-bold h5 mb-0 me-3"><span>50%</span></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="progress progress-sm">
|
||||
<div class="progress-bar bg-info" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%;"><span class="visually-hidden">50%</span></div>
|
||||
</div>
|
||||
<div class="text-dark fw-bold h5 mb-0 me-3"><span>{{domain_count}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto"><i class="fas fa-clipboard-list fa-2x text-gray-300"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,13 +5,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Login - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
</head>
|
||||
|
@ -5,13 +5,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Register - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
|
127
templates/send.html
Normal file
127
templates/send.html
Normal file
@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="light" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Send - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
<div id="wrapper">
|
||||
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="#">
|
||||
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||
</a>
|
||||
<hr class="sidebar-divider my-0">
|
||||
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||
<li class="nav-item"><a class="nav-link" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/send"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="margin-right: 4px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
|
||||
</svg><span>Send HNS</span></a></li>
|
||||
</ul>
|
||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="d-flex flex-column" id="content-wrapper">
|
||||
<div id="content">
|
||||
<nav class="navbar navbar-expand bg-white shadow mb-4 topbar static-top navbar-light">
|
||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search">
|
||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain"><button class="btn btn-primary py-0" type="button"><i class="fas fa-search"></i></button></div>
|
||||
</form>
|
||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||
<form class="me-auto navbar-search w-100">
|
||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for ...">
|
||||
<div class="input-group-append"><button class="btn btn-primary py-0" type="button"><i class="fas fa-search"></i></button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown no-arrow">
|
||||
<div class="nav-item dropdown no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><span class="d-none d-lg-inline me-2 text-gray-600 small">{{account}}</span><img class="border rounded-circle img-profile" src="/assets/img/HNS.png"></a>
|
||||
<div class="dropdown-menu shadow dropdown-menu-end animated--grow-in"><a class="dropdown-item" href="/logout"><i class="fas fa-sign-out-alt fa-sm fa-fw me-2 text-gray-400"></i> Logout</a></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid text-center">
|
||||
<h3 class="text-center text-dark mb-1">Send HNS</h3><span style="color: rgb(255,0,0);">{{message}}</span>
|
||||
</div>
|
||||
<form style="margin: auto;max-width: 400px;margin-top: 50px;" method="post">
|
||||
<div style="margin-top: 25px;"><label class="form-label">Send to</label><input class="form-control" type="text" id="address" placeholder="Address or @domain" name="address" value="{{address}}"><span id="addressValid"></span><script>
|
||||
function checkAddress(inputValue) {
|
||||
// Make API request to "/checkaddress"
|
||||
var apiUrl = '/checkaddress?address=' + encodeURIComponent(inputValue);
|
||||
|
||||
fetch(apiUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Update the content of the span with the response data
|
||||
var addressCheckSpan = document.getElementById('addressValid');
|
||||
addressCheckSpan.textContent = data.result; // You can replace 'addressInfo' with the actual property you receive from the API
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to handle input field blur event
|
||||
function handleBlur() {
|
||||
var inputField = document.getElementById('address');
|
||||
var inputValue = inputField.value;
|
||||
|
||||
// Check if the input value is not empty
|
||||
if (inputValue.trim() !== '') {
|
||||
checkAddress(inputValue);
|
||||
} else {
|
||||
var addressCheckSpan = document.getElementById('addressValid');
|
||||
addressCheckSpan.textContent = 'Invalid address';
|
||||
}
|
||||
}
|
||||
|
||||
// Add a blur event listener to the input field
|
||||
var inputField = document.getElementById('address');
|
||||
inputField.addEventListener('blur', handleBlur);
|
||||
</script></div>
|
||||
<div style="margin-top: 25px;"><label class="form-label">Amount</label><input class="form-control" type="text" id="amount" placeholder="0.00000" name="amount" style="margin-bottom: 10px;" value="{{amount}}"><span>Available to send: {{max}} HNS</span><button class="btn btn-primary btn-sm" id="maxButton" type="button" style="margin-left: 25px;">Send Max</button><script>
|
||||
// Function to fill in the form input
|
||||
function fillInput() {
|
||||
// Get the input field and set its value
|
||||
var inputField = document.getElementById('amount');
|
||||
inputField.value = '{{max}}';
|
||||
}
|
||||
|
||||
// Add a click event listener to the button
|
||||
var fillButton = document.getElementById('maxButton');
|
||||
fillButton.addEventListener('click', fillInput);
|
||||
</script></div>
|
||||
<div class="text-center" style="margin-top: 25px;"><input class="btn btn-primary" type="submit" value="Send"></div>
|
||||
</form>
|
||||
</div>
|
||||
<footer class="bg-white sticky-footer">
|
||||
<div class="container my-auto">
|
||||
<div class="text-center my-auto copyright"><span>Copyright © FireWallet 2023</span></div>
|
||||
</div>
|
||||
</footer>
|
||||
</div><a class="border rounded d-inline scroll-to-top" href="#page-top"><i class="fas fa-angle-up"></i></a>
|
||||
</div>
|
||||
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="/assets/js/script.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
82
templates/success.html
Normal file
82
templates/success.html
Normal file
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="light" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Success - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
<div id="wrapper">
|
||||
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="#">
|
||||
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||
</a>
|
||||
<hr class="sidebar-divider my-0">
|
||||
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||
<li class="nav-item"><a class="nav-link" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/send"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="margin-right: 4px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
|
||||
</svg><span>Send HNS</span></a></li>
|
||||
</ul>
|
||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="d-flex flex-column" id="content-wrapper">
|
||||
<div id="content">
|
||||
<nav class="navbar navbar-expand bg-white shadow mb-4 topbar static-top navbar-light">
|
||||
<div class="container-fluid"><button class="btn btn-link d-md-none rounded-circle me-3" id="sidebarToggleTop" type="button"><i class="fas fa-bars"></i></button>
|
||||
<form class="d-none d-sm-inline-block me-auto ms-md-3 my-2 my-md-0 mw-100 navbar-search">
|
||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for domain"><button class="btn btn-primary py-0" type="button"><i class="fas fa-search"></i></button></div>
|
||||
</form>
|
||||
<ul class="navbar-nav flex-nowrap ms-auto">
|
||||
<li class="nav-item dropdown d-sm-none no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><i class="fas fa-search"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-end p-3 animated--grow-in" aria-labelledby="searchDropdown">
|
||||
<form class="me-auto navbar-search w-100">
|
||||
<div class="input-group"><input class="bg-light form-control border-0 small" type="text" placeholder="Search for ...">
|
||||
<div class="input-group-append"><button class="btn btn-primary py-0" type="button"><i class="fas fa-search"></i></button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown no-arrow">
|
||||
<div class="nav-item dropdown no-arrow"><a class="dropdown-toggle nav-link" aria-expanded="false" data-bs-toggle="dropdown" href="#"><span class="d-none d-lg-inline me-2 text-gray-600 small">{{account}}</span><img class="border rounded-circle img-profile" src="/assets/img/HNS.png"></a>
|
||||
<div class="dropdown-menu shadow dropdown-menu-end animated--grow-in"><a class="dropdown-item" href="/logout"><i class="fas fa-sign-out-alt fa-sm fa-fw me-2 text-gray-400"></i> Logout</a></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<h3 class="text-center text-dark mb-1">Transaction send successfully</h3>
|
||||
</div>
|
||||
<div class="card" style="max-width: 500px;margin: auto;margin-top: 50px;">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Your transaction has been sent and will be mined soon.</h4><span style="display: block;font-size: 12px;">TX: {{tx}}</span><span style="display: block;">Check your transaction on a block explorer</span><a class="card-link" href="https://niami.io/tx/{{tx}}" target="_blank">Niami</a><a class="card-link" href="https://3xpl.com/handshake/transaction/{{tx}}" target="_blank">3xpl</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="bg-white sticky-footer">
|
||||
<div class="container my-auto">
|
||||
<div class="text-center my-auto copyright"><span>Copyright © FireWallet 2023</span></div>
|
||||
</div>
|
||||
</footer>
|
||||
</div><a class="border rounded d-inline scroll-to-top" href="#page-top"><i class="fas fa-angle-up"></i></a>
|
||||
</div>
|
||||
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="/assets/js/script.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -5,13 +5,11 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>Transactions - FireWallet</title>
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="670x700" href="/assets/img/HNS.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="icon" type="image/png" sizes="900x768" href="/assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i&display=swap">
|
||||
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css">
|
||||
@ -21,13 +19,17 @@
|
||||
<div id="wrapper">
|
||||
<nav class="navbar align-items-start sidebar sidebar-dark accordion bg-gradient-primary p-0 navbar-dark">
|
||||
<div class="container-fluid d-flex flex-column p-0"><a class="navbar-brand d-flex justify-content-center align-items-center sidebar-brand m-0" href="#">
|
||||
<div class="sidebar-brand-icon rotate-n-15"><i class="fas fa-laugh-wink"></i></div>
|
||||
<div class="sidebar-brand-icon"><img src="/assets/img/favicon.png" width="44"></div>
|
||||
<div class="sidebar-brand-text mx-3"><span>FireWallet</span></div>
|
||||
</a>
|
||||
<hr class="sidebar-divider my-0">
|
||||
<ul class="navbar-nav text-light" id="accordionSidebar">
|
||||
<li class="nav-item"><a class="nav-link" href="/"><i class="fas fa-tachometer-alt"></i><span>Dashboard</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tx"><i class="fas fa-table"></i><span>Transactions</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/send"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="margin-right: 4px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
|
||||
</svg><span>Send HNS</span></a></li>
|
||||
</ul>
|
||||
<div class="text-center d-none d-md-inline"><button class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button></div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user