Skip to content

Commit e9ede8a

Browse files
authored
Merge pull request #8322 from BitGo/BTC-3161/dot-wasm-verify-transaction
feat: use wasm-dot parser in verifyTransaction for tdot
2 parents 9440aa6 + 6060d22 commit e9ede8a

4 files changed

Lines changed: 106 additions & 19 deletions

File tree

modules/sdk-coin-dot/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@bitgo/sdk-core": "^36.35.0",
6060
"@bitgo/sdk-lib-mpc": "^10.9.0",
6161
"@bitgo/statics": "^58.31.0",
62-
"@bitgo/wasm-dot": "^1.5.0",
62+
"@bitgo/wasm-dot": "^1.7.0",
6363
"@polkadot/api": "14.1.1",
6464
"@polkadot/api-augment": "14.1.1",
6565
"@polkadot/keyring": "13.5.6",

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

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
Transaction,
4040
TransactionBuilderFactory,
4141
Utils,
42+
explainDotTransaction,
4243
} from './lib';
4344
import '@polkadot/api-augment';
4445
import { ApiPromise, WsProvider } from '@polkadot/api';
@@ -664,22 +665,37 @@ export class Dot extends BaseCoin {
664665
throw new Error('missing txHex in txPrebuild');
665666
}
666667

667-
const factory = this.getBuilder();
668-
const txBuilder = factory.from(txPrebuild.txHex) as unknown as NativeTransferBuilder;
668+
const chain = this.getChain();
669+
let txTo: string;
670+
let txAmount: string;
671+
let isSweep: boolean;
672+
673+
if (chain === 'tdot') {
674+
const material = Utils.default.getMaterial(coins.get(chain));
675+
const explained = explainDotTransaction({ txHex: txPrebuild.txHex, material });
676+
txTo = explained.outputs[0]?.address ?? '';
677+
txAmount = String(explained.outputs[0]?.amount ?? '0');
678+
isSweep = explained.methodName === 'balances.transferAll';
679+
} else {
680+
const factory = this.getBuilder();
681+
const txBuilder = factory.from(txPrebuild.txHex) as unknown as NativeTransferBuilder;
682+
txTo = txBuilder['_to'];
683+
txAmount = txBuilder['_amount'];
684+
isSweep = txBuilder['_sweepFreeBalance'] === true;
685+
}
669686

670687
if (verification?.consolidationToBaseAddress) {
671-
// Verify funds are sent to wallet's base address for consolidation
672688
const baseAddress = wallet?.coinSpecific()?.rootAddress || wallet?.coinSpecific()?.baseAddress;
673689
if (!baseAddress) {
674690
throw new Error('Unable to determine base address for consolidation');
675691
}
676-
if (txBuilder['_to'] !== baseAddress) {
692+
if (txTo !== baseAddress) {
677693
throw new TxIntentMismatchRecipientError(
678-
`Transaction destination address ${txBuilder['_to']} does not match wallet base address ${baseAddress}`,
694+
`Transaction destination address ${txTo} does not match wallet base address ${baseAddress}`,
679695
reqId,
680696
[txParams],
681697
txPrebuild.txHex,
682-
[{ address: txBuilder['_to'], amount: txBuilder['_amount'] }]
698+
[{ address: txTo, amount: txAmount }]
683699
);
684700
}
685701
}
@@ -695,25 +711,23 @@ export class Dot extends BaseCoin {
695711
);
696712
}
697713

698-
// validate recipient is same as txBuilder['_to']
699-
if (txParams.recipients[0].address !== txBuilder['_to']) {
714+
if (txParams.recipients[0].address !== txTo) {
700715
throw new TxIntentMismatchRecipientError(
701-
`Recipient address ${txParams.recipients[0].address} does not match transaction destination address ${txBuilder['_to']}`,
716+
`Recipient address ${txParams.recipients[0].address} does not match transaction destination address ${txTo}`,
702717
reqId,
703718
[txParams],
704719
txPrebuild.txHex,
705-
[{ address: txBuilder['_to'], amount: txBuilder['_amount'] }]
720+
[{ address: txTo, amount: txAmount }]
706721
);
707722
}
708723

709-
// validate amount is same as txBuilder['_amount']
710-
if (!txBuilder['_sweepFreeBalance'] && txParams.recipients[0].amount !== txBuilder['_amount']) {
724+
if (!isSweep && txParams.recipients[0].amount !== txAmount) {
711725
throw new TxIntentMismatchRecipientError(
712-
`Recipient amount ${txParams.recipients[0].amount} does not match transaction amount ${txBuilder['_amount']}`,
726+
`Recipient amount ${txParams.recipients[0].amount} does not match transaction amount ${txAmount}`,
713727
reqId,
714728
[txParams],
715729
txPrebuild.txHex,
716-
[{ address: txBuilder['_to'], amount: txBuilder['_amount'] }]
730+
[{ address: txTo, amount: txAmount }]
717731
);
718732
}
719733
}

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,79 @@ describe('DOT:', function () {
895895
});
896896
assert.strictEqual(result, true);
897897
});
898+
899+
const wasmTransferKeepAliveHex =
900+
'0xa80a0300161b969b6b53ef81225feea3882284c778cd4a406d23215fcf492e83f75d42960b00204aa9d101eb600400000065900f001000000067f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9a7b7420ee3e4fe2b88da0fc42b30897e18d56d8b56a1934211d9de730cf96de300';
901+
const wasmTransferAllHex =
902+
'0x900a04009f7b0675db59d19b4bd9c8c72eaabba75a9863d02b30115b8b3c3ca5c20f025401d50121030000009d880f001000000067f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d00';
903+
904+
it('should accept a valid WASM-built payment (tdot)', async function () {
905+
const txParams = {
906+
recipients: [{ amount: '2000000000000', address: '5CZh773vKGwKFCYUjGc31AwXCbf7TPkavdeuk2XoujJMjbBD' }],
907+
};
908+
const result = await basecoin.verifyTransaction({ txPrebuild: { txHex: wasmTransferKeepAliveHex }, txParams });
909+
assert.strictEqual(result, true);
910+
});
911+
912+
it('should reject WASM-built payment with wrong amount (tdot)', async function () {
913+
const txParams = {
914+
recipients: [{ amount: '9999', address: '5CZh773vKGwKFCYUjGc31AwXCbf7TPkavdeuk2XoujJMjbBD' }],
915+
};
916+
await basecoin
917+
.verifyTransaction({ txPrebuild: { txHex: wasmTransferKeepAliveHex }, txParams })
918+
.should.be.rejectedWith(/does not match transaction amount/);
919+
});
920+
921+
it('should reject WASM-built payment with wrong recipient (tdot)', async function () {
922+
const txParams = {
923+
recipients: [{ amount: '2000000000000', address: '5DxD9nT16GQLrU6aB5pSS5VtxoZbVju3NHUCcawxZyZCTf74' }],
924+
};
925+
await basecoin
926+
.verifyTransaction({ txPrebuild: { txHex: wasmTransferKeepAliveHex }, txParams })
927+
.should.be.rejectedWith(/does not match transaction destination address/);
928+
});
929+
930+
it('should verify WASM-built transferAll consolidation (tdot)', async function () {
931+
const mockedWallet = {
932+
coinSpecific: () => ({ baseAddress: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq' }),
933+
};
934+
const result = await basecoin.verifyTransaction({
935+
txPrebuild: { txHex: wasmTransferAllHex },
936+
txParams: {},
937+
wallet: mockedWallet as any,
938+
verification: { consolidationToBaseAddress: true },
939+
});
940+
assert.strictEqual(result, true);
941+
});
942+
943+
it('should verify WASM-built transferAll with recipients skips amount check (tdot)', async function () {
944+
const mockedWallet = {
945+
coinSpecific: () => ({ baseAddress: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq' }),
946+
};
947+
const txParams = {
948+
recipients: [{ address: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq', amount: '999999' }],
949+
};
950+
const result = await basecoin.verifyTransaction({
951+
txPrebuild: { txHex: wasmTransferAllHex },
952+
txParams,
953+
wallet: mockedWallet as any,
954+
verification: { consolidationToBaseAddress: true },
955+
});
956+
assert.strictEqual(result, true);
957+
});
958+
959+
it('should decode wasm-built signing payload with legacy TransactionBuilderFactory', async function () {
960+
const { TransactionBuilderFactory } = await import('../../src/lib');
961+
const factory = new TransactionBuilderFactory(coins.get('tdot'));
962+
const txBuilder = factory.from(wasmTransferKeepAliveHex);
963+
txBuilder.sender({ address: '5EGoFA95omzemRssELLDjVenNZ68aXyUeqtKQScXSEBvVJkr' });
964+
txBuilder.validity({ firstValid: 1000 });
965+
txBuilder.referenceBlock('0x149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d');
966+
const tx = await txBuilder.build();
967+
const json = tx.toJson();
968+
assert.strictEqual((json as any).to, '5CZh773vKGwKFCYUjGc31AwXCbf7TPkavdeuk2XoujJMjbBD');
969+
assert.strictEqual((json as any).amount, '2000000000000');
970+
});
898971
});
899972

900973
describe('isWalletAddress', () => {

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -985,10 +985,10 @@
985985
monocle-ts "^2.3.13"
986986
newtype-ts "^0.3.5"
987987

988-
"@bitgo/wasm-dot@^1.5.0":
989-
version "1.6.0"
990-
resolved "https://registry.npmjs.org/@bitgo/wasm-dot/-/wasm-dot-1.6.0.tgz#4a0a3e1447e1ee112d11f01399645c8e2b5d573c"
991-
integrity sha512-YYvvmMz4OQRLq0OYdGIkNn9L32Uzi7cHaGBfuxtvVEf14w8VlA/gU/UdC6dat+oitVB4jHEyM54rltqNoBkKsA==
988+
"@bitgo/wasm-dot@^1.7.0":
989+
version "1.7.0"
990+
resolved "https://registry.npmjs.org/@bitgo/wasm-dot/-/wasm-dot-1.7.0.tgz#d22aafea9d38ebcb4b75d38538202237eadac685"
991+
integrity sha512-KoXavJvyDHlEN+sWcigbgxYJtdFaU7gS0EkYQbNH4npVjNlzo6rL6gwjyWbyOy7oEs65DhpJ9vY5kRbE/bKiTQ==
992992

993993
"@bitgo/wasm-solana@^2.6.0":
994994
version "2.6.0"

0 commit comments

Comments
 (0)