feat: Add Bitcoin support, BTC chips, and improve UX
Some checks failed
Workflow push to Dappbundle / Build (push) Has been cancelled
Some checks failed
Workflow push to Dappbundle / Build (push) Has been cancelled
- Implement Bitcoin address generation (Bech32) and WIF display. - Add BTC chips to searched addresses list. - Add input validation: reject FLO/BTC addresses with clear intent messages. - Update search placeholder and errors to use 'MNT'. - Fix desktop notification panel positioning.
This commit is contained in:
parent
72351dd5b8
commit
6be4402f6f
70
index.html
70
index.html
@ -468,9 +468,10 @@
|
||||
const apply = () => {
|
||||
if (window.matchMedia('(min-width: 640px)').matches) {
|
||||
Object.assign(panel.style, {
|
||||
left: 'calc(10rem + 1rem)',
|
||||
bottom: '1rem',
|
||||
top: 'auto',
|
||||
// Desktop: top-right to avoid overlapping "Searched addresses" (which is left-side)
|
||||
left: 'auto',
|
||||
bottom: 'auto',
|
||||
top: '1rem',
|
||||
right: '1rem',
|
||||
zIndex: '1000'
|
||||
});
|
||||
@ -663,7 +664,7 @@
|
||||
</h2>
|
||||
<sm-form oninvalid="handleInvalidSearch()">
|
||||
<div id="input_wrapper">
|
||||
<sm-input id="check_balance_input" class="password-field flex-1" placeholder="Address, private key, or tx hash"
|
||||
<sm-input id="check_balance_input" class="password-field flex-1" placeholder="MNT Address, private key, or tx hash"
|
||||
type="password" animate>
|
||||
<svg class="icon" slot="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <g> <rect fill="none" height="24" width="24"></rect> </g> <g> <path d="M21,10h-8.35C11.83,7.67,9.61,6,7,6c-3.31,0-6,2.69-6,6s2.69,6,6,6c2.61,0,4.83-1.67,5.65-4H13l2,2l2-2l2,2l4-4.04L21,10z M7,15c-1.65,0-3-1.35-3-3c0-1.65,1.35-3,3-3s3,1.35,3,3C10,13.65,8.65,15,7,15z"> </path> </g> </svg>
|
||||
<label slot="right" class="interact">
|
||||
@ -734,13 +735,14 @@
|
||||
}
|
||||
const renderedContacts = []
|
||||
for (const floAddress in contacts) {
|
||||
const { ethAddress } = contacts[floAddress]
|
||||
const { ethAddress, btcAddress } = contacts[floAddress]
|
||||
renderedContacts.push(html`
|
||||
<li class="contact" .dataset=${{ floAddress, ethAddress }}>
|
||||
<li class="contact" .dataset=${{ floAddress, ethAddress, btcAddress }}>
|
||||
${floAddress === ethAddress ? html`
|
||||
`: html`
|
||||
<sm-chips onchange=${e => e.target.closest('.contact').querySelector('sm-copy').value = e.target.value}>
|
||||
<sm-chip value=${floAddress} selected>FLO</sm-chip>
|
||||
${btcAddress ? html`<sm-chip value=${btcAddress}>BTC</sm-chip>` : ''}
|
||||
<sm-chip value=${ethAddress}>MNT</sm-chip>
|
||||
</sm-chips>
|
||||
`}
|
||||
@ -777,6 +779,19 @@
|
||||
}
|
||||
|
||||
// The search bar is versatile: it accepts addresses, private keys, or transaction hashes.
|
||||
|
||||
// Reject FLO addresses (start with F)
|
||||
if (/^F[a-km-zA-HJ-NP-Z1-9]{26,34}$/.test(keyToConvert)) {
|
||||
notify('FLO addresses are not supported. Please use a MNT address or private key.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Reject BTC addresses (legacy: 1/3, segwit: bc1)
|
||||
if (/^(1|3)[a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(keyToConvert) || /^bc1[a-z0-9]{39,59}$/i.test(keyToConvert)) {
|
||||
notify('BTC addresses are not supported. Please use a MNT address or private key.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (ethOperator.isValidAddress(keyToConvert)) {
|
||||
ethAddress = keyToConvert
|
||||
}
|
||||
@ -787,12 +802,37 @@
|
||||
// Otherwise, try to convert as private key
|
||||
else {
|
||||
try {
|
||||
let isHex = false;
|
||||
let btcAddress;
|
||||
|
||||
if (/^[0-9a-fA-F]{64}$/.test(keyToConvert)) {
|
||||
keyToConvert = coinjs.privkey2wif(keyToConvert)
|
||||
isHex = true;
|
||||
}
|
||||
const ethPrivateKey = coinjs.wif2privkey(keyToConvert).privkey;
|
||||
ethAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey)
|
||||
floAddress = floCrypto.getFloID(keyToConvert)
|
||||
|
||||
|
||||
|
||||
if (!isHex) {
|
||||
floAddress = floCrypto.getFloID(keyToConvert)
|
||||
try {
|
||||
btcAddress = btcOperator.bech32Address(keyToConvert)
|
||||
} catch (e) { console.error(e) }
|
||||
}
|
||||
|
||||
// Save to indexed DB
|
||||
compactIDB.readData('contacts', floAddress || ethAddress).then(result => {
|
||||
if (result) return
|
||||
compactIDB.addData('contacts', {
|
||||
ethAddress,
|
||||
btcAddress
|
||||
}, floAddress || ethAddress).then(() => {
|
||||
renderSearchedAddressList()
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
notify('Invalid input. Please enter a valid Ethereum address or private key.', 'error');
|
||||
return;
|
||||
@ -1962,8 +2002,24 @@
|
||||
const { floID, privKey } = floCrypto.generateNewID();
|
||||
const ethPrivateKey = coinjs.wif2privkey(privKey).privkey;
|
||||
const ethAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey)
|
||||
|
||||
// Bitcoin support
|
||||
const btcBech32 = btcOperator.bech32Address(privKey);
|
||||
// Convert to Bitcoin WIF format
|
||||
const btcPrivKey = btcOperator.convert.wif(privKey);
|
||||
|
||||
renderElem(getRef('created_address_wrapper'), html`
|
||||
<ul id="generated_addresses" class="grid gap-1-5">
|
||||
<li class="grid gap-0-5">
|
||||
<div>
|
||||
<h5>Bitcoin Address</h5>
|
||||
<sm-copy value="${btcBech32}"></sm-copy>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Private Key</h5>
|
||||
<sm-copy value="${btcPrivKey}"></sm-copy>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid gap-0-5">
|
||||
<div>
|
||||
<h5>FLO Address</h5>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user