-
Notifications
You must be signed in to change notification settings - Fork 7
log on initial load, swap layout fix, spendable balance fix #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -109,6 +109,169 @@ function saveSwapReport(swapId, swapData) { | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function toNumber(value, fallback = 0) { | ||||||||||||||||||
| const normalized = Number(value); | ||||||||||||||||||
| return Number.isFinite(normalized) ? normalized : fallback; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function getAllSwapReportPaths() { | ||||||||||||||||||
| const reportsRoot = path.join(api1State.DATA_DIR, 'swap_reports'); | ||||||||||||||||||
| const reportPaths = []; | ||||||||||||||||||
|
|
||||||||||||||||||
| function walk(dirPath) { | ||||||||||||||||||
| if (!fs.existsSync(dirPath)) return; | ||||||||||||||||||
|
|
||||||||||||||||||
| for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) { | ||||||||||||||||||
| const fullPath = path.join(dirPath, entry.name); | ||||||||||||||||||
| if (entry.isDirectory()) { | ||||||||||||||||||
| walk(fullPath); | ||||||||||||||||||
| } else if (entry.isFile() && entry.name.endsWith('.json')) { | ||||||||||||||||||
| reportPaths.push(fullPath); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| walk(reportsRoot); | ||||||||||||||||||
| return reportPaths; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function getHistoricalSwapOutputMap() { | ||||||||||||||||||
| const swapOutputs = new Map(); | ||||||||||||||||||
|
|
||||||||||||||||||
| for (const reportPath of getAllSwapReportPaths()) { | ||||||||||||||||||
| try { | ||||||||||||||||||
| const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); | ||||||||||||||||||
| const outputSwapUtxos = | ||||||||||||||||||
| report.output_swap_utxos || | ||||||||||||||||||
| report.outputSwapUtxos || | ||||||||||||||||||
| report.report?.output_swap_utxos || | ||||||||||||||||||
| report.report?.outputSwapUtxos || | ||||||||||||||||||
| []; | ||||||||||||||||||
|
|
||||||||||||||||||
| outputSwapUtxos.forEach((entry) => { | ||||||||||||||||||
| if (!Array.isArray(entry) || entry.length < 2) return; | ||||||||||||||||||
| const [amount, address] = entry; | ||||||||||||||||||
| if (!address) return; | ||||||||||||||||||
| swapOutputs.set(address, toNumber(amount, 0)); | ||||||||||||||||||
| }); | ||||||||||||||||||
|
Comment on lines
+141
to
+156
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Match historical swap outputs on more than the address. The parser records 🛠️ Suggested fix function getHistoricalSwapOutputMap() {
const swapOutputs = new Map();
@@
outputSwapUtxos.forEach((entry) => {
if (!Array.isArray(entry) || entry.length < 2) return;
const [amount, address] = entry;
if (!address) return;
- swapOutputs.set(address, toNumber(amount, 0));
+ const normalizedAmount = toNumber(amount, 0);
+ const amounts = swapOutputs.get(address) || new Set();
+ amounts.add(normalizedAmount);
+ swapOutputs.set(address, amounts);
});
@@
const matchedHistoricalSwapUtxos = rawUtxos.filter(([utxoEntry]) =>
- historicalSwapOutputs.has(utxoEntry?.address)
+ historicalSwapOutputs
+ .get(utxoEntry?.address)
+ ?.has(toNumber(utxoEntry?.amount?.sats, 0))
);Also applies to: 213-218 🤖 Prompt for AI Agents |
||||||||||||||||||
| } catch (error) { | ||||||||||||||||||
| console.warn('⚠️ Failed to parse swap report for balance derivation:', { | ||||||||||||||||||
| reportPath, | ||||||||||||||||||
| error: error.message, | ||||||||||||||||||
| }); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return swapOutputs; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function deriveBalancesFromUtxos(rawUtxos = []) { | ||||||||||||||||||
| const derived = { | ||||||||||||||||||
| spendable: 0, | ||||||||||||||||||
| regular: 0, | ||||||||||||||||||
| swap: 0, | ||||||||||||||||||
| contract: 0, | ||||||||||||||||||
| fidelity: 0, | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| rawUtxos.forEach(([utxoEntry, spendInfo]) => { | ||||||||||||||||||
| const amount = toNumber(utxoEntry?.amount?.sats, 0); | ||||||||||||||||||
| const spendType = String(spendInfo?.spendType || '').toLowerCase(); | ||||||||||||||||||
| const isSpendable = Boolean(utxoEntry?.spendable); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (isSpendable) { | ||||||||||||||||||
| derived.spendable += amount; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (spendType.includes('swap')) { | ||||||||||||||||||
| derived.swap += amount; | ||||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (spendType.includes('contract')) { | ||||||||||||||||||
| derived.contract += amount; | ||||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (spendType.includes('fidelity')) { | ||||||||||||||||||
| derived.fidelity += amount; | ||||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (spendType.includes('seed') || spendType.includes('regular')) { | ||||||||||||||||||
| derived.regular += amount; | ||||||||||||||||||
| } | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| return derived; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function normalizeBalancePayload(rawBalance = {}, rawUtxos = []) { | ||||||||||||||||||
| const derivedFromUtxos = deriveBalancesFromUtxos(rawUtxos); | ||||||||||||||||||
| const historicalSwapOutputs = getHistoricalSwapOutputMap(); | ||||||||||||||||||
|
|
||||||||||||||||||
| const matchedHistoricalSwapUtxos = rawUtxos.filter(([utxoEntry]) => | ||||||||||||||||||
| historicalSwapOutputs.has(utxoEntry?.address) | ||||||||||||||||||
| ); | ||||||||||||||||||
| const historicalSwapBalance = matchedHistoricalSwapUtxos.reduce( | ||||||||||||||||||
| (sum, [utxoEntry]) => sum + toNumber(utxoEntry?.amount?.sats, 0), | ||||||||||||||||||
| 0 | ||||||||||||||||||
| ); | ||||||||||||||||||
|
|
||||||||||||||||||
| const normalized = { | ||||||||||||||||||
| spendable: toNumber( | ||||||||||||||||||
| rawBalance.spendable ?? rawBalance.spendable_balance, | ||||||||||||||||||
| derivedFromUtxos.spendable | ||||||||||||||||||
| ), | ||||||||||||||||||
| regular: toNumber( | ||||||||||||||||||
| rawBalance.regular ?? rawBalance.regular_balance, | ||||||||||||||||||
| derivedFromUtxos.regular | ||||||||||||||||||
| ), | ||||||||||||||||||
| swap: toNumber( | ||||||||||||||||||
| rawBalance.swap ?? | ||||||||||||||||||
| rawBalance.swap_balance ?? | ||||||||||||||||||
| rawBalance.swapCoin ?? | ||||||||||||||||||
| rawBalance.swapcoin, | ||||||||||||||||||
| derivedFromUtxos.swap | ||||||||||||||||||
| ), | ||||||||||||||||||
| contract: toNumber( | ||||||||||||||||||
| rawBalance.contract ?? rawBalance.contract_balance, | ||||||||||||||||||
| derivedFromUtxos.contract | ||||||||||||||||||
| ), | ||||||||||||||||||
| fidelity: toNumber( | ||||||||||||||||||
| rawBalance.fidelity ?? rawBalance.fidelity_balance, | ||||||||||||||||||
| derivedFromUtxos.fidelity | ||||||||||||||||||
| ), | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Completed swap outputs can show up as SeedCoin/regular in the current UTXO API. | ||||||||||||||||||
| // Recover that provenance by matching active UTXOs against saved swap reports. | ||||||||||||||||||
| if (historicalSwapBalance > normalized.swap) { | ||||||||||||||||||
| normalized.swap = historicalSwapBalance; | ||||||||||||||||||
| normalized.regular = Math.max( | ||||||||||||||||||
| 0, | ||||||||||||||||||
| normalized.spendable - | ||||||||||||||||||
| normalized.swap - | ||||||||||||||||||
| normalized.contract - | ||||||||||||||||||
| normalized.fidelity | ||||||||||||||||||
| ); | ||||||||||||||||||
| } | ||||||||||||||||||
|
keraliss marked this conversation as resolved.
|
||||||||||||||||||
|
|
||||||||||||||||||
| return { | ||||||||||||||||||
| normalized, | ||||||||||||||||||
| debug: { | ||||||||||||||||||
| rawBalance, | ||||||||||||||||||
| derivedFromUtxos, | ||||||||||||||||||
| historicalSwapBalance, | ||||||||||||||||||
| historicalSwapMatches: matchedHistoricalSwapUtxos.map(([utxoEntry, spendInfo]) => ({ | ||||||||||||||||||
| address: utxoEntry?.address, | ||||||||||||||||||
| amount: toNumber(utxoEntry?.amount?.sats, 0), | ||||||||||||||||||
| spendType: spendInfo?.spendType, | ||||||||||||||||||
| })), | ||||||||||||||||||
| }, | ||||||||||||||||||
| }; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| async function initNAPI() { | ||||||||||||||||||
| try { | ||||||||||||||||||
| api1State.coinswapNapi = require('coinswap-napi'); | ||||||||||||||||||
|
|
@@ -469,16 +632,16 @@ function registerTakerHandlers() { | |||||||||||||||||
| // Sync removed to prevent UI blocking on page load - relies on background sync | ||||||||||||||||||
| // api1State.takerInstance.syncAndSave(); | ||||||||||||||||||
| const balance = api1State.takerInstance.getBalances(); | ||||||||||||||||||
| const rawUtxos = api1State.takerInstance.listAllUtxoSpendInfo(); | ||||||||||||||||||
| const { normalized, debug } = normalizeBalancePayload(balance, rawUtxos); | ||||||||||||||||||
|
|
||||||||||||||||||
| console.log('💰 Raw taker balance payload:', balance); | ||||||||||||||||||
| console.log('💰 Normalized taker balance payload:', normalized); | ||||||||||||||||||
| console.log('💰 Balance derivation details:', debug); | ||||||||||||||||||
|
Comment on lines
+638
to
+640
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redact wallet metadata from these logs.
🛡️ Suggested fix- console.log('💰 Raw taker balance payload:', balance);
- console.log('💰 Normalized taker balance payload:', normalized);
- console.log('💰 Balance derivation details:', debug);
+ console.log('💰 Normalized taker balance payload:', normalized);
+ console.log('💰 Balance derivation summary:', {
+ historicalSwapBalance: debug.historicalSwapBalance,
+ historicalSwapMatchCount: debug.historicalSwapMatches.length,
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| return { | ||||||||||||||||||
| success: true, | ||||||||||||||||||
| balance: { | ||||||||||||||||||
| spendable: balance.spendable, | ||||||||||||||||||
| regular: balance.regular, | ||||||||||||||||||
| swap: balance.swap, | ||||||||||||||||||
| contract: balance.contract, | ||||||||||||||||||
| fidelity: balance.fidelity, | ||||||||||||||||||
| }, | ||||||||||||||||||
| balance: normalized, | ||||||||||||||||||
| }; | ||||||||||||||||||
|
Comment on lines
+635
to
645
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep both balance IPCs on the same normalization path. After this change 🤖 Prompt for AI Agents |
||||||||||||||||||
| } catch (error) { | ||||||||||||||||||
| console.error('❌ Failed to get balance:', error); | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't rebuild the historical swap index inside the balance hot path.
getHistoricalSwapOutputMap()synchronously walks and parses everyswap_reports/**/*.jsonfile each time balance is requested. Becausetaker:getBalanceruns on Electron's main process and is hit on initial load, wallets with many reports will block the UI here. Cache this per wallet and invalidate it whensaveSwapReport()writes a new report, or at minimum scope the scan to the active wallet directory.Also applies to: 138-166
🤖 Prompt for AI Agents