OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet-desktop commits / 708949e

adding in broken implementation
708949e6a2b539db179630d5894294f746793973
by plankatron <fleeky@gmail.com> · 2026-01-29T21:07:54.000Z

Changed files (5) +420 -43

USDT_CONTRACTS.md +78 -0
……@@ -0,0 +1,78 @@
1# USDT Contract Addresses
2
3## Mainnet (Ethereum)
4- **Contract**: `0xdAC17F958D2ee523a2206206994597C13D831ec7`
5- **Decimals**: 6
6- **Verified**: ✅ Official Tether USDT contract
7- **Explorer**: https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7
8
9## Testnet (Sepolia)
10- **Contract**: `0x7169D38820dfd117C3FA1f22a697dBA58d90BA06`
11- **Decimals**: 6
12- **Status**: ⚠️ NEEDS VERIFICATION
13- **Explorer**: https://sepolia.etherscan.io/token/0x7169D38820dfd117C3FA1f22a697dBA58d90BA06
14
15### ⚠️ Important Notes for Testnet USDT:
16
171. **This is a test token**, not official Tether USDT
182. **You need to mint testnet USDT** to your address
193. **Check the contract exists** on Sepolia Etherscan
204. If contract doesn't work, alternative Sepolia USDT contracts:
21 - Aave Faucet USDT: `0xaA8E23Fb1079EA71e0a56F48a2aA51851D8433D0`
22 - Other test USDT contracts can be found at: https://faucet.circle.com/
23
24### How to Get Testnet USDT:
25
261. **Get Sepolia ETH first** (for gas):
27 - https://sepoliafaucet.com/
28 - https://www.alchemy.com/faucets/ethereum-sepolia
29
302. **Get test USDT**:
31 - Use a testnet USDT faucet (if available)
32 - OR deploy your own ERC-20 test token
33 - OR ask in Discord/Telegram for test tokens
34
35### Debugging Zero Balance:
36
37If balance shows as zero or inconsistent:
38
391. **Verify the contract address is correct**:
40 ```bash
41 # Check on Sepolia Etherscan
42 https://sepolia.etherscan.io/token/0x7169D38820dfd117C3FA1f22a697dBA58d90BA06
43 ```
44
452. **Verify you have tokens at your address**:
46 ```bash
47 # Use the debug script
48 cd hyper-fleeky/hyper-wallet-desktop
49 bare debug-usdt-balance.cjs <your-password>
50 ```
51
523. **Check if the contract is a valid ERC-20**:
53 - Does it have `balanceOf(address)` function?
54 - Does it have `decimals()` function?
55 - Does it have `symbol()` function?
56
574. **Try alternative testnet contract**:
58 - Update `lib/wdk-integration.cjs` with a different testnet address
59 - Restart the wallet server
60
61### Common Issues:
62
63#### Balance shows zero but you have tokens:
64- ✅ Fixed: Contract address checksum (mainnet)
65- ⚠️ Check: Wrong account index
66- ⚠️ Check: Wrong network (testnet vs mainnet)
67- ⚠️ Check: RPC provider issue (try different provider)
68
69#### Balance is inconsistent (sometimes 0, sometimes correct):
70- ⚠️ Network timeout during RPC call
71- ⚠️ RPC provider rate limiting
72- ⚠️ Caching issue in WDK
73- ✅ Fixed: Added detailed logging to track this
74
75#### Cannot see transactions:
76- ⚠️ Blockscout/Etherscan might not index testnet tokens immediately
77- ⚠️ Check if transaction actually succeeded on chain
78- ⚠️ Use manual sync button after sending/receiving
debug-usdt-balance.cjs +124 -0
……@@ -0,0 +1,124 @@
1#!/usr/bin/env bare
2
3// Debug script to test USDT balance loading
4require('bare-node-runtime/global')
5require('bare-node-runtime/imports')
6
7const { initializeWDK, getTokenInfo, getBaseChain } = require('./lib/wdk-integration.cjs')
8const { loadWalletMnemonic } = require('./lib/wdk-integration.cjs')
9const { loadIdentity } = require('./lib/wallet-storage.cjs')
10const { initializeStorage } = require('./lib/wallet-storage.cjs')
11const { getCurrentAddress } = require('./lib/wallet-storage.cjs')
12
13async function debugUSDTBalance() {
14 console.log('🔍 USDT Balance Debug Script')
15 console.log('=' .repeat(60))
16
17 try {
18 // 1. Load wallet mnemonic
19 console.log('\n1️⃣ Loading wallet mnemonic...')
20 const password = process.argv[2]
21 if (!password) {
22 console.error('❌ Usage: bare debug-usdt-balance.cjs <password>')
23 process.exit(1)
24 }
25
26 const walletMnemonic = loadWalletMnemonic(password)
27 if (!walletMnemonic) {
28 console.error('❌ Wallet mnemonic not found')
29 process.exit(1)
30 }
31 console.log('✅ Wallet mnemonic loaded')
32
33 // 2. Initialize storage
34 console.log('\n2️⃣ Initializing storage...')
35 const identity = await loadIdentity()
36 await initializeStorage(identity)
37 console.log('✅ Storage initialized')
38
39 // 3. Get current address for USDT
40 console.log('\n3️⃣ Getting current USDT address...')
41 const chain = 'ethereum-usdt'
42 const currentAddr = await getCurrentAddress(chain)
43 const accountIndex = currentAddr ? currentAddr.index : 0
44 console.log(` Address index: ${accountIndex}`)
45 if (currentAddr) {
46 console.log(` Address: ${currentAddr.address}`)
47 }
48
49 // 4. Test both testnet and mainnet
50 for (const network of ['testnet', 'mainnet']) {
51 console.log(`\n4️⃣ Testing ${network.toUpperCase()}...`)
52 console.log('-'.repeat(60))
53
54 try {
55 // Get token info
56 const tokenInfo = getTokenInfo(chain, network === 'testnet')
57 console.log(` Token: ${tokenInfo.symbol}`)
58 console.log(` Contract: ${tokenInfo.contractAddress}`)
59 console.log(` Decimals: ${tokenInfo.decimals}`)
60
61 // Initialize WDK
62 console.log(`\n Initializing WDK for ${network}...`)
63 const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet' })
64 console.log(' ✅ WDK initialized')
65
66 // Get base chain account
67 const baseChain = getBaseChain(chain)
68 console.log(`\n Getting ${baseChain} account at index ${accountIndex}...`)
69 const account = await wdk.getAccount(baseChain, accountIndex)
70 console.log(' ✅ Account retrieved')
71
72 // Get address
73 const address = await account.getAddress()
74 console.log(` Address: ${address}`)
75
76 // Check if stored address matches
77 if (currentAddr && currentAddr.address.toLowerCase() !== address.toLowerCase()) {
78 console.warn(` ⚠️ WARNING: Stored address mismatch!`)
79 console.warn(` Stored: ${currentAddr.address}`)
80 console.warn(` Derived: ${address}`)
81 }
82
83 // Get token balance
84 console.log(`\n Fetching USDT balance...`)
85 console.log(` Calling: account.getTokenBalance("${tokenInfo.contractAddress}")`)
86
87 const startTime = Date.now()
88 const balance = await account.getTokenBalance(tokenInfo.contractAddress)
89 const duration = Date.now() - startTime
90
91 console.log(` ✅ Balance retrieved in ${duration}ms`)
92 console.log(` Raw balance: ${balance.toString()}`)
93
94 // Format balance
95 const balanceDecimal = Number(balance) / Math.pow(10, tokenInfo.decimals)
96 console.log(` Formatted: ${balanceDecimal.toFixed(2)} ${tokenInfo.symbol}`)
97
98 // Also get ETH balance for gas
99 console.log(`\n Checking ETH balance (for gas)...`)
100 const ethBalance = await account.getBalance()
101 const ethBalanceDecimal = Number(ethBalance) / 1e18
102 console.log(` ETH balance: ${ethBalanceDecimal.toFixed(6)} ETH`)
103
104 if (ethBalance === 0n) {
105 console.warn(` ⚠️ WARNING: No ETH for gas fees!`)
106 }
107
108 } catch (err) {
109 console.error(` ❌ ${network} failed:`, err.message)
110 console.error(` Stack:`, err.stack)
111 }
112 }
113
114 console.log('\n' + '='.repeat(60))
115 console.log('✅ Debug complete')
116
117 } catch (err) {
118 console.error('\n❌ Fatal error:', err.message)
119 console.error('Stack:', err.stack)
120 process.exit(1)
121 }
122}
123
124debugUSDTBalance()
deploy.sh +42 -0
……@@ -0,0 +1,42 @@
1#!/usr/bin/env bash
2# DEPLOYMENT SCRIPT FOR HYPER-WALLET-DESKTOP
3# This ensures we ALWAYS deploy to the correct channel
4
5set -e
6
7# ⚠️ CRITICAL: Always use the channel name, NOT the key
8CHANNEL="hyper-wallet-desktop"
9
10echo "🍐 Hyper Wallet Desktop Deployment"
11echo "=================================="
12echo ""
13echo "Channel: $CHANNEL"
14echo ""
15
16# Confirm deployment
17read -p "Deploy to $CHANNEL? (y/N): " confirm
18if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
19 echo "❌ Deployment cancelled"
20 exit 1
21fi
22
23echo ""
24echo "📦 Staging changes..."
25pear stage $CHANNEL .
26
27echo ""
28echo "🚀 Releasing..."
29pear release $CHANNEL
30
31echo ""
32echo "📡 Updating seeder in tmux..."
33echo ""
34echo "To restart the seeder in tmux, run:"
35echo " cd /storage/projects/code/hypercore-kb/hyper-fleeky"
36echo " ./scripts/restage-seed.sh hyper-wallet-desktop"
37echo ""
38echo "Or manually update pear-seed:5:"
39echo " tmux attach -t pear-seed"
40echo " (Navigate to window 5)"
41echo " Ctrl+C to stop seeder"
42echo " pear seed $CHANNEL ."
desktop/wallet.js +112 -20
……@@ -1502,6 +1502,7 @@ const SUPPORTED_CHAINS = {
15021502 icon: '₮',
15031503 testnetName: 'Sepolia',
15041504 mainnetName: 'Mainnet',
1505 decimals: 6, // USDT has 6 decimals
15051506 formatBalance: (balance) => {
15061507 const usdt = Number(balance) / 1e6 // USDT has 6 decimals
15071508 return `${usdt.toFixed(2)} USDT`
……@@ -1676,7 +1677,12 @@ async function loadCurrentBalance() {
16761677 element.innerHTML = '<div class="spinner"></div>'
16771678
16781679 try {
1679 const res = await authFetch(`/api/balance/${selectedNetwork}-${selectedCurrency}`)
1680 // Get currently selected address index
1681 const select = document.getElementById('wallet-address-select')
1682 const rawIndex = select ? parseInt(select.value) : 0
1683 const accountIndex = isNaN(rawIndex) ? 0 : rawIndex
1684
1685 const res = await authFetch(`/api/balance/${selectedNetwork}-${selectedCurrency}?accountIndex=${accountIndex}`)
16801686 const data = await res.json()
16811687
16821688 if (data.error) {
……@@ -1698,8 +1704,15 @@ async function loadCurrentBalance() {
16981704 if (selectedNetwork === 'mainnet' && pricesLoaded) {
16991705 const price = prices[chain.priceId]
17001706 if (price && price > 0) {
1701 // Convert to base unit (varies by chain)
1702 const divisor = selectedCurrency === 'bitcoin' ? 100000000 : 1e18
1707 // Convert to base unit (varies by chain - respect token decimals)
1708 let divisor
1709 if (selectedCurrency === 'bitcoin') {
1710 divisor = 100000000 // 8 decimals
1711 } else if (chain.decimals) {
1712 divisor = Math.pow(10, chain.decimals) // Use chain-specific decimals (e.g., 6 for USDT)
1713 } else {
1714 divisor = 1e18 // Default to 18 for ETH
1715 }
17031716 const baseAmount = Number(data.balance) / divisor
17041717 const usdValue = baseAmount * price
17051718 usdHTML = `<div class="balance-usd">${formatUSD(usdValue)}</div>`
……@@ -1892,14 +1905,23 @@ async function syncTransactions() {
18921905 syncBtn.disabled = true
18931906 syncBtn.textContent = '🔄 Syncing...'
18941907
1895 // Sync current currency chain
1908 // Get currently selected address index
1909 const select = document.getElementById('wallet-address-select')
1910 const rawIndex = select ? parseInt(select.value) : 0
1911 const accountIndex = isNaN(rawIndex) ? 0 : rawIndex
1912
1913 // Sync current currency chain with selected account index
18961914 const chain = selectedCurrency
18971915 const res = await authFetch(`/api/transactions/sync`, {
18981916 method: 'POST',
18991917 headers: {
19001918 'Content-Type': 'application/json'
19011919 },
1902 body: JSON.stringify({ chain })
1920 body: JSON.stringify({
1921 chain,
1922 network: selectedNetwork, // Add network parameter
1923 accountIndex
1924 })
19031925 })
19041926
19051927 const data = await res.json()
……@@ -2597,9 +2619,17 @@ async function showSend(chain) {
25972619 const data = await res.json()
25982620 currentBalance = Number(data.balance)
25992621
2600 const unit = chain === 'bitcoin' ? 'sats' : 'wei'
2601 document.getElementById('send-amount-helper').textContent =
2602 `Balance: ${currentBalance.toLocaleString()} ${unit}`
2622 // Format balance display based on chain type
2623 const chainConfig = SUPPORTED_CHAINS[chain]
2624 let balanceDisplay
2625 if (chainConfig && chainConfig.formatBalance) {
2626 balanceDisplay = chainConfig.formatBalance(currentBalance)
2627 } else {
2628 const unit = chain === 'bitcoin' ? 'sats' : 'wei'
2629 balanceDisplay = `${currentBalance.toLocaleString()} ${unit}`
2630 }
2631
2632 document.getElementById('send-amount-helper').textContent = `Balance: ${balanceDisplay}`
26032633 } catch (err) {
26042634 console.error('Failed to load balance:', err)
26052635 document.getElementById('send-amount-helper').textContent = 'Balance: unavailable'
……@@ -2819,9 +2849,27 @@ function confirmSend() {
28192849 if (!amount || isNaN(amount) || Number(amount) <= 0) {
28202850 document.getElementById('send-amount-error').textContent = 'Invalid amount'
28212851 hasError = true
2822 } else if (Number(amount) > currentBalance) {
2823 document.getElementById('send-amount-error').textContent = 'Insufficient balance'
2824 hasError = true
2852 } else {
2853 // Check balance based on chain type
2854 const chain = SUPPORTED_CHAINS[currentSendChain]
2855 let hasInsufficientBalance = false
2856
2857 if (chain && chain.isToken) {
2858 // For tokens, currentBalance is in smallest unit (e.g., 1 USDT = 1000000)
2859 // User enters decimal amount (e.g., "1.5")
2860 // Need to convert user input to smallest unit for comparison
2861 const decimals = chain.decimals || 6 // USDT has 6 decimals
2862 const amountInSmallestUnit = Number(amount) * Math.pow(10, decimals)
2863 hasInsufficientBalance = amountInSmallestUnit > currentBalance
2864 } else {
2865 // For native tokens (BTC/ETH), balance and amount are both in smallest unit
2866 hasInsufficientBalance = Number(amount) > currentBalance
2867 }
2868
2869 if (hasInsufficientBalance) {
2870 document.getElementById('send-amount-error').textContent = 'Insufficient balance'
2871 hasError = true
2872 }
28252873 }
28262874
28272875 if (hasError) return
……@@ -2839,12 +2887,47 @@ function confirmSend() {
28392887 const confirmModal = document.getElementById('confirm-modal')
28402888 const confirmContent = document.getElementById('confirm-content')
28412889
2842 const amountFormatted = currentSendChain === 'bitcoin'
2843 ? formatBTC(amount)
2844 : formatETH(amount)
2845 const feeFormatted = currentSendChain === 'bitcoin'
2846 ? formatSats(estimatedFee)
2847 : formatGwei(estimatedFee)
2890 // Get chain configuration
2891 const chain = SUPPORTED_CHAINS[currentSendChain]
2892
2893 // Format amount based on chain type
2894 let amountFormatted
2895 if (chain && chain.isToken) {
2896 // For tokens (like USDT), use the token's format function
2897 // Convert user decimal input to smallest unit first
2898 const decimals = chain.decimals || 6
2899 const amountInSmallestUnit = Number(amount) * Math.pow(10, decimals)
2900 amountFormatted = chain.formatBalance ? chain.formatBalance(amountInSmallestUnit) : `${amount} ${chain.icon || ''}`
2901 } else if (currentSendChain === 'bitcoin') {
2902 amountFormatted = formatBTC(amount)
2903 } else {
2904 amountFormatted = formatETH(amount)
2905 }
2906
2907 // Format fee based on base chain (tokens use base chain for fees)
2908 let feeFormatted
2909 let feeChain = currentSendChain
2910 if (chain && chain.baseChain) {
2911 feeChain = chain.baseChain // Tokens use base chain for fees (e.g., ETH for USDT)
2912 }
2913
2914 if (feeChain === 'bitcoin') {
2915 feeFormatted = formatSats(estimatedFee)
2916 } else {
2917 feeFormatted = formatGwei(estimatedFee)
2918 }
2919
2920 // Calculate total display
2921 let totalDisplay
2922 if (chain && chain.isToken) {
2923 // For tokens, can't add token amount + base chain fee (different units!)
2924 // Show separately
2925 totalDisplay = `${amountFormatted} + ${feeFormatted} gas`
2926 } else if (currentSendChain === 'bitcoin') {
2927 totalDisplay = `${(Number(amount) + Number(estimatedFee)).toLocaleString()} sats`
2928 } else {
2929 totalDisplay = `${(Number(amount) + Number(estimatedFee)).toLocaleString()} wei`
2930 }
28482931
28492932 const networkWarning = selectedNetwork === 'mainnet'
28502933 ? '<div class="confirm-warning">⚠️ WARNING: You are sending on MAINNET. This will use real funds!</div>'
……@@ -2854,7 +2937,7 @@ function confirmSend() {
28542937 ${networkWarning}
28552938 <div class="confirm-row">
28562939 <span class="confirm-label">Chain:</span>
2857 <span class="confirm-value">${currentSendChain.toUpperCase()} (${selectedNetwork})</span>
2940 <span class="confirm-value">${chain ? chain.name : currentSendChain.toUpperCase()} (${selectedNetwork})</span>
28582941 </div>
28592942 <div class="confirm-row">
28602943 <span class="confirm-label">To:</span>
……@@ -2870,7 +2953,7 @@ function confirmSend() {
28702953 </div>
28712954 <div class="confirm-row">
28722955 <span class="confirm-label">Total:</span>
2873 <span class="confirm-value">${Number(amount) + Number(estimatedFee)} ${currentSendChain === 'bitcoin' ? 'sats' : 'wei'}</span>
2956 <span class="confirm-value">${totalDisplay}</span>
28742957 </div>
28752958 `
28762959
……@@ -2933,10 +3016,19 @@ async function executeSend() {
29333016 setTimeout(async () => {
29343017 // First sync from blockchain to fetch the new transaction
29353018 try {
3019 // Get current account index for sync
3020 const select = document.getElementById('wallet-address-select')
3021 const rawIndex = select ? parseInt(select.value) : 0
3022 const accountIndex = isNaN(rawIndex) ? 0 : rawIndex
3023
29363024 const syncRes = await authFetch(`/api/transactions/sync`, {
29373025 method: 'POST',
29383026 headers: { 'Content-Type': 'application/json' },
2939 body: JSON.stringify({ chain: currentSendChain })
3027 body: JSON.stringify({
3028 chain: currentSendChain,
3029 network: selectedNetwork, // Add network parameter
3030 accountIndex
3031 })
29403032 })
29413033 const syncData = await syncRes.json()
29423034 console.log('Auto-sync after send:', syncData)
wallet-server.cjs +64 -23
……@@ -381,8 +381,9 @@ const routes = {
381381 })
382382 },
383383
384 // GET /api/balance/:chain (protected)
384 // GET /api/balance/:chain?accountIndex=N (protected)
385385 // Supports both native tokens (bitcoin, ethereum) and ERC-20 tokens (ethereum-usdt)
386 // WDK design: Application must explicitly specify which account to query
386387 'GET /api/balance/': requireAuth(async (req, res, chain) => {
387388 try {
388389 const walletMnemonic = loadWalletMnemonicCached()
……@@ -390,27 +391,52 @@ const routes = {
390391 return sendError(res, 'Wallet not initialized', 400)
391392 }
392393
393 const network = chain.startsWith('testnet') ? 'testnet' : 'mainnet'
394 const chainParam = chain.replace(/^(testnet|mainnet)-/, '')
394 // Strip query string from chain parameter (route parsing includes it)
395 const chainPath = chain.split('?')[0]
395396
396 // Get current address index from storage (defaults to 0)
397 const identity = await loadIdentity()
398 await initializeStorage(identity)
397 const network = chainPath.startsWith('testnet') ? 'testnet' : 'mainnet'
398 const chainParam = chainPath.replace(/^(testnet|mainnet)-/, '')
399
400 // Parse accountIndex from query string (e.g., ?accountIndex=2)
401 const url = new URL(req.url, `http://${req.headers.host}`)
402 const accountIndexParam = url.searchParams.get('accountIndex')
403 const accountIndex = accountIndexParam !== null ? parseInt(accountIndexParam) : 0
404
405 console.log(`[BALANCE] chain=${chainParam}, accountIndex=${accountIndex}`)
399406
400407 // Check if this is a token balance request (e.g., 'ethereum-usdt')
401408 if (isTokenChain(chainParam)) {
409 console.log(`[BALANCE] Token balance request:`)
410 console.log(` Chain: ${chainParam}`)
411 console.log(` Network: ${network}`)
412 console.log(` Account Index: ${accountIndex}`)
413
402414 const tokenInfo = getTokenInfo(chainParam, network === 'testnet')
403 const baseChain = getBaseChain(chainParam)
415 console.log(` Token: ${tokenInfo.symbol}`)
416 console.log(` Contract: ${tokenInfo.contractAddress}`)
417 console.log(` Decimals: ${tokenInfo.decimals}`)
404418
405 // FIX: Use chainParam (ethereum-usdt) not baseChain (ethereum) for storage lookup
406 const currentAddr = await getCurrentAddress(chainParam)
407 const accountIndex = currentAddr ? currentAddr.index : 0
419 const baseChain = getBaseChain(chainParam)
420 console.log(` Base Chain: ${baseChain}`)
408421
409422 const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet' })
423 console.log(` ✅ WDK initialized`)
424
410425 const account = await wdk.getAccount(baseChain, accountIndex)
426 console.log(` ✅ Account retrieved`)
427
428 const address = await account.getAddress()
429 console.log(` Address: ${address}`)
411430
412431 // Get token balance using contract address
432 console.log(` Calling getTokenBalance...`)
433 const startTime = Date.now()
413434 const balance = await account.getTokenBalance(tokenInfo.contractAddress)
435 const duration = Date.now() - startTime
436 console.log(` ✅ Balance retrieved in ${duration}ms: ${balance.toString()}`)
437
438 const balanceFormatted = (Number(balance) / Math.pow(10, tokenInfo.decimals)).toFixed(2)
439 console.log(` Formatted: ${balanceFormatted} ${tokenInfo.symbol}`)
414440
415441 sendJSON(res, {
416442 chain: chainParam,
……@@ -421,10 +447,7 @@ const routes = {
421447 isToken: true
422448 })
423449 } else {
424 // Native token balance (existing code)
425 const currentAddr = await getCurrentAddress(chainParam)
426 const accountIndex = currentAddr ? currentAddr.index : 0
427
450 // Native token balance
428451 const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet' })
429452 const account = await wdk.getAccount(chainParam, accountIndex)
430453 const balance = await account.getBalance()
……@@ -717,6 +740,17 @@ const routes = {
717740 const chainFilter = chain === 'all' ? 'all' : chain.replace(/^(testnet|mainnet)-/, '')
718741 const transactions = await getTransactions(chainFilter)
719742
743 // Debug: Log ethereum-usdt transactions
744 const usdtTxs = transactions.filter(tx => tx.chain === 'ethereum-usdt')
745 if (usdtTxs.length > 0) {
746 console.log(` 💰 Found ${usdtTxs.length} USDT transactions:`)
747 usdtTxs.forEach(tx => {
748 console.log(` ${tx.hash.substring(0, 16)}... - ${tx.direction} - ${tx.amount}`)
749 })
750 } else {
751 console.log(` ⚠️ No ethereum-usdt transactions found in database`)
752 }
753
720754 // Note: Storage remains open (persistent daemon mode)
721755
722756 sendJSON(res, {
……@@ -746,7 +780,7 @@ const routes = {
746780 'POST /api/transactions/sync': withRateLimit(requireAuth(async (req, res) => {
747781 try {
748782 const body = await parseBody(req)
749 const { chain } = body
783 const { chain, network, accountIndex } = body
750784
751785 if (!chain) {
752786 return sendError(res, 'Missing required field: chain', 400)
……@@ -761,20 +795,27 @@ const routes = {
761795 return sendError(res, 'Wallet not initialized', 400)
762796 }
763797
764 const { initializeWDK } = require('./lib/wdk-integration.cjs')
765 const { wdk } = await initializeWDK(walletMnemonic, { testnet: true })
798 // Determine network - use explicit parameter if provided, otherwise try to extract from chain
799 let finalNetwork = network
800 if (!finalNetwork) {
801 const networkMatch = chain.match(/^(testnet|mainnet)-/)
802 finalNetwork = networkMatch ? networkMatch[1] : 'testnet'
803 }
766804
767 // Sync transactions
768 // Extract network from chain (testnet-ethereum-usdt -> testnet)
769 const networkMatch = chain.match(/^(testnet|mainnet)-/)
770 const network = networkMatch ? networkMatch[1] : 'testnet'
805 // Strip network prefix from chain if present (testnet-ethereum-usdt -> ethereum-usdt)
771806 const chainName = chain.replace(/^(testnet|mainnet)-/, '')
772807
773 console.log(`[SYNC] Syncing chain: ${chain} -> ${chainName}, network: ${network}`)
808 // Use explicit account index (WDK design pattern)
809 const finalAccountIndex = accountIndex !== undefined ? accountIndex : 0
810 console.log(`[SYNC] chain=${chainName}, network=${finalNetwork}, accountIndex=${finalAccountIndex}`)
811
812 const { initializeWDK } = require('./lib/wdk-integration.cjs')
813 const { wdk } = await initializeWDK(walletMnemonic, { testnet: finalNetwork === 'testnet' })
774814
775815 const result = await syncTransactionsFromWDK(wdk, {
776816 chains: [chainName],
777 network: network
817 network: finalNetwork,
818 accountIndex: finalAccountIndex // Pass explicit account index
778819 })
779820
780821 console.log(`[SYNC] Result: synced=${result.synced}, errors=${result.errors.length}`)