diff --git a/Electrum.sqlite-journal b/Electrum.sqlite-journal new file mode 100644 index 0000000..b7c7941 Binary files /dev/null and b/Electrum.sqlite-journal differ diff --git a/src/tui/screens/Transaction.tsx b/src/tui/screens/Transaction.tsx index 6bff8f5..6a5d01f 100644 --- a/src/tui/screens/Transaction.tsx +++ b/src/tui/screens/Transaction.tsx @@ -189,11 +189,22 @@ export function TransactionScreen(): React.ReactElement { // Extract transaction data from invitation const commits = invitation?.commits ?? []; - const inputs: Array<{ txid: string; index: number; value?: bigint }> = []; - const outputs: Array<{ value: bigint; lockingBytecode: string }> = []; + const inputs: Array<{ txid: string; index: number; value?: bigint; inputIdentifier?: string }> = []; + const outputs: Array<{ value?: bigint; lockingBytecode: string; outputIdentifier?: string; isTemplate: boolean }> = []; + const variables: Array<{ id: string; value: string }> = []; - // Parse commits for inputs and outputs + // Parse commits for inputs, outputs, and variables for (const commit of commits) { + // Extract variables (to help understand output values) + if (commit.data?.variables) { + for (const variable of commit.data.variables) { + variables.push({ + id: variable.variableIdentifier, + value: String(variable.value), + }); + } + } + if (commit.data?.inputs) { for (const input of commit.data.inputs) { // Convert Uint8Array to hex string if needed @@ -201,13 +212,17 @@ export function TransactionScreen(): React.ReactElement { ? typeof input.outpointTransactionHash === 'string' ? input.outpointTransactionHash : Buffer.from(input.outpointTransactionHash).toString('hex') - : 'unknown'; + : undefined; - inputs.push({ - txid: txidHex, - index: input.outpointIndex ?? 0, - value: undefined, // libauth Input doesn't have valueSatoshis directly - }); + // Skip inputs that are just placeholders (no txid) + if (txidHex) { + inputs.push({ + txid: txidHex, + index: input.outpointIndex ?? 0, + value: undefined, // Will be looked up from UTXO data + inputIdentifier: (input as any).inputIdentifier, + }); + } } } if (commit.data?.outputs) { @@ -217,20 +232,45 @@ export function TransactionScreen(): React.ReactElement { ? typeof output.lockingBytecode === 'string' ? output.lockingBytecode : Buffer.from(output.lockingBytecode).toString('hex') - : 'unknown'; + : undefined; + + // Check if this is a template-defined output (has outputIdentifier but no direct value) + const isTemplateOutput = !!(output as any).outputIdentifier && !output.valueSatoshis; outputs.push({ - value: output.valueSatoshis ?? 0n, - lockingBytecode: lockingBytecodeHex, + value: output.valueSatoshis, + lockingBytecode: lockingBytecodeHex ?? '(pending)', + outputIdentifier: (output as any).outputIdentifier, + isTemplate: isTemplateOutput, }); } } } - // Calculate totals - const totalIn = inputs.reduce((sum, i) => sum + (i.value ?? 0n), 0n); - const totalOut = outputs.reduce((sum, o) => sum + o.value, 0n); - const fee = totalIn - totalOut; + // Try to resolve template output values from variables + const resolvedOutputs = outputs.map(output => { + if (output.isTemplate && output.outputIdentifier) { + // Look for a matching variable (e.g., requestSatoshisOutput -> requestedSatoshis) + const satoshiVar = variables.find(v => + v.id.toLowerCase().includes('satoshi') || + v.id.toLowerCase().includes('amount') + ); + if (satoshiVar) { + return { + ...output, + value: BigInt(satoshiVar.value), + resolvedFrom: satoshiVar.id, + }; + } + } + return output; + }); + + // Calculate totals (only for resolved values) + const totalOut = resolvedOutputs.reduce((sum, o) => sum + (o.value ?? 0n), 0n); + // Note: We can't calculate totalIn without UTXO lookup, so fee is unknown + const hasUnresolvedOutputs = resolvedOutputs.some(o => o.value === undefined); + const hasUnresolvedInputs = inputs.length > 0; // Input values are always unknown from commit data return ( @@ -251,10 +291,16 @@ export function TransactionScreen(): React.ReactElement { Transaction Summary {invitation ? ( - Inputs: {inputs.length} | Outputs: {outputs.length} | Commits: {commits.length} - Total In: {formatSatoshis(totalIn)} - Total Out: {formatSatoshis(totalOut)} - Fee: {formatSatoshis(fee)} + Inputs: {inputs.length} | Outputs: {resolvedOutputs.length} | Commits: {commits.length} + {hasUnresolvedInputs && ( + Total In: (requires UTXO lookup) + )} + Total Out: {formatSatoshis(totalOut)}{hasUnresolvedOutputs ? ' (partial)' : ''} + {hasUnresolvedInputs ? ( + Fee: (calculated at broadcast) + ) : ( + Fee: {formatSatoshis(0n)} + )} ) : ( Loading... @@ -301,15 +347,21 @@ export function TransactionScreen(): React.ReactElement { > Outputs - {outputs.length === 0 ? ( + {resolvedOutputs.length === 0 ? ( No outputs ) : ( - outputs.map((output, index) => ( + resolvedOutputs.map((output, index) => ( - {index + 1}. {formatSatoshis(output.value)} + {index + 1}. {output.value !== undefined ? formatSatoshis(output.value) : '(pending)'} + {output.outputIdentifier && ( + [{output.outputIdentifier}] + )} - {formatHex(output.lockingBytecode, 20)} + {output.lockingBytecode !== '(pending)' ? formatHex(output.lockingBytecode, 20) : '(pending)'} + {(output as any).resolvedFrom && ( + (from ${(output as any).resolvedFrom}) + )} )) )}