Skip to content

Commit 2b45af1

Browse files
Merge pull request #5887 from BitGo/COIN-3422-erc20-sendmany
feat: enable erc20 sendmany
2 parents 3391628 + a412a7c commit 2b45af1

File tree

3 files changed

+117
-8
lines changed

3 files changed

+117
-8
lines changed

modules/abstract-eth/src/abstractEthLikeNewCoins.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ interface EthTransactionParams extends TransactionParams {
338338
hopParams?: HopParams;
339339
hop?: boolean;
340340
prebuildTx?: PrebuildTransactionResult;
341+
tokenName?: string;
341342
}
342343

343344
interface VerifyEthTransactionOptions extends VerifyTransactionOptions {
@@ -2598,14 +2599,21 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
25982599
await this.validateHopPrebuild(wallet, txPrebuild.hopTransaction, { recipients });
25992600
} else if (txParams.recipients.length > 1) {
26002601
// Check total amount for batch transaction
2601-
let expectedTotalAmount = new BigNumber(0);
2602-
for (let i = 0; i < txParams.recipients.length; i++) {
2603-
expectedTotalAmount = expectedTotalAmount.plus(txParams.recipients[i].amount);
2604-
}
2605-
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2606-
throw new Error(
2607-
'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client'
2608-
);
2602+
if (txParams.tokenName) {
2603+
const expectedTotalAmount = new BigNumber(0);
2604+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2605+
throw new Error('batch token transaction amount in txPrebuild should be zero for token transfers');
2606+
}
2607+
} else {
2608+
let expectedTotalAmount = new BigNumber(0);
2609+
for (let i = 0; i < txParams.recipients.length; i++) {
2610+
expectedTotalAmount = expectedTotalAmount.plus(txParams.recipients[i].amount);
2611+
}
2612+
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2613+
throw new Error(
2614+
'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client'
2615+
);
2616+
}
26092617
}
26102618

26112619
// Check batch transaction is sent to the batcher contract address for the chain

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

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { OfflineVaultTxInfo, optionalDeps, SignTransactionOptions } from '@bitgo
1010
import { Opeth, Topeth, TransactionBuilder, TransferBuilder } from '../../src/index';
1111
import * as mockData from '../fixtures/opeth';
1212
import { getBuilder } from '../getBuilder';
13+
import { EthereumNetwork } from '@bitgo/statics';
1314

1415
nock.enableNetConnect();
1516

@@ -578,6 +579,105 @@ describe('Optimism', function () {
578579
.verifyTransaction({ txParams, txPrebuild, wallet, verification })
579580
.should.be.rejectedWith('coin in txPrebuild did not match that in txParams supplied by client');
580581
});
582+
583+
it('should verify a txPrebuild with more than one recipient in case of token batch transfer', async function () {
584+
const wallet = new Wallet(bitgo, basecoin, {});
585+
586+
const txParams = {
587+
tokenName: 'topeth:terc18dp',
588+
recipients: [
589+
{ amount: '1', address: address1 },
590+
{ amount: '2', address: address2 },
591+
{ amount: '3', address: address2 },
592+
],
593+
wallet: wallet,
594+
walletPassphrase: 'fakeWalletPassphrase',
595+
};
596+
597+
const txPrebuild = {
598+
recipients: [
599+
{ amount: '0', address: (basecoin?.staticsCoin?.network as EthereumNetwork).batcherContractAddress },
600+
],
601+
nextContractSequenceId: 0,
602+
gasPrice: 20000000000,
603+
gasLimit: 500000,
604+
isBatch: true,
605+
coin: 'topeth',
606+
walletId: 'fakeWalletId',
607+
walletContractAddress: 'fakeWalletContractAddress',
608+
};
609+
610+
const verification = {};
611+
612+
const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, wallet, verification });
613+
isTransactionVerified.should.equal(true);
614+
});
615+
616+
it('should reject a txPrebuild with more than one recipient in case of token batch transfer with wrong amount', async function () {
617+
const wallet = new Wallet(bitgo, basecoin, {});
618+
619+
const txParams = {
620+
tokenName: 'topeth:terc18dp',
621+
recipients: [
622+
{ amount: '1', address: address1 },
623+
{ amount: '2', address: address2 },
624+
{ amount: '3', address: address2 },
625+
],
626+
wallet: wallet,
627+
walletPassphrase: 'fakeWalletPassphrase',
628+
};
629+
630+
const txPrebuild = {
631+
recipients: [
632+
{ amount: '6', address: (basecoin?.staticsCoin?.network as EthereumNetwork).batcherContractAddress },
633+
],
634+
nextContractSequenceId: 0,
635+
gasPrice: 20000000000,
636+
gasLimit: 500000,
637+
isBatch: true,
638+
coin: 'topeth',
639+
walletId: 'fakeWalletId',
640+
walletContractAddress: 'fakeWalletContractAddress',
641+
};
642+
643+
const verification = {};
644+
645+
await basecoin
646+
.verifyTransaction({ txParams, txPrebuild, wallet, verification })
647+
.should.be.rejectedWith(`batch token transaction amount in txPrebuild should be zero for token transfers`);
648+
});
649+
650+
it('should reject a txPrebuild with more than one recipient in case of token batch transfer with wrong batcher contract address', async function () {
651+
const wallet = new Wallet(bitgo, basecoin, {});
652+
653+
const txParams = {
654+
tokenName: 'topeth:terc18dp',
655+
recipients: [
656+
{ amount: '1', address: address1 },
657+
{ amount: '2', address: address2 },
658+
{ amount: '3', address: address2 },
659+
],
660+
wallet: wallet,
661+
walletPassphrase: 'fakeWalletPassphrase',
662+
};
663+
664+
const txPrebuild = {
665+
recipients: [{ amount: '0', address: 'fakeContractAddress' }],
666+
nextContractSequenceId: 0,
667+
gasPrice: 20000000000,
668+
gasLimit: 500000,
669+
isBatch: true,
670+
coin: 'topeth',
671+
walletId: 'fakeWalletId',
672+
walletContractAddress: 'fakeWalletContractAddress',
673+
};
674+
675+
const verification = {};
676+
677+
await basecoin
678+
.verifyTransaction({ txParams, txPrebuild, wallet, verification })
679+
.should.be.rejectedWith(`recipient address of txPrebuild does not match batcher address`);
680+
});
581681
});
582682

583683
describe('Recover transaction:', function () {

modules/statics/src/networks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,7 @@ class OptimismTestnet extends Testnet implements EthereumNetwork {
10781078
forwarderImplementationAddress = '0xd5fe1c1f216b775dfd30638fa7164d41321ef79b';
10791079
walletFactoryAddress = '0x809ee567e413543af1caebcdb247f6a67eafc8dd';
10801080
walletImplementationAddress = '0x944fef03af368414f29dc31a72061b8d64f568d2';
1081+
batcherContractAddress = '0xc84c7eb4c84271ec03ca9e3dbf12cfa42146309e';
10811082
}
10821083

10831084
class ZkSync extends Mainnet implements EthereumNetwork {

0 commit comments

Comments
 (0)