@@ -7,10 +7,20 @@ import { Xdc, Txdc } from '../../src/index';
77import { UnsignedSweepTxMPCv2 } from '@bitgo/abstract-eth' ;
88import { mockDataUnsignedSweep , mockDataNonBitGoRecovery } from '../resources' ;
99import nock from 'nock' ;
10- import { common } from '@bitgo/sdk-core' ;
10+ import { common , TransactionType , Wallet } from '@bitgo/sdk-core' ;
1111import { Transaction } from '@ethereumjs/tx' ;
1212import { stripHexPrefix } from '@ethereumjs/util' ;
1313
14+ import { TransactionBuilder } from '../../src/lib' ;
15+ import { getBuilder } from './getBuilder' ;
16+
17+ /** Encode ERC-20 transfer(address,uint256) calldata without ethereumjs-abi. */
18+ function encodeErc20Transfer ( to : string , amount : string ) : string {
19+ const address = to . toLowerCase ( ) . replace ( / ^ 0 x / , '' ) . padStart ( 64 , '0' ) ;
20+ const value = BigInt ( amount ) . toString ( 16 ) . padStart ( 64 , '0' ) ;
21+ return `0xa9059cbb${ address } ${ value } ` ;
22+ }
23+
1424const bitgo : TestBitGoAPI = TestBitGo . decorate ( BitGoAPI , { env : 'test' } ) ;
1525
1626describe ( 'xdc' , function ( ) {
@@ -45,6 +55,119 @@ describe('xdc', function () {
4555 txdc . allowsAccountConsolidations ( ) . should . equal ( false ) ;
4656 } ) ;
4757 } ) ;
58+
59+ describe ( 'verifyTssTransaction' , function ( ) {
60+ const recipientAddress = '0x174cfd823af8ce27ed0afee3fcf3c3ba259116be' ;
61+ const wrongAddress = '0x7e85bdc27c050e3905ebf4b8e634d9ad6edd0de6' ;
62+ const tokenContractAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ;
63+ const transferAmount = '1000000000000000000' ;
64+
65+ it ( 'should accept a native XDC transfer where txHex matches declared recipient' , async function ( ) {
66+ const coin = bitgo . coin ( 'txdc' ) as Txdc ;
67+
68+ const txBuilder = getBuilder ( 'txdc' ) as TransactionBuilder ;
69+ txBuilder . type ( TransactionType . SingleSigSend ) ;
70+ txBuilder . fee ( { fee : '10' , gasLimit : '21000' } ) ;
71+ txBuilder . counter ( 1 ) ;
72+ txBuilder . contract ( recipientAddress ) ;
73+ txBuilder . value ( transferAmount ) ;
74+ const tx = await txBuilder . build ( ) ;
75+ const txHex = tx . toBroadcastFormat ( ) ;
76+
77+ const wallet = new Wallet ( bitgo , coin , { coinSpecific : { baseAddress : recipientAddress } } ) ;
78+
79+ const result = await coin . verifyTssTransaction ( {
80+ txParams : {
81+ type : 'transfer' ,
82+ recipients : [ { address : recipientAddress , amount : transferAmount } ] ,
83+ } as any ,
84+ txPrebuild : { txHex, coin : 'txdc' , walletId : 'fakeWalletId' } as any ,
85+ wallet,
86+ } ) ;
87+ result . should . equal ( true ) ;
88+ } ) ;
89+
90+ it ( 'should reject a native XDC transfer when txHex recipient does not match declared recipient' , async function ( ) {
91+ const coin = bitgo . coin ( 'txdc' ) as Txdc ;
92+
93+ const txBuilder = getBuilder ( 'txdc' ) as TransactionBuilder ;
94+ txBuilder . type ( TransactionType . SingleSigSend ) ;
95+ txBuilder . fee ( { fee : '10' , gasLimit : '21000' } ) ;
96+ txBuilder . counter ( 1 ) ;
97+ txBuilder . contract ( wrongAddress ) ;
98+ txBuilder . value ( transferAmount ) ;
99+ const tx = await txBuilder . build ( ) ;
100+ const txHex = tx . toBroadcastFormat ( ) ;
101+
102+ const wallet = new Wallet ( bitgo , coin , { coinSpecific : { baseAddress : recipientAddress } } ) ;
103+
104+ await coin
105+ . verifyTssTransaction ( {
106+ txParams : {
107+ type : 'transfer' ,
108+ recipients : [ { address : recipientAddress , amount : transferAmount } ] ,
109+ } as any ,
110+ txPrebuild : { txHex, coin : 'txdc' , walletId : 'fakeWalletId' } as any ,
111+ wallet,
112+ } )
113+ . should . be . rejectedWith ( 'destination address does not match with the recipient address' ) ;
114+ } ) ;
115+
116+ it ( 'should accept an ERC-20 token transfer where calldata matches declared recipient' , async function ( ) {
117+ const coin = bitgo . coin ( 'txdc' ) as Txdc ;
118+
119+ const erc20TransferData = encodeErc20Transfer ( recipientAddress , '10000000' ) ;
120+
121+ const txBuilder = getBuilder ( 'txdc' ) as TransactionBuilder ;
122+ txBuilder . type ( TransactionType . ContractCall ) ;
123+ txBuilder . fee ( { fee : '10' , gasLimit : '60000' } ) ;
124+ txBuilder . counter ( 1 ) ;
125+ txBuilder . contract ( tokenContractAddress ) ;
126+ txBuilder . data ( erc20TransferData ) ;
127+ const tx = await txBuilder . build ( ) ;
128+ const txHex = tx . toBroadcastFormat ( ) ;
129+
130+ const wallet = new Wallet ( bitgo , coin , { coinSpecific : { baseAddress : recipientAddress } } ) ;
131+
132+ const result = await coin . verifyTssTransaction ( {
133+ txParams : {
134+ type : 'transfer' ,
135+ recipients : [ { address : recipientAddress , amount : '10000000' } ] ,
136+ } as any ,
137+ txPrebuild : { txHex, coin : 'txdc' , walletId : 'fakeWalletId' } as any ,
138+ wallet,
139+ } ) ;
140+ result . should . equal ( true ) ;
141+ } ) ;
142+
143+ it ( 'should reject an ERC-20 token transfer when calldata recipient does not match declared recipient' , async function ( ) {
144+ const coin = bitgo . coin ( 'txdc' ) as Txdc ;
145+
146+ const erc20TransferData = encodeErc20Transfer ( wrongAddress , '10000000' ) ;
147+
148+ const txBuilder = getBuilder ( 'txdc' ) as TransactionBuilder ;
149+ txBuilder . type ( TransactionType . ContractCall ) ;
150+ txBuilder . fee ( { fee : '10' , gasLimit : '60000' } ) ;
151+ txBuilder . counter ( 1 ) ;
152+ txBuilder . contract ( tokenContractAddress ) ;
153+ txBuilder . data ( erc20TransferData ) ;
154+ const tx = await txBuilder . build ( ) ;
155+ const txHex = tx . toBroadcastFormat ( ) ;
156+
157+ const wallet = new Wallet ( bitgo , coin , { coinSpecific : { baseAddress : recipientAddress } } ) ;
158+
159+ await coin
160+ . verifyTssTransaction ( {
161+ txParams : {
162+ type : 'transfer' ,
163+ recipients : [ { address : recipientAddress , amount : '10000000' } ] ,
164+ } as any ,
165+ txPrebuild : { txHex, coin : 'txdc' , walletId : 'fakeWalletId' } as any ,
166+ wallet,
167+ } )
168+ . should . be . rejectedWith ( 'destination address does not match with the recipient address' ) ;
169+ } ) ;
170+ } ) ;
48171} ) ;
49172
50173describe ( 'Build Unsigned Sweep for Self-Custody Cold Wallets - (MPCv2)' , function ( ) {
0 commit comments