Skip to content

Commit 6a38723

Browse files
Merge pull request #9171 from BitGo/CHALO-726-test
test(sdk-coin-eth): add tests to check txinfo recipients
2 parents 17edfd8 + bf81b44 commit 6a38723

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

modules/sdk-coin-eth/test/unit/erc7984Token.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,4 +1070,42 @@ describe('verifyTransaction – confidential consolidation (FlushERC7984Forwarde
10701070
})
10711071
.should.be.rejectedWith(/parent address mismatch/);
10721072
});
1073+
1074+
it('should verify multisig consolidation using forwarder address from txPrebuild.txInfo.recipients', async function () {
1075+
const txHex = await buildMultisigConsolidationTxHex();
1076+
const wallet = new Wallet(bitgo, coin, {
1077+
coinSpecific: { baseAddress: CONSOLIDATION_BASE_ADDRESS },
1078+
});
1079+
1080+
// No top-level recipients; forwarder comes from txInfo.recipients
1081+
const result = await coin.verifyTransaction({
1082+
txParams: { type: 'consolidate' } as any,
1083+
txPrebuild: {
1084+
consolidateId: '6a44ebc0326e1be45c1d797542c8c634',
1085+
txHex,
1086+
txInfo: { recipients: [{ address: CONSOLIDATION_FORWARDER, amount: '0' }] },
1087+
} as any,
1088+
wallet,
1089+
});
1090+
result.should.equal(true);
1091+
});
1092+
1093+
it('should reject multisig consolidation when txInfo.recipients forwarder address does not match tx forwarder', async function () {
1094+
const txHex = await buildMultisigConsolidationTxHex();
1095+
const wallet = new Wallet(bitgo, coin, {
1096+
coinSpecific: { baseAddress: CONSOLIDATION_BASE_ADDRESS },
1097+
});
1098+
1099+
await coin
1100+
.verifyTransaction({
1101+
txParams: { type: 'consolidate' } as any,
1102+
txPrebuild: {
1103+
consolidateId: 'abc123',
1104+
txHex,
1105+
txInfo: { recipients: [{ address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', amount: '0' }] },
1106+
} as any,
1107+
wallet,
1108+
})
1109+
.should.be.rejectedWith(/forwarder address mismatch/);
1110+
});
10731111
});

modules/sdk-coin-eth/test/unit/ethWallet.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,104 @@ describe('Sign ETH Transaction', async function () {
9494
});
9595
});
9696

97+
describe('Sign ETH Transaction – txInfo fallback', async function () {
98+
let bitgo: TestBitGoAPI;
99+
let ethWallet;
100+
const recipients = [{ address: '0xe59dfe5c67114b39a5662cc856be536c614124c0', amount: '100000' }];
101+
102+
before(function () {
103+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
104+
bitgo.initializeTestVars();
105+
bitgo.safeRegister('teth', Teth.createInstance);
106+
const coin = bitgo.coin('teth');
107+
ethWallet = coin.newWalletObject({});
108+
});
109+
110+
afterEach(function () {
111+
sinon.restore();
112+
});
113+
114+
it('should read recipients from txPrebuild.txInfo.recipients when txPrebuild.recipients is absent', async function () {
115+
sinon.stub(Util, 'xprvToEthPrivateKey');
116+
sinon.stub(Util, 'ethSignMsgHash');
117+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
118+
119+
const txPrebuild = { txInfo: { recipients, nextContractSequenceId: 1 } };
120+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
121+
halfSigned.should.have.property('recipients', recipients);
122+
});
123+
124+
it('should prefer txPrebuild.recipients over txPrebuild.txInfo.recipients', async function () {
125+
sinon.stub(Util, 'xprvToEthPrivateKey');
126+
sinon.stub(Util, 'ethSignMsgHash');
127+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
128+
129+
const txInfoRecipients = [{ address: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', amount: '1' }];
130+
const txPrebuild = { recipients, nextContractSequenceId: 0, txInfo: { recipients: txInfoRecipients } };
131+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
132+
halfSigned.should.have.property('recipients', recipients);
133+
});
134+
135+
it('should use txPrebuild.txInfo.nextContractSequenceId when nextContractSequenceId is absent', async function () {
136+
sinon.stub(Util, 'xprvToEthPrivateKey');
137+
sinon.stub(Util, 'ethSignMsgHash');
138+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
139+
140+
const txPrebuild = { recipients, txInfo: { nextContractSequenceId: 5 } };
141+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
142+
halfSigned.should.have.property('contractSequenceId', 5);
143+
});
144+
145+
it('should throw when nextContractSequenceId is absent from both txPrebuild and txInfo', async function () {
146+
await ethWallet
147+
.signTransaction({ txPrebuild: { recipients }, prv: 'my_user_prv' })
148+
.should.be.rejectedWith('transaction prebuild missing required property nextContractSequenceId');
149+
});
150+
151+
it('should use txPrebuild.txInfo.eip1559 when txPrebuild.eip1559 is absent', async function () {
152+
sinon.stub(Util, 'xprvToEthPrivateKey');
153+
sinon.stub(Util, 'ethSignMsgHash');
154+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
155+
156+
const eip1559 = { maxFeePerGas: '100', maxPriorityFeePerGas: '10' };
157+
const txPrebuild = { recipients, nextContractSequenceId: 0, txInfo: { eip1559 } };
158+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
159+
halfSigned.should.have.property('eip1559', eip1559);
160+
});
161+
162+
it('should prefer txPrebuild.eip1559 over txPrebuild.txInfo.eip1559', async function () {
163+
sinon.stub(Util, 'xprvToEthPrivateKey');
164+
sinon.stub(Util, 'ethSignMsgHash');
165+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
166+
167+
const eip1559 = { maxFeePerGas: '200', maxPriorityFeePerGas: '20' };
168+
const txInfoEip1559 = { maxFeePerGas: '999', maxPriorityFeePerGas: '99' };
169+
const txPrebuild = { recipients, nextContractSequenceId: 0, eip1559, txInfo: { eip1559: txInfoEip1559 } };
170+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
171+
halfSigned.should.have.property('eip1559', eip1559);
172+
});
173+
174+
it('should use txPrebuild.txInfo.isBatch when txPrebuild.isBatch is absent', async function () {
175+
sinon.stub(Util, 'xprvToEthPrivateKey');
176+
sinon.stub(Util, 'ethSignMsgHash');
177+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
178+
179+
const txPrebuild = { recipients, nextContractSequenceId: 0, txInfo: { isBatch: true } };
180+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
181+
halfSigned.should.have.property('isBatch', true);
182+
});
183+
184+
it('should prefer txPrebuild.isBatch over txPrebuild.txInfo.isBatch', async function () {
185+
sinon.stub(Util, 'xprvToEthPrivateKey');
186+
sinon.stub(Util, 'ethSignMsgHash');
187+
sinon.stub(ethWallet.getOperationSha3ForExecuteAndConfirm);
188+
189+
const txPrebuild = { recipients, nextContractSequenceId: 0, isBatch: false, txInfo: { isBatch: true } };
190+
const { halfSigned } = (await ethWallet.signTransaction({ txPrebuild, prv: 'my_user_prv' })) as any;
191+
halfSigned.should.have.property('isBatch', false);
192+
});
193+
});
194+
97195
describe('Ethereum Hop Transactions', function () {
98196
let bitgo: TestBitGoAPI;
99197
let ethWallet;

0 commit comments

Comments
 (0)