@@ -34,6 +34,7 @@ import { getBuilder } from './getBuilder';
3434import * as testData from '../resources/eth' ;
3535import * as mockData from '../fixtures/eth' ;
3636import should from 'should' ;
37+ import EthereumAbi from 'ethereumjs-abi' ;
3738import { ethMultiSigBackupKey } from './fixtures/ethMultiSigBackupKey' ;
3839import { ethTssBackupKey } from './fixtures/ethTssBackupKey' ;
3940
@@ -1007,6 +1008,76 @@ describe('ETH:', function () {
10071008 . should . be . rejectedWith ( 'missing txHex in txPrebuild' ) ;
10081009 } ) ;
10091010
1011+ it ( 'should verify ERC20 token consolidation when calldata recipient matches base address' , async function ( ) {
1012+ const coin = bitgo . coin ( 'hteth' ) as Hteth ;
1013+ const baseAddress = '0x174cfd823af8ce27ed0afee3fcf3c3ba259116be' ;
1014+ const tokenContractAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ;
1015+
1016+ // Build an ERC20 transfer(address, uint256) tx — mimics what the server returns
1017+ // for a v6 TSS wallet token consolidation: txJson.to = token contract,
1018+ // actual recipient is encoded in the 0xa9059cbb calldata
1019+ const methodId = EthereumAbi . methodID ( 'transfer' , [ 'address' , 'uint256' ] ) ;
1020+ const encodedParams = EthereumAbi . rawEncode ( [ 'address' , 'uint256' ] , [ baseAddress , '10000000' ] ) ;
1021+ const erc20TransferData = '0x' + Buffer . concat ( [ methodId , encodedParams ] ) . toString ( 'hex' ) ;
1022+
1023+ const txBuilder = getBuilder ( 'hteth' ) as TransactionBuilder ;
1024+ txBuilder . type ( TransactionType . ContractCall ) ;
1025+ txBuilder . fee ( { fee : '10' , gasLimit : '60000' } ) ;
1026+ txBuilder . counter ( 1 ) ;
1027+ txBuilder . contract ( tokenContractAddress ) ;
1028+ txBuilder . data ( erc20TransferData ) ;
1029+ const tx = await txBuilder . build ( ) ;
1030+ const txHex = tx . toBroadcastFormat ( ) ;
1031+
1032+ const wallet = new Wallet ( bitgo , coin , {
1033+ coinSpecific : { baseAddress } ,
1034+ } ) ;
1035+
1036+ const isTransactionVerified = await coin . verifyTransaction ( {
1037+ txParams : { type : 'consolidate' , wallet, walletPassphrase : 'fake' } as any ,
1038+ txPrebuild : { consolidateId : 'abc123' , txHex, coin : 'hteth' , walletId : 'fakeWalletId' } as any ,
1039+ wallet,
1040+ verification : { consolidationToBaseAddress : true } ,
1041+ walletType : 'tss' ,
1042+ } ) ;
1043+ isTransactionVerified . should . equal ( true ) ;
1044+ } ) ;
1045+
1046+ it ( 'should reject ERC20 token consolidation when calldata recipient does not match base address' , async function ( ) {
1047+ const coin = bitgo . coin ( 'hteth' ) as Hteth ;
1048+ const baseAddress = '0x174cfd823af8ce27ed0afee3fcf3c3ba259116be' ;
1049+ const wrongRecipient = '0x7e85bdc27c050e3905ebf4b8e634d9ad6edd0de6' ;
1050+ const tokenContractAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ;
1051+
1052+ // Build an ERC20 transfer where calldata recipient is a WRONG address
1053+ const methodId = EthereumAbi . methodID ( 'transfer' , [ 'address' , 'uint256' ] ) ;
1054+ const encodedParams = EthereumAbi . rawEncode ( [ 'address' , 'uint256' ] , [ wrongRecipient , '10000000' ] ) ;
1055+ const erc20TransferData = '0x' + Buffer . concat ( [ methodId , encodedParams ] ) . toString ( 'hex' ) ;
1056+
1057+ const txBuilder = getBuilder ( 'hteth' ) as TransactionBuilder ;
1058+ txBuilder . type ( TransactionType . ContractCall ) ;
1059+ txBuilder . fee ( { fee : '10' , gasLimit : '60000' } ) ;
1060+ txBuilder . counter ( 1 ) ;
1061+ txBuilder . contract ( tokenContractAddress ) ;
1062+ txBuilder . data ( erc20TransferData ) ;
1063+ const tx = await txBuilder . build ( ) ;
1064+ const txHex = tx . toBroadcastFormat ( ) ;
1065+
1066+ const wallet = new Wallet ( bitgo , coin , {
1067+ coinSpecific : { baseAddress } ,
1068+ } ) ;
1069+
1070+ await coin
1071+ . verifyTransaction ( {
1072+ txParams : { type : 'consolidate' , wallet, walletPassphrase : 'fake' } as any ,
1073+ txPrebuild : { consolidateId : 'abc123' , txHex, coin : 'hteth' , walletId : 'fakeWalletId' } as any ,
1074+ wallet,
1075+ verification : { consolidationToBaseAddress : true } ,
1076+ walletType : 'tss' ,
1077+ } )
1078+ . should . be . rejectedWith ( 'Consolidation transaction recipient does not match wallet base address' ) ;
1079+ } ) ;
1080+
10101081 it ( 'should throw error when wallet is missing baseAddress for consolidation verification' , async function ( ) {
10111082 const coin = bitgo . coin ( 'hteth' ) as Hteth ;
10121083 const baseAddress = '0x174cfd823af8ce27ed0afee3fcf3c3ba259116be' ;
0 commit comments