Skip to content

Commit 652bec2

Browse files
committed
fix(bsc): remove ethereumjs-util import, add WalletConnect test coverage
Replace the ethereumjs-util addHexPrefix import (not in sdk-coin-bsc deps) with an inline helper. ethereumjs-util is a transitive dep of abstract-eth, not a direct dep of sdk-coin-bsc, so relying on it was fragile. Add two tests for the WalletConnect recipients[0].data flow in verifyTssTransaction: - BEP-20 with matching recipients[0].data passes - BEP-20 with tampered txHex calldata but correct recipients[0].data throws (tampered-recipient rejection) Ticket: WCI-1169 Session-Id: 4ade7ff7-085e-476d-b280-f4d3dd01c105 Task-Id: 9e3fa483-2a00-47db-aa04-c64b6f003a77
1 parent 98afd5e commit 652bec2

3 files changed

Lines changed: 70 additions & 2 deletions

File tree

modules/sdk-coin-bsc/src/bsc.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import {
1717
recoveryBlockchainExplorerQuery,
1818
VerifyEthTransactionOptions,
1919
} from '@bitgo/abstract-eth';
20-
import { addHexPrefix } from 'ethereumjs-util';
2120
import { TransactionBuilder } from './lib';
2221

22+
const addHexPrefix = (hex: string): string => (hex.startsWith('0x') ? hex : `0x${hex}`);
23+
2324
export class Bsc extends AbstractEthLikeNewCoins {
2425
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
2526
super(bitgo, staticsCoin);

modules/sdk-coin-bsc/src/bscToken.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ import {
1919
getRawDecoded,
2020
VerifyEthTransactionOptions,
2121
} from '@bitgo/abstract-eth';
22-
import { addHexPrefix } from 'ethereumjs-util';
2322
import { TransactionBuilder } from './lib';
2423

24+
const addHexPrefix = (hex: string): string => (hex.startsWith('0x') ? hex : `0x${hex}`);
25+
2526
export { EthLikeTokenConfig };
2627

2728
export class BscToken extends EthLikeToken {

modules/sdk-coin-bsc/test/unit/bsc.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,71 @@ describe('Native BNB', function () {
159159
})
160160
.should.be.rejectedWith('destination address does not match with the recipient address');
161161
});
162+
163+
it('should accept a BEP-20 token transfer using WalletConnect recipients[0].data flow', async function () {
164+
const coin = bitgo.coin('tbsc') as Tbsc;
165+
166+
// txHex sends to recipientAddress; recipients[0].data encodes the same intent
167+
const methodId = EthereumAbi.methodID('transfer', ['address', 'uint256']);
168+
const encodedParams = EthereumAbi.rawEncode(['address', 'uint256'], [recipientAddress, '10000000']);
169+
const erc20TransferData = '0x' + Buffer.concat([methodId, encodedParams]).toString('hex');
170+
171+
const txBuilder = getBuilder('tbsc') as TransactionBuilder;
172+
txBuilder.type(TransactionType.ContractCall);
173+
txBuilder.fee({ fee: '10', gasLimit: '60000' });
174+
txBuilder.counter(1);
175+
txBuilder.contract(tokenContractAddress);
176+
txBuilder.data(erc20TransferData);
177+
const tx = await txBuilder.build();
178+
const txHex = tx.toBroadcastFormat();
179+
180+
const wallet = new Wallet(bitgo, coin, { coinSpecific: { baseAddress: recipientAddress } });
181+
182+
const result = await coin.verifyTssTransaction({
183+
txParams: {
184+
type: 'transfer',
185+
// WalletConnect passes the intended calldata in recipients[0].data
186+
recipients: [{ address: tokenContractAddress, amount: '0', data: erc20TransferData }],
187+
} as any,
188+
txPrebuild: { txHex, coin: 'tbsc', walletId: 'fakeWalletId' } as any,
189+
wallet,
190+
});
191+
result.should.equal(true);
192+
});
193+
194+
it('should reject a BEP-20 token transfer using WalletConnect flow when calldata recipient is tampered', async function () {
195+
const coin = bitgo.coin('tbsc') as Tbsc;
196+
197+
// txHex sends to wrongAddress (tampered)
198+
const methodId = EthereumAbi.methodID('transfer', ['address', 'uint256']);
199+
const tamperedParams = EthereumAbi.rawEncode(['address', 'uint256'], [wrongAddress, '10000000']);
200+
const tamperedData = '0x' + Buffer.concat([methodId, tamperedParams]).toString('hex');
201+
202+
const txBuilder = getBuilder('tbsc') as TransactionBuilder;
203+
txBuilder.type(TransactionType.ContractCall);
204+
txBuilder.fee({ fee: '10', gasLimit: '60000' });
205+
txBuilder.counter(1);
206+
txBuilder.contract(tokenContractAddress);
207+
txBuilder.data(tamperedData);
208+
const tx = await txBuilder.build();
209+
const txHex = tx.toBroadcastFormat();
210+
211+
// recipients[0].data declares the correct recipient (recipientAddress)
212+
const correctParams = EthereumAbi.rawEncode(['address', 'uint256'], [recipientAddress, '10000000']);
213+
const correctData = '0x' + Buffer.concat([methodId, correctParams]).toString('hex');
214+
215+
const wallet = new Wallet(bitgo, coin, { coinSpecific: { baseAddress: recipientAddress } });
216+
217+
await coin
218+
.verifyTssTransaction({
219+
txParams: {
220+
type: 'transfer',
221+
recipients: [{ address: tokenContractAddress, amount: '0', data: correctData }],
222+
} as any,
223+
txPrebuild: { txHex, coin: 'tbsc', walletId: 'fakeWalletId' } as any,
224+
wallet,
225+
})
226+
.should.be.rejectedWith('destination address does not match with the recipient address');
227+
});
162228
});
163229
});

0 commit comments

Comments
 (0)