hd-wallet-ui 2.0.19 → 2.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/app.js +145 -227
- package/src/template.js +57 -44
- package/src/trust-ui.js +4 -6
- package/src/wallet-storage.js +0 -3
- package/styles/main.css +83 -201
- package/styles/widget.css +83 -201
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hd-wallet-ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.21",
|
|
4
4
|
"description": "HD Wallet modal UI — login, keys, identity, trust map, and security bond. Attach to any button in your app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/app.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"buffer": "^6.0.3",
|
|
41
41
|
"flatbuffers": "^25.9.23",
|
|
42
42
|
"flatc-wasm": "^26.1.32",
|
|
43
|
-
"hd-wallet-wasm": "^2.0.
|
|
43
|
+
"hd-wallet-wasm": "^2.0.21",
|
|
44
44
|
"qrcode": "^1.5.3",
|
|
45
45
|
"spacedatastandards.org": "^1.93.3",
|
|
46
46
|
"vcard-cryptoperson": "^1.1.11"
|
package/src/app.js
CHANGED
|
@@ -526,50 +526,6 @@ function deriveAccountPeerId() {
|
|
|
526
526
|
}
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
function getWalletIdentityPath(wallet = getCurrentWallet()) {
|
|
530
|
-
if (!wallet) return "m/44'/0'/0'";
|
|
531
|
-
return `m/44'/0'/${wallet.accountIndex}'`;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function getCurrentWalletIdentity(wallet = getCurrentWallet()) {
|
|
535
|
-
if (!state.hdRoot || !wallet) return { xpub: '', peerId: '', path: getWalletIdentityPath(wallet) };
|
|
536
|
-
const path = getWalletIdentityPath(wallet);
|
|
537
|
-
try {
|
|
538
|
-
const accountKey = deriveHDKey(path);
|
|
539
|
-
return {
|
|
540
|
-
xpub: accountKey?.toXpub?.() || '',
|
|
541
|
-
peerId: accountKey?.peerIdString?.() || '',
|
|
542
|
-
path,
|
|
543
|
-
};
|
|
544
|
-
} catch (e) {
|
|
545
|
-
console.warn('Failed to derive wallet identity keys:', e);
|
|
546
|
-
return { xpub: '', peerId: '', path };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function getCurrentWalletSigningAccounts(wallet = getCurrentWallet()) {
|
|
551
|
-
if (!wallet) return [];
|
|
552
|
-
return state.activeAccounts.filter(a => a.active && getAccountWalletId(a) === wallet.id && isSigningAccountForWallet(a, wallet));
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function getCurrentWalletSignatureKey(wallet = getCurrentWallet()) {
|
|
556
|
-
if (!state.hdRoot || !wallet) return null;
|
|
557
|
-
const accountIndex = wallet.accountIndex;
|
|
558
|
-
try {
|
|
559
|
-
const path = buildSigningPath(501, accountIndex, 0);
|
|
560
|
-
const derived = deriveHDKey(path);
|
|
561
|
-
return {
|
|
562
|
-
privateKey: derived.privateKey(),
|
|
563
|
-
accountIndex,
|
|
564
|
-
index: 0,
|
|
565
|
-
path,
|
|
566
|
-
};
|
|
567
|
-
} catch (e) {
|
|
568
|
-
console.warn('Failed to derive selected wallet signature key:', e);
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
529
|
function updatePathDisplay() {
|
|
574
530
|
const coin = $('hd-coin')?.value;
|
|
575
531
|
const account = $('hd-account')?.value || '0';
|
|
@@ -821,60 +777,30 @@ function updateCustomPathDefault() {
|
|
|
821
777
|
input.dataset.autogenerated = 'true';
|
|
822
778
|
}
|
|
823
779
|
|
|
824
|
-
function fitWalletSelectorToSelectedLabel(select) {
|
|
825
|
-
if (!select) return;
|
|
826
|
-
const selected = select.options[select.selectedIndex];
|
|
827
|
-
const label = selected?.textContent || '';
|
|
828
|
-
const width = Math.max(72, Math.min(260, (label.length * 8) + 46));
|
|
829
|
-
select.style.width = `${width}px`;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
780
|
function renderWalletSelector() {
|
|
833
|
-
const
|
|
834
|
-
if (
|
|
781
|
+
const select = $('wallet-active-select');
|
|
782
|
+
if (!select) return;
|
|
835
783
|
ensureWalletNamesNormalized();
|
|
836
784
|
|
|
837
785
|
const currentWallet = getCurrentWallet();
|
|
838
786
|
if (!currentWallet) {
|
|
839
|
-
|
|
787
|
+
select.innerHTML = '';
|
|
840
788
|
return;
|
|
841
789
|
}
|
|
842
790
|
state.activeWalletId = currentWallet.id;
|
|
843
791
|
|
|
792
|
+
select.innerHTML = '';
|
|
844
793
|
const displayCurrency = state.walletFiatCurrency || getSelectedCurrency();
|
|
845
794
|
const activeWallets = getActiveWallets();
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
option.textContent = wallet.id === state.activeWalletId
|
|
853
|
-
? wallet.name
|
|
854
|
-
: `${wallet.name} (${formatCurrencyValue(walletValue, displayCurrency)})`;
|
|
855
|
-
select.appendChild(option);
|
|
856
|
-
});
|
|
857
|
-
select.value = String(state.activeWalletId);
|
|
858
|
-
fitWalletSelectorToSelectedLabel(select);
|
|
795
|
+
activeWallets.forEach((wallet) => {
|
|
796
|
+
const option = document.createElement('option');
|
|
797
|
+
option.value = String(wallet.id);
|
|
798
|
+
const walletValue = state.walletFiatTotals[wallet.id] ?? 0;
|
|
799
|
+
option.textContent = `${wallet.name} (${formatCurrencyValue(walletValue, displayCurrency)})`;
|
|
800
|
+
select.appendChild(option);
|
|
859
801
|
});
|
|
802
|
+
select.value = String(state.activeWalletId);
|
|
860
803
|
updateCustomPathWalletLabel();
|
|
861
|
-
updateIdentityWalletKeys();
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
function updateWalletBondDisplay(currency = state.walletFiatCurrency || getSelectedCurrency()) {
|
|
865
|
-
const valueEl = $('wallet-bond-value');
|
|
866
|
-
const wallet = getCurrentWallet();
|
|
867
|
-
const walletValue = wallet ? state.walletFiatTotals?.[wallet.id] ?? 0 : 0;
|
|
868
|
-
if (valueEl) valueEl.textContent = formatCurrencyValue(walletValue, currency);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function updateIdentityWalletKeys() {
|
|
872
|
-
const identity = getCurrentWalletIdentity();
|
|
873
|
-
const xpubEl = $('identity-wallet-xpub');
|
|
874
|
-
const peerIdEl = $('identity-wallet-peerid');
|
|
875
|
-
|
|
876
|
-
if (xpubEl) setTruncatedValue(xpubEl, identity.xpub || 'N/A');
|
|
877
|
-
if (peerIdEl) setTruncatedValue(peerIdEl, identity.peerId || 'N/A');
|
|
878
804
|
}
|
|
879
805
|
|
|
880
806
|
function sleep(ms) {
|
|
@@ -1295,7 +1221,6 @@ function hideWalletOverlays() {
|
|
|
1295
1221
|
function showWalletMainView() {
|
|
1296
1222
|
const main = $('wallet-main-view');
|
|
1297
1223
|
hideWalletOverlays();
|
|
1298
|
-
closeAssetActionOverlay();
|
|
1299
1224
|
if (main) main.style.display = 'block';
|
|
1300
1225
|
}
|
|
1301
1226
|
|
|
@@ -1598,49 +1523,6 @@ function closeWalletActionMenus() {
|
|
|
1598
1523
|
$('wallet-receive-menu')?.classList.remove('visible');
|
|
1599
1524
|
}
|
|
1600
1525
|
|
|
1601
|
-
function closeAssetActionOverlay() {
|
|
1602
|
-
const overlay = $('wallet-asset-action-overlay');
|
|
1603
|
-
if (overlay) overlay.style.display = 'none';
|
|
1604
|
-
state.selectedWalletAssetIdx = null;
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
function showAssetActionOverlay(acct, idx) {
|
|
1608
|
-
const overlay = $('wallet-asset-action-overlay');
|
|
1609
|
-
if (!overlay || !acct) return;
|
|
1610
|
-
|
|
1611
|
-
state.selectedWalletAssetIdx = idx;
|
|
1612
|
-
const icon = CHAIN_ICONS[acct.name] || { symbol: '?' };
|
|
1613
|
-
const fullName = CHAIN_FULL_NAMES[acct.name] || acct.name;
|
|
1614
|
-
const pathLabel = acct.path || `m/44'/${acct.coinType}'/${acct.account}'/0/${acct.index}`;
|
|
1615
|
-
|
|
1616
|
-
const titleEl = $('wallet-asset-action-title');
|
|
1617
|
-
const pathEl = $('wallet-asset-action-path');
|
|
1618
|
-
const addressEl = $('wallet-asset-action-address');
|
|
1619
|
-
if (titleEl) titleEl.textContent = `${icon.symbol} ${fullName}`;
|
|
1620
|
-
if (pathEl) pathEl.textContent = pathLabel;
|
|
1621
|
-
if (addressEl) {
|
|
1622
|
-
addressEl.textContent = acct.address || '';
|
|
1623
|
-
addressEl.title = acct.address || '';
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
const sendBtn = $('wallet-asset-send');
|
|
1627
|
-
const receiveBtn = $('wallet-asset-receive');
|
|
1628
|
-
if (sendBtn) {
|
|
1629
|
-
sendBtn.onclick = () => {
|
|
1630
|
-
closeAssetActionOverlay();
|
|
1631
|
-
showSendView(idx);
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
if (receiveBtn) {
|
|
1635
|
-
receiveBtn.onclick = () => {
|
|
1636
|
-
closeAssetActionOverlay();
|
|
1637
|
-
showReceiveModal(acct);
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
overlay.style.display = 'flex';
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
1526
|
function renderAccountsList() {
|
|
1645
1527
|
const listEl = $('wallet-accounts-list');
|
|
1646
1528
|
const emptyEl = $('wallet-accounts-empty');
|
|
@@ -1689,7 +1571,7 @@ function renderAccountsList() {
|
|
|
1689
1571
|
'</div>';
|
|
1690
1572
|
|
|
1691
1573
|
row.addEventListener('click', () => {
|
|
1692
|
-
|
|
1574
|
+
showReceiveModal(acct);
|
|
1693
1575
|
});
|
|
1694
1576
|
|
|
1695
1577
|
listEl.appendChild(row);
|
|
@@ -2146,6 +2028,7 @@ async function updateWalletBondTotal() {
|
|
|
2146
2028
|
const currency = getSelectedCurrency();
|
|
2147
2029
|
const prices = await fetchCryptoPrices(currency);
|
|
2148
2030
|
|
|
2031
|
+
let total = 0;
|
|
2149
2032
|
const walletTotals = {};
|
|
2150
2033
|
let hasPositiveBalance = false;
|
|
2151
2034
|
let missingPriceForFundedAccount = false;
|
|
@@ -2162,20 +2045,27 @@ async function updateWalletBondTotal() {
|
|
|
2162
2045
|
}
|
|
2163
2046
|
|
|
2164
2047
|
const fiatValue = bal * price;
|
|
2048
|
+
total += fiatValue;
|
|
2165
2049
|
const walletId = getAccountWalletId(acct);
|
|
2166
2050
|
walletTotals[walletId] = (walletTotals[walletId] || 0) + fiatValue;
|
|
2167
2051
|
}
|
|
2168
2052
|
|
|
2169
|
-
|
|
2170
|
-
if (hasPositiveBalance && pricedTotal <= 0 && missingPriceForFundedAccount) {
|
|
2053
|
+
if (hasPositiveBalance && total <= 0 && missingPriceForFundedAccount) {
|
|
2171
2054
|
throw new Error('Funded accounts found but fiat pricing is unavailable');
|
|
2172
2055
|
}
|
|
2173
2056
|
|
|
2174
2057
|
state.walletFiatTotals = walletTotals;
|
|
2175
2058
|
state.walletFiatCurrency = currency;
|
|
2176
2059
|
|
|
2177
|
-
|
|
2060
|
+
const formatted = formatCurrencyValue(total, currency);
|
|
2061
|
+
if (valueEl) valueEl.textContent = formatted;
|
|
2178
2062
|
renderWalletSelector();
|
|
2063
|
+
|
|
2064
|
+
// Also update the header bond total
|
|
2065
|
+
const accountTotalEl = $('account-total-value');
|
|
2066
|
+
if (accountTotalEl) {
|
|
2067
|
+
accountTotalEl.textContent = 'Bond: ' + formatted;
|
|
2068
|
+
}
|
|
2179
2069
|
} catch (e) {
|
|
2180
2070
|
console.warn('Bond total calculation failed:', e);
|
|
2181
2071
|
// Keep last known totals if pricing endpoint is temporarily unavailable.
|
|
@@ -2183,7 +2073,10 @@ async function updateWalletBondTotal() {
|
|
|
2183
2073
|
const cachedTotal = Object.values(cachedTotals).reduce((sum, v) => sum + (Number.isFinite(v) ? v : 0), 0);
|
|
2184
2074
|
if (cachedTotal > 0) {
|
|
2185
2075
|
const displayCurrency = state.walletFiatCurrency || getSelectedCurrency();
|
|
2186
|
-
|
|
2076
|
+
const formatted = formatCurrencyValue(cachedTotal, displayCurrency);
|
|
2077
|
+
if (valueEl) valueEl.textContent = formatted;
|
|
2078
|
+
const accountTotalEl = $('account-total-value');
|
|
2079
|
+
if (accountTotalEl) accountTotalEl.textContent = 'Bond: ' + formatted;
|
|
2187
2080
|
} else if (valueEl && !valueEl.textContent) {
|
|
2188
2081
|
valueEl.textContent = '$0.00';
|
|
2189
2082
|
}
|
|
@@ -2596,36 +2489,6 @@ async function generatePKIKeyPairs() {
|
|
|
2596
2489
|
// Login / Logout
|
|
2597
2490
|
// =============================================================================
|
|
2598
2491
|
|
|
2599
|
-
function hideStoredWalletLoginUI() {
|
|
2600
|
-
const storedTab = $('stored-tab');
|
|
2601
|
-
if (storedTab) storedTab.style.display = 'none';
|
|
2602
|
-
|
|
2603
|
-
const pinSect = $('stored-pin-section');
|
|
2604
|
-
if (pinSect) pinSect.style.display = 'block';
|
|
2605
|
-
|
|
2606
|
-
const psSect = $('stored-passkey-section');
|
|
2607
|
-
if (psSect) psSect.style.display = 'none';
|
|
2608
|
-
|
|
2609
|
-
const divider = $('stored-divider');
|
|
2610
|
-
if (divider) divider.style.display = 'none';
|
|
2611
|
-
|
|
2612
|
-
const dateEl = $('stored-wallet-date');
|
|
2613
|
-
if (dateEl) dateEl.textContent = '';
|
|
2614
|
-
|
|
2615
|
-
const unlockPin = $('pin-input-unlock');
|
|
2616
|
-
if (unlockPin) unlockPin.value = '';
|
|
2617
|
-
|
|
2618
|
-
const unlockBtn = $('unlock-stored-wallet');
|
|
2619
|
-
if (unlockBtn) unlockBtn.disabled = true;
|
|
2620
|
-
|
|
2621
|
-
$qa('.method-tab').forEach(t => t.classList.remove('active'));
|
|
2622
|
-
$qa('.method-content').forEach(c => c.classList.remove('active'));
|
|
2623
|
-
const pwMethod = $('password-method');
|
|
2624
|
-
if (pwMethod) pwMethod.classList.add('active');
|
|
2625
|
-
const pwTab = $q('.method-tab[data-method="password"]');
|
|
2626
|
-
if (pwTab) pwTab.classList.add('active');
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
2492
|
function login(keys) {
|
|
2630
2493
|
state.loggedIn = true;
|
|
2631
2494
|
state.wallet = keys;
|
|
@@ -2696,6 +2559,21 @@ function login(keys) {
|
|
|
2696
2559
|
if (xpubEl) {
|
|
2697
2560
|
setTruncatedValue(xpubEl, state.hdRoot.toXpub() || 'N/A');
|
|
2698
2561
|
}
|
|
2562
|
+
// Populate wallet tab xpub display
|
|
2563
|
+
const walletTabXpubEl = $('wallet-tab-xpub');
|
|
2564
|
+
if (walletTabXpubEl) {
|
|
2565
|
+
setTruncatedValue(walletTabXpubEl, state.hdRoot.toXpub() || 'N/A');
|
|
2566
|
+
}
|
|
2567
|
+
// Populate wallet tab PeerID display
|
|
2568
|
+
const walletTabPeerIdEl = $('wallet-tab-peerid');
|
|
2569
|
+
const peerIdRow = $('ph-portfolio-peerid-row');
|
|
2570
|
+
if (walletTabPeerIdEl && peerIdRow) {
|
|
2571
|
+
const peerIdStr = deriveAccountPeerId();
|
|
2572
|
+
if (peerIdStr) {
|
|
2573
|
+
setTruncatedValue(walletTabPeerIdEl, peerIdStr);
|
|
2574
|
+
peerIdRow.style.display = '';
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2699
2577
|
populateAccountAddressDropdown();
|
|
2700
2578
|
if (xprvEl) {
|
|
2701
2579
|
xprvEl.textContent = 'Hidden (click reveal)';
|
|
@@ -2816,10 +2694,7 @@ function logout() {
|
|
|
2816
2694
|
state.masterSeed = null;
|
|
2817
2695
|
state.hdRoot = null;
|
|
2818
2696
|
state.mnemonic = null;
|
|
2819
|
-
state.addresses = { btc: null, eth: null, sol: null };
|
|
2820
2697
|
|
|
2821
|
-
WalletStorage.clearStorage();
|
|
2822
|
-
hideStoredWalletLoginUI();
|
|
2823
2698
|
localStorage.removeItem(PKI_STORAGE_KEY);
|
|
2824
2699
|
|
|
2825
2700
|
// Update hero stats
|
|
@@ -2856,9 +2731,6 @@ function logout() {
|
|
|
2856
2731
|
// Clear HD wallet UI
|
|
2857
2732
|
const derivedResult = $('derived-result');
|
|
2858
2733
|
if (derivedResult) derivedResult.style.display = 'none';
|
|
2859
|
-
|
|
2860
|
-
$('login-modal')?.classList.remove('active');
|
|
2861
|
-
$('keys-modal')?.classList.remove('active');
|
|
2862
2734
|
}
|
|
2863
2735
|
|
|
2864
2736
|
// =============================================================================
|
|
@@ -2945,9 +2817,43 @@ function downloadData(data, filename, mimeType) {
|
|
|
2945
2817
|
// Wallet Address Population & Balance Fetching
|
|
2946
2818
|
// =============================================================================
|
|
2947
2819
|
|
|
2820
|
+
// Account header — show xpub only
|
|
2948
2821
|
function populateAccountAddressDropdown() {
|
|
2949
|
-
|
|
2950
|
-
|
|
2822
|
+
const addrEl = $('account-address-display');
|
|
2823
|
+
if (!addrEl) return;
|
|
2824
|
+
|
|
2825
|
+
const xpubStr = state.hdRoot ? state.hdRoot.toXpub() : '';
|
|
2826
|
+
addrEl.textContent = `${xpubStr.slice(0,10)}...${xpubStr.slice(-10)}`;
|
|
2827
|
+
addrEl.title = xpubStr;
|
|
2828
|
+
|
|
2829
|
+
const copyBtn = $('account-address-copy');
|
|
2830
|
+
if (copyBtn) {
|
|
2831
|
+
copyBtn.onclick = async () => {
|
|
2832
|
+
if (xpubStr && await safeCopyText(xpubStr)) {
|
|
2833
|
+
copyBtn.title = 'Copied!';
|
|
2834
|
+
setTimeout(() => { copyBtn.title = 'Copy xpub'; }, 1500);
|
|
2835
|
+
}
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// Populate PeerID row (derived from account-level secp256k1 key)
|
|
2840
|
+
const peerIdStr = deriveAccountPeerId();
|
|
2841
|
+
const peerIdRow = $('account-peerid-row');
|
|
2842
|
+
const peerIdEl = $('account-peerid-display');
|
|
2843
|
+
if (peerIdStr && peerIdRow && peerIdEl) {
|
|
2844
|
+
peerIdRow.style.display = '';
|
|
2845
|
+
peerIdEl.textContent = `${peerIdStr.slice(0,8)}...${peerIdStr.slice(-8)}`;
|
|
2846
|
+
peerIdEl.title = peerIdStr;
|
|
2847
|
+
}
|
|
2848
|
+
const peerCopyBtn = $('account-peerid-copy');
|
|
2849
|
+
if (peerCopyBtn) {
|
|
2850
|
+
peerCopyBtn.onclick = async () => {
|
|
2851
|
+
if (peerIdStr && await safeCopyText(peerIdStr)) {
|
|
2852
|
+
peerCopyBtn.title = 'Copied!';
|
|
2853
|
+
setTimeout(() => { peerCopyBtn.title = 'Copy PeerID'; }, 1500);
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2951
2857
|
}
|
|
2952
2858
|
|
|
2953
2859
|
function populateWalletAddresses() {
|
|
@@ -3406,11 +3312,10 @@ function generateVCard(info, { skipPhoto = false } = {}) {
|
|
|
3406
3312
|
}
|
|
3407
3313
|
|
|
3408
3314
|
if (info.includeKeys && state.wallet?.x25519) {
|
|
3409
|
-
const identity = getCurrentWalletIdentity();
|
|
3410
3315
|
person.KEY = [
|
|
3411
|
-
// Always include
|
|
3412
|
-
...(
|
|
3413
|
-
XPUB:
|
|
3316
|
+
// Always include xPub
|
|
3317
|
+
...(state.hdRoot?.toXpub ? [{
|
|
3318
|
+
XPUB: state.hdRoot.toXpub(),
|
|
3414
3319
|
LABEL: '',
|
|
3415
3320
|
}] : []),
|
|
3416
3321
|
// X25519 encryption key
|
|
@@ -3419,7 +3324,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
|
|
|
3419
3324
|
LABEL: 'X25519',
|
|
3420
3325
|
},
|
|
3421
3326
|
// Active accounts from wallet scan
|
|
3422
|
-
...
|
|
3327
|
+
...state.activeAccounts
|
|
3328
|
+
.filter(a => a.active && isSigningAccount(a))
|
|
3423
3329
|
.flatMap(a => {
|
|
3424
3330
|
const entries = [];
|
|
3425
3331
|
const pathLabel = a.path || `m/44'/${a.coinType}'/${a.account}'/0/${a.index}`;
|
|
@@ -3440,9 +3346,8 @@ function generateVCard(info, { skipPhoto = false } = {}) {
|
|
|
3440
3346
|
return entries;
|
|
3441
3347
|
}),
|
|
3442
3348
|
];
|
|
3443
|
-
} else if (info.xpubOnly) {
|
|
3444
|
-
|
|
3445
|
-
if (identity.xpub) person.KEY = [{ XPUB: identity.xpub, LABEL: '' }];
|
|
3349
|
+
} else if (info.xpubOnly && state.hdRoot?.toXpub) {
|
|
3350
|
+
person.KEY = [{ XPUB: state.hdRoot.toXpub(), LABEL: '' }];
|
|
3446
3351
|
}
|
|
3447
3352
|
|
|
3448
3353
|
const note = info.includeKeys
|
|
@@ -3494,16 +3399,15 @@ function getSignableBody(vcardText) {
|
|
|
3494
3399
|
}
|
|
3495
3400
|
|
|
3496
3401
|
function signVCard(vcardText) {
|
|
3497
|
-
|
|
3498
|
-
if (!signatureKey?.privateKey) return vcardText;
|
|
3402
|
+
if (!state.wallet?.ed25519?.privateKey) return vcardText;
|
|
3499
3403
|
|
|
3500
3404
|
const body = getSignableBody(vcardText);
|
|
3501
3405
|
const messageBytes = new TextEncoder().encode(body);
|
|
3502
|
-
const signature = hdWallet().curves.ed25519.sign(messageBytes,
|
|
3406
|
+
const signature = hdWallet().curves.ed25519.sign(messageBytes, state.wallet.ed25519.privateKey);
|
|
3503
3407
|
const sigB64 = toBase64(signature);
|
|
3504
3408
|
|
|
3505
|
-
// Encode signature +
|
|
3506
|
-
const sigValue = `${sigB64}:501
|
|
3409
|
+
// Encode signature + derivation path (coinType=501, account=0, index=0)
|
|
3410
|
+
const sigValue = `${sigB64}:501:0:0`;
|
|
3507
3411
|
|
|
3508
3412
|
// Find highest itemN and key index
|
|
3509
3413
|
let maxItem = 0;
|
|
@@ -3872,8 +3776,8 @@ function isPasskeySupported() {
|
|
|
3872
3776
|
|
|
3873
3777
|
// Track selected remember method (pin or passkey) for each login type
|
|
3874
3778
|
const rememberMethod = {
|
|
3875
|
-
password: '
|
|
3876
|
-
seed: '
|
|
3779
|
+
password: 'passkey',
|
|
3780
|
+
seed: 'passkey'
|
|
3877
3781
|
};
|
|
3878
3782
|
|
|
3879
3783
|
function setupLoginHandlers() {
|
|
@@ -4319,7 +4223,7 @@ function setupMainAppHandlers() {
|
|
|
4319
4223
|
// Modal close handlers
|
|
4320
4224
|
$qa('.modal').forEach(modal => {
|
|
4321
4225
|
modal.addEventListener('click', (e) => {
|
|
4322
|
-
if (e.target === modal || e.target.
|
|
4226
|
+
if (e.target === modal || e.target.classList.contains('modal-close')) {
|
|
4323
4227
|
modal.classList.remove('active');
|
|
4324
4228
|
}
|
|
4325
4229
|
});
|
|
@@ -4328,7 +4232,7 @@ function setupMainAppHandlers() {
|
|
|
4328
4232
|
// Account modal tab switching
|
|
4329
4233
|
$qa('.modal-tab[data-modal-tab]').forEach(tab => {
|
|
4330
4234
|
tab.addEventListener('click', () => {
|
|
4331
|
-
$qa('.modal-tab').forEach(t => t.classList.remove('active'));
|
|
4235
|
+
$qa('.modal-tab[data-modal-tab]').forEach(t => t.classList.remove('active'));
|
|
4332
4236
|
$qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
|
|
4333
4237
|
tab.classList.add('active');
|
|
4334
4238
|
const target = $(tab.dataset.modalTab);
|
|
@@ -4746,12 +4650,10 @@ function setupMainAppHandlers() {
|
|
|
4746
4650
|
if (targetEl) {
|
|
4747
4651
|
try {
|
|
4748
4652
|
let value = '';
|
|
4749
|
-
if (targetId === 'wallet-xpub') {
|
|
4653
|
+
if (targetId === 'wallet-xpub' || targetId === 'wallet-tab-xpub') {
|
|
4750
4654
|
value = state.hdRoot?.toXpub?.() || '';
|
|
4751
|
-
} else if (targetId === '
|
|
4752
|
-
value =
|
|
4753
|
-
} else if (targetId === 'identity-wallet-peerid') {
|
|
4754
|
-
value = getCurrentWalletIdentity().peerId || '';
|
|
4655
|
+
} else if (targetId === 'wallet-tab-peerid') {
|
|
4656
|
+
value = deriveAccountPeerId() || '';
|
|
4755
4657
|
} else if (targetId === 'wallet-xprv') {
|
|
4756
4658
|
if (targetEl.dataset.revealed !== 'true') {
|
|
4757
4659
|
alert('Reveal the xprv first to copy it.');
|
|
@@ -4844,45 +4746,74 @@ function setupMainAppHandlers() {
|
|
|
4844
4746
|
});
|
|
4845
4747
|
|
|
4846
4748
|
// Wallet tab controls
|
|
4847
|
-
|
|
4749
|
+
$('wallet-active-select')?.addEventListener('change', (e) => {
|
|
4848
4750
|
const walletId = Number.parseInt(e.target.value, 10);
|
|
4849
4751
|
if (Number.isNaN(walletId)) return;
|
|
4850
4752
|
state.activeWalletId = walletId;
|
|
4851
4753
|
closeWalletActionMenus();
|
|
4852
|
-
closeAssetActionOverlay();
|
|
4853
4754
|
renderWalletSelector();
|
|
4854
|
-
updateWalletBondDisplay();
|
|
4855
4755
|
renderAccountsList();
|
|
4856
4756
|
updateCustomPathDefault();
|
|
4857
|
-
};
|
|
4858
|
-
$('account-wallet-select')?.addEventListener('change', handleWalletSelectChange);
|
|
4859
|
-
$('wallet-active-select')?.addEventListener('change', handleWalletSelectChange);
|
|
4860
|
-
$('wallet-manage-tab')?.addEventListener('click', () => {
|
|
4861
|
-
closeWalletActionMenus();
|
|
4862
|
-
closeAssetActionOverlay();
|
|
4863
|
-
$qa('.modal-tab').forEach(t => t.classList.remove('active'));
|
|
4864
|
-
$qa('.modal-tab-content').forEach(c => c.classList.remove('active'));
|
|
4865
|
-
$('wallet-manage-tab')?.classList.add('active');
|
|
4866
|
-
$('wallet-tab-content')?.classList.add('active');
|
|
4867
|
-
showWalletsView();
|
|
4868
4757
|
});
|
|
4869
4758
|
$('wallet-manage-btn')?.addEventListener('click', () => {
|
|
4870
4759
|
closeWalletActionMenus();
|
|
4871
|
-
closeAssetActionOverlay();
|
|
4872
4760
|
showWalletsView();
|
|
4873
4761
|
});
|
|
4874
4762
|
$('wallet-scan-btn')?.addEventListener('click', () => {
|
|
4875
4763
|
scanActiveAccounts();
|
|
4876
4764
|
});
|
|
4877
|
-
$('wallet-
|
|
4765
|
+
const sendAction = $('wallet-send-action');
|
|
4766
|
+
const receiveAction = $('wallet-receive-action');
|
|
4767
|
+
$('wallet-send-btn')?.addEventListener('click', (e) => {
|
|
4768
|
+
e.stopPropagation();
|
|
4769
|
+
updateWalletActionMenus();
|
|
4770
|
+
const sendMenu = $('wallet-send-menu');
|
|
4771
|
+
const receiveMenu = $('wallet-receive-menu');
|
|
4772
|
+
if (!sendMenu || !receiveMenu) return;
|
|
4773
|
+
const nextVisible = !sendMenu.classList.contains('visible');
|
|
4774
|
+
receiveMenu.classList.remove('visible');
|
|
4775
|
+
sendMenu.classList.toggle('visible', nextVisible);
|
|
4776
|
+
});
|
|
4777
|
+
$('wallet-receive-btn-main')?.addEventListener('click', (e) => {
|
|
4778
|
+
e.stopPropagation();
|
|
4779
|
+
updateWalletActionMenus();
|
|
4780
|
+
const sendMenu = $('wallet-send-menu');
|
|
4781
|
+
const receiveMenu = $('wallet-receive-menu');
|
|
4782
|
+
if (!sendMenu || !receiveMenu) return;
|
|
4783
|
+
const nextVisible = !receiveMenu.classList.contains('visible');
|
|
4784
|
+
sendMenu.classList.remove('visible');
|
|
4785
|
+
receiveMenu.classList.toggle('visible', nextVisible);
|
|
4786
|
+
});
|
|
4787
|
+
$qa('#wallet-send-menu .ph-action-menu-item').forEach((btn) => {
|
|
4788
|
+
btn.addEventListener('click', (e) => {
|
|
4789
|
+
e.stopPropagation();
|
|
4790
|
+
const chain = btn.dataset.chain;
|
|
4791
|
+
const acct = getWalletAccountForChain(chain);
|
|
4792
|
+
closeWalletActionMenus();
|
|
4793
|
+
if (!acct) return;
|
|
4794
|
+
showSendView(state.activeAccounts.indexOf(acct));
|
|
4795
|
+
});
|
|
4796
|
+
});
|
|
4797
|
+
$qa('#wallet-receive-menu .ph-action-menu-item').forEach((btn) => {
|
|
4798
|
+
btn.addEventListener('click', (e) => {
|
|
4799
|
+
e.stopPropagation();
|
|
4800
|
+
const chain = btn.dataset.chain;
|
|
4801
|
+
const acct = getWalletAccountForChain(chain);
|
|
4802
|
+
closeWalletActionMenus();
|
|
4803
|
+
if (!acct) return;
|
|
4804
|
+
showReceiveModal(acct);
|
|
4805
|
+
});
|
|
4806
|
+
});
|
|
4807
|
+
_root.addEventListener('click', (e) => {
|
|
4808
|
+
if (sendAction?.contains(e.target) || receiveAction?.contains(e.target)) return;
|
|
4809
|
+
closeWalletActionMenus();
|
|
4810
|
+
});
|
|
4878
4811
|
$('wallet-export-btn-main')?.addEventListener('click', () => {
|
|
4879
4812
|
closeWalletActionMenus();
|
|
4880
|
-
closeAssetActionOverlay();
|
|
4881
4813
|
showExportView();
|
|
4882
4814
|
});
|
|
4883
4815
|
$('wallet-advanced-btn-main')?.addEventListener('click', () => {
|
|
4884
4816
|
closeWalletActionMenus();
|
|
4885
|
-
closeAssetActionOverlay();
|
|
4886
4817
|
showAdvancedView();
|
|
4887
4818
|
});
|
|
4888
4819
|
$('wallet-wallets-back')?.addEventListener('click', () => {
|
|
@@ -6060,27 +5991,14 @@ export async function createWalletUI(rootElement, options = {}) {
|
|
|
6060
5991
|
return {
|
|
6061
5992
|
/** Open the login modal */
|
|
6062
5993
|
openLogin() {
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
if (accountModal) accountModal.classList.add('active');
|
|
6066
|
-
return;
|
|
6067
|
-
}
|
|
6068
|
-
const loginModal = document.getElementById('login-modal');
|
|
6069
|
-
if (loginModal) {
|
|
6070
|
-
loginModal.classList.add('active');
|
|
6071
|
-
const storedTab = loginModal.querySelector('[data-method="stored"]');
|
|
6072
|
-
if (storedTab && storedTab.style.display !== 'none') storedTab.click();
|
|
6073
|
-
}
|
|
5994
|
+
const modal = document.getElementById('login-modal');
|
|
5995
|
+
if (modal) modal.classList.add('active');
|
|
6074
5996
|
},
|
|
6075
5997
|
/** Open the account / keys modal (requires login first) */
|
|
6076
5998
|
openAccount() {
|
|
6077
5999
|
const modal = document.getElementById('keys-modal');
|
|
6078
6000
|
if (modal) modal.classList.add('active');
|
|
6079
6001
|
},
|
|
6080
|
-
/** Clear the wallet session; host applications own any visible logout control */
|
|
6081
|
-
logout() {
|
|
6082
|
-
logout();
|
|
6083
|
-
},
|
|
6084
6002
|
/** Remove all injected wallet UI elements from the DOM */
|
|
6085
6003
|
destroy() {
|
|
6086
6004
|
const container = document.getElementById('hd-wallet-ui-container');
|