@@ -3031,6 +3031,34 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
30313031 return txPrebuild . coin === nativeCoin ;
30323032 }
30333033
3034+ /**
3035+ * Generate transaction explanation for error reporting
3036+ * @param txPrebuild - Transaction prebuild containing txHex and fee info
3037+ * @returns Stringified JSON explanation
3038+ */
3039+ private async getTxExplanation ( txPrebuild ?: TransactionPrebuild ) : Promise < string | undefined > {
3040+ if ( ! txPrebuild ?. txHex || ! txPrebuild ?. gasPrice ) {
3041+ return undefined ;
3042+ }
3043+
3044+ try {
3045+ const explanation = await this . explainTransaction ( {
3046+ txHex : txPrebuild . txHex ,
3047+ feeInfo : {
3048+ fee : txPrebuild . gasPrice . toString ( ) ,
3049+ } ,
3050+ } ) ;
3051+ return JSON . stringify ( explanation , null , 2 ) ;
3052+ } catch ( e ) {
3053+ const errorDetails = {
3054+ error : 'Failed to parse transaction explanation' ,
3055+ txHex : txPrebuild . txHex ,
3056+ details : e instanceof Error ? e . message : String ( e ) ,
3057+ } ;
3058+ return JSON . stringify ( errorDetails , null , 2 ) ;
3059+ }
3060+ }
3061+
30343062 /**
30353063 * Verify if a tss transaction is valid
30363064 *
@@ -3045,15 +3073,25 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
30453073 const { txParams, txPrebuild, wallet } = params ;
30463074
30473075 // Helper to throw TxIntentMismatchRecipientError with recipient details
3048- const throwRecipientMismatch = ( message : string , mismatchedRecipients : Recipient [ ] ) : never => {
3049- throw new TxIntentMismatchRecipientError ( message , undefined , [ txParams ] , txPrebuild ?. txHex , mismatchedRecipients ) ;
3076+ const throwRecipientMismatch = async ( message : string , mismatchedRecipients : Recipient [ ] ) : Promise < never > => {
3077+ const txExplanation = await this . getTxExplanation ( txPrebuild ) ;
3078+ throw new TxIntentMismatchRecipientError (
3079+ message ,
3080+ undefined ,
3081+ [ txParams ] ,
3082+ txPrebuild ?. txHex ,
3083+ mismatchedRecipients ,
3084+ txExplanation
3085+ ) ;
30503086 } ;
30513087
30523088 if (
30533089 ! txParams ?. recipients &&
30543090 ! (
30553091 txParams . prebuildTx ?. consolidateId ||
3056- ( txParams . type && [ 'acceleration' , 'fillNonce' , 'transferToken' , 'tokenApproval' ] . includes ( txParams . type ) )
3092+ txPrebuild ?. consolidateId ||
3093+ ( txParams . type &&
3094+ [ 'acceleration' , 'fillNonce' , 'transferToken' , 'tokenApproval' , 'consolidate' ] . includes ( txParams . type ) )
30573095 )
30583096 ) {
30593097 throw new Error ( 'missing txParams' ) ;
@@ -3077,12 +3115,13 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
30773115 const txJson = tx . toJson ( ) ;
30783116 if ( txJson . data === '0x' ) {
30793117 if ( expectedAmount !== txJson . value ) {
3080- throwRecipientMismatch ( 'the transaction amount in txPrebuild does not match the value given by client' , [
3081- { address : txJson . to , amount : txJson . value } ,
3082- ] ) ;
3118+ await throwRecipientMismatch (
3119+ 'the transaction amount in txPrebuild does not match the value given by client' ,
3120+ [ { address : txJson . to , amount : txJson . value } ]
3121+ ) ;
30833122 }
30843123 if ( expectedDestination . toLowerCase ( ) !== txJson . to . toLowerCase ( ) ) {
3085- throwRecipientMismatch ( 'destination address does not match with the recipient address' , [
3124+ await throwRecipientMismatch ( 'destination address does not match with the recipient address' , [
30863125 { address : txJson . to , amount : txJson . value } ,
30873126 ] ) ;
30883127 }
@@ -3112,20 +3151,50 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
31123151 }
31133152
31143153 if ( expectedTokenAmount !== amount . toString ( ) ) {
3115- throwRecipientMismatch ( 'the transaction amount in txPrebuild does not match the value given by client' , [
3116- { address : addHexPrefix ( recipientAddress . toString ( ) ) , amount : amount . toString ( ) } ,
3117- ] ) ;
3154+ await throwRecipientMismatch (
3155+ 'the transaction amount in txPrebuild does not match the value given by client' ,
3156+ [ { address : addHexPrefix ( recipientAddress . toString ( ) ) , amount : amount . toString ( ) } ]
3157+ ) ;
31183158 }
31193159
31203160 if ( expectedRecipientAddress !== addHexPrefix ( recipientAddress . toString ( ) ) . toLowerCase ( ) ) {
3121- throwRecipientMismatch ( 'destination address does not match with the recipient address' , [
3161+ await throwRecipientMismatch ( 'destination address does not match with the recipient address' , [
31223162 { address : addHexPrefix ( recipientAddress . toString ( ) ) , amount : amount . toString ( ) } ,
31233163 ] ) ;
31243164 }
31253165 }
31263166 }
31273167 }
31283168
3169+ // Verify consolidation transactions send to base address
3170+ if ( params . verification ?. consolidationToBaseAddress ) {
3171+ const coinSpecific = wallet . coinSpecific ( ) ;
3172+ if ( ! coinSpecific || ! coinSpecific . baseAddress ) {
3173+ throw new Error ( 'Unable to determine base address for consolidation' ) ;
3174+ }
3175+ const baseAddress = coinSpecific . baseAddress ;
3176+
3177+ if ( ! txPrebuild . txHex ) {
3178+ throw new Error ( 'missing txHex in txPrebuild' ) ;
3179+ }
3180+
3181+ const txBuilder = this . getTransactionBuilder ( ) ;
3182+ txBuilder . from ( txPrebuild . txHex ) ;
3183+ const tx = await txBuilder . build ( ) ;
3184+ const txJson = tx . toJson ( ) ;
3185+
3186+ // Verify the transaction recipient matches the base address
3187+ if ( ! txJson . to ) {
3188+ throw new Error ( 'Consolidation transaction is missing recipient address' ) ;
3189+ }
3190+
3191+ if ( txJson . to . toLowerCase ( ) !== baseAddress . toLowerCase ( ) ) {
3192+ await throwRecipientMismatch ( 'Consolidation transaction recipient does not match wallet base address' , [
3193+ { address : txJson . to , amount : txJson . value } ,
3194+ ] ) ;
3195+ }
3196+ }
3197+
31293198 return true ;
31303199 }
31313200
@@ -3149,8 +3218,16 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
31493218 }
31503219
31513220 // Helper to throw TxIntentMismatchRecipientError with recipient details
3152- const throwRecipientMismatch = ( message : string , mismatchedRecipients : Recipient [ ] ) : never => {
3153- throw new TxIntentMismatchRecipientError ( message , undefined , [ txParams ] , txPrebuild ?. txHex , mismatchedRecipients ) ;
3221+ const throwRecipientMismatch = async ( message : string , mismatchedRecipients : Recipient [ ] ) : Promise < never > => {
3222+ const txExplanation = await this . getTxExplanation ( txPrebuild ) ;
3223+ throw new TxIntentMismatchRecipientError (
3224+ message ,
3225+ undefined ,
3226+ [ txParams ] ,
3227+ txPrebuild ?. txHex ,
3228+ mismatchedRecipients ,
3229+ txExplanation
3230+ ) ;
31543231 } ;
31553232
31563233 if ( ! txParams ?. recipients || ! txPrebuild ?. recipients || ! wallet ) {
@@ -3180,7 +3257,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
31803257 const expectedHopAddress = optionalDeps . ethUtil . stripHexPrefix ( decodedHopTx . getSenderAddress ( ) . toString ( ) ) ;
31813258 const actualHopAddress = optionalDeps . ethUtil . stripHexPrefix ( txPrebuild . recipients [ 0 ] . address ) ;
31823259 if ( expectedHopAddress . toLowerCase ( ) !== actualHopAddress . toLowerCase ( ) ) {
3183- throwRecipientMismatch ( 'recipient address of txPrebuild does not match hop address' , [
3260+ await throwRecipientMismatch ( 'recipient address of txPrebuild does not match hop address' , [
31843261 { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ,
31853262 ] ) ;
31863263 }
@@ -3200,17 +3277,18 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
32003277 if ( txParams . tokenName ) {
32013278 const expectedTotalAmount = new BigNumber ( 0 ) ;
32023279 if ( ! expectedTotalAmount . isEqualTo ( txPrebuild . recipients [ 0 ] . amount ) ) {
3203- throwRecipientMismatch ( 'batch token transaction amount in txPrebuild should be zero for token transfers' , [
3204- { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ,
3205- ] ) ;
3280+ await throwRecipientMismatch (
3281+ 'batch token transaction amount in txPrebuild should be zero for token transfers' ,
3282+ [ { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ]
3283+ ) ;
32063284 }
32073285 } else {
32083286 let expectedTotalAmount = new BigNumber ( 0 ) ;
32093287 for ( let i = 0 ; i < recipients . length ; i ++ ) {
32103288 expectedTotalAmount = expectedTotalAmount . plus ( recipients [ i ] . amount ) ;
32113289 }
32123290 if ( ! expectedTotalAmount . isEqualTo ( txPrebuild . recipients [ 0 ] . amount ) ) {
3213- throwRecipientMismatch (
3291+ await throwRecipientMismatch (
32143292 'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client' ,
32153293 [ { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ]
32163294 ) ;
@@ -3223,7 +3301,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
32233301 ! batcherContractAddress ||
32243302 batcherContractAddress . toLowerCase ( ) !== txPrebuild . recipients [ 0 ] . address . toLowerCase ( )
32253303 ) {
3226- throwRecipientMismatch ( 'recipient address of txPrebuild does not match batcher address' , [
3304+ await throwRecipientMismatch ( 'recipient address of txPrebuild does not match batcher address' , [
32273305 { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ,
32283306 ] ) ;
32293307 }
@@ -3234,25 +3312,27 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
32343312 }
32353313 const expectedAmount = new BigNumber ( recipients [ 0 ] . amount ) ;
32363314 if ( ! expectedAmount . isEqualTo ( txPrebuild . recipients [ 0 ] . amount ) ) {
3237- throwRecipientMismatch (
3315+ await throwRecipientMismatch (
32383316 'normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client' ,
32393317 [ { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ]
32403318 ) ;
32413319 }
32423320 if ( this . isETHAddress ( recipients [ 0 ] . address ) && recipients [ 0 ] . address !== txPrebuild . recipients [ 0 ] . address ) {
3243- throwRecipientMismatch (
3321+ await throwRecipientMismatch (
32443322 'destination address in normal txPrebuild does not match that in txParams supplied by client' ,
32453323 [ { address : txPrebuild . recipients [ 0 ] . address , amount : txPrebuild . recipients [ 0 ] . amount . toString ( ) } ]
32463324 ) ;
32473325 }
32483326 }
32493327 // Check coin is correct for all transaction types
32503328 if ( ! this . verifyCoin ( txPrebuild ) ) {
3329+ const txExplanation = await this . getTxExplanation ( txPrebuild ) ;
32513330 throw new TxIntentMismatchError (
32523331 'coin in txPrebuild did not match that in txParams supplied by client' ,
32533332 undefined ,
32543333 [ txParams ] ,
3255- txPrebuild ?. txHex
3334+ txPrebuild ?. txHex ,
3335+ txExplanation
32563336 ) ;
32573337 }
32583338 return true ;
0 commit comments