Fix receive and send

This commit is contained in:
2026-03-16 06:48:29 +00:00
parent 9ef1720e1f
commit dd275593cd
28 changed files with 1918 additions and 769 deletions

View File

@@ -6,10 +6,11 @@
* Shows required, selected, and change amounts.
*/
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Box, Text, useInput } from 'ink';
import { colors, formatSatoshis } from '../../../../theme.js';
import type { InputsSelectStepProps, SelectableUTXO } from '../types.js';
import { autoSelectGreedyUtxos, mapUnspentOutputsToSelectable } from '../../../../../utils/invitation-flow.js';
/** Default fee estimate in satoshis. */
const DEFAULT_FEE = 500n;
@@ -64,34 +65,9 @@ export function InputsSelectStep({
outputIdentifier: 'receiveOutput',
});
// Map to selectable UTXOs
const selectable: SelectableUTXO[] = unspentOutputs.map((utxo: any) => ({
outpointTransactionHash: utxo.outpointTransactionHash,
outpointIndex: utxo.outpointIndex,
valueSatoshis: BigInt(utxo.valueSatoshis),
lockingBytecode: utxo.lockingBytecode
? typeof utxo.lockingBytecode === 'string'
? utxo.lockingBytecode
: Buffer.from(utxo.lockingBytecode).toString('hex')
: undefined,
selected: false,
}));
// Greedy auto-select, skipping duplicate locking bytecodes
let accumulated = 0n;
const seenBytecodes = new Set<string>();
for (const utxo of selectable) {
if (utxo.lockingBytecode && seenBytecodes.has(utxo.lockingBytecode)) continue;
if (utxo.lockingBytecode) seenBytecodes.add(utxo.lockingBytecode);
utxo.selected = true;
accumulated += utxo.valueSatoshis;
if (accumulated >= required + fee) break;
}
setUtxos(selectable);
const selectable = mapUnspentOutputsToSelectable(unspentOutputs);
const autoSelected = autoSelectGreedyUtxos(selectable, required + fee);
setUtxos(autoSelected as SelectableUTXO[]);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
@@ -99,9 +75,15 @@ export function InputsSelectStep({
}
}, [invitation, computeRequiredAmount, fee]);
// Load UTXOs on mount
// Load UTXOs once on mount. We use a ref guard to prevent re-firing when
// `loadUtxos` identity changes due to parent re-renders — each re-fire
// flashes the loading state, causing the visible flicker bug.
const hasLoadedRef = useRef(false);
useEffect(() => {
if (isActive) loadUtxos();
if (isActive && !hasLoadedRef.current) {
hasLoadedRef.current = true;
loadUtxos();
}
}, [isActive, loadUtxos]);
/**