@@ -7,18 +7,19 @@ import * as utxolib from '@bitgo/utxo-lib';
77import type { AbstractUtxoCoin , ParseTransactionOptions } from '../../abstractUtxoCoin' ;
88import type { FixedScriptWalletOutput , Output , ParsedTransaction } from '../types' ;
99import { fetchKeychains , getKeySignatures , toKeychainTriple , UtxoKeychain , UtxoNamedKeychains } from '../../keychains' ;
10- import { ComparableOutput , outputDifference } from '../outputDifference' ;
1110import {
1211 assertValidTransactionRecipient ,
1312 fromExtendedAddressFormatToScript ,
1413 isScriptRecipient ,
1514 toExtendedAddressFormat ,
15+ toOutputScript ,
1616} from '../recipient' ;
17+ import { ComparableOutput , ExpectedOutput , outputDifference } from '../outputDifference' ;
1718
1819import type { TransactionExplanation } from './explainTransaction' ;
1920import { CustomChangeOptions , parseOutput } from './parseOutput' ;
2021
21- export type ComparableOutputWithExternal < TValue > = ComparableOutput < TValue > & {
22+ export type ComparableOutputWithExternal < TValue > = ( ComparableOutput < TValue > | ExpectedOutput ) & {
2223 external : boolean | undefined ;
2324} ;
2425
@@ -76,21 +77,37 @@ function toExpectedOutputs(
7677 allowExternalChangeAddress ?: boolean ;
7778 changeAddress ?: string ;
7879 }
79- ) : Output [ ] {
80+ ) : ExpectedOutput [ ] {
8081 // verify that each recipient from txParams has their own output
81- const expectedOutputs = ( txParams . recipients ?? [ ] ) . flatMap ( ( output ) => {
82+ const expectedOutputs : ExpectedOutput [ ] = ( txParams . recipients ?? [ ] ) . flatMap ( ( output ) => {
8283 if ( output . address === undefined ) {
84+ assert ( 'script' in output , 'script is required for non-encodeable scriptPubkeys' ) ;
8385 if ( output . amount . toString ( ) !== '0' ) {
8486 throw new Error ( `Only zero amounts allowed for non-encodeable scriptPubkeys: ${ output } ` ) ;
8587 }
86- return [ output ] ;
88+ return [
89+ {
90+ script : toOutputScript ( output , coin . name ) ,
91+ value : output . amount === 'max' ? 'max' : BigInt ( output . amount ) ,
92+ } ,
93+ ] ;
8794 }
88- return [ { ...output , address : coin . canonicalAddress ( output . address ) } ] ;
95+ return [
96+ {
97+ script : fromExtendedAddressFormatToScript ( output . address , coin . name ) ,
98+ value : output . amount === 'max' ? 'max' : BigInt ( output . amount ) ,
99+ } ,
100+ ] ;
89101 } ) ;
90102 if ( txParams . allowExternalChangeAddress && txParams . changeAddress ) {
91- // when an external change address is explicitly specified, count all outputs going towards that
103+ // When an external change address is explicitly specified, count all outputs going towards that
92104 // address in the expected outputs (regardless of the output amount)
93- expectedOutputs . push ( { address : coin . canonicalAddress ( txParams . changeAddress ) , amount : 'max' } ) ;
105+ // Note that the output is not required to exist, so we filter it out later.
106+ expectedOutputs . push ( {
107+ script : toOutputScript ( txParams . changeAddress , coin . name ) ,
108+ value : 'max' ,
109+ optional : true ,
110+ } ) ;
94111 }
95112 return expectedOutputs ;
96113}
@@ -206,15 +223,9 @@ export async function parseTransaction<TNumber extends bigint | number>(
206223 } ) ) ;
207224 }
208225
209- const missingOutputs = outputDifference (
210- toComparableOutputsWithExternal ( expectedOutputs ) ,
211- toComparableOutputsWithExternal ( allOutputs )
212- ) ;
226+ const missingOutputs = outputDifference ( expectedOutputs , toComparableOutputsWithExternal ( allOutputs ) ) ;
213227
214- const implicitOutputs = outputDifference (
215- toComparableOutputsWithExternal ( allOutputDetails ) ,
216- toComparableOutputsWithExternal ( expectedOutputs )
217- ) ;
228+ const implicitOutputs = outputDifference ( toComparableOutputsWithExternal ( allOutputDetails ) , expectedOutputs ) ;
218229 const explicitOutputs = outputDifference ( toComparableOutputsWithExternal ( allOutputDetails ) , implicitOutputs ) ;
219230
220231 // these are all the non-wallet outputs that had been originally explicitly specified in recipients
@@ -244,7 +255,7 @@ export async function parseTransaction<TNumber extends bigint | number>(
244255 coin . amountType
245256 ) ;
246257
247- function toOutputs ( outputs : ComparableOutputWithExternal < bigint | 'max' > [ ] ) : Output [ ] {
258+ function toOutputs ( outputs : ExpectedOutput [ ] | ComparableOutputWithExternal < bigint | 'max' > [ ] ) : Output [ ] {
248259 return outputs . map ( ( output ) => ( {
249260 address : toExtendedAddressFormat ( output . script , coin . name ) ,
250261 amount : output . value . toString ( ) ,
0 commit comments