Skip to content

Commit 9573f45

Browse files
committed
feat: add WASM-based DOT transaction parsing and explanation
Add @bitgo/wasm-dot integration for parsing, explaining, and building DOT transactions using Rust/WASM instead of @polkadot/api. - wasmParser.ts: adapter that maps wasm-dot's explainTransaction() output to BitGoJS TransactionExplanation format (type mapping, output/input extraction, fee handling) - wasmBuilderByteComparison.ts: tests verifying WASM-built transactions produce byte-identical signing payloads to legacy txwrapper-polkadot - Covers: transfers, staking (bond/unbond/withdraw/chill), proxy (add/remove), and batch transactions BTC-0 TICKET: BTC-0
1 parent 79aad00 commit 9573f45

File tree

6 files changed

+4738
-2165
lines changed

6 files changed

+4738
-2165
lines changed
428 KB
Binary file not shown.

modules/sdk-coin-dot/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@bitgo/sdk-core": "^36.30.0",
4444
"@bitgo/sdk-lib-mpc": "^10.9.0",
4545
"@bitgo/statics": "^58.24.0",
46+
"@bitgo/wasm-dot": "file:bitgo-wasm-dot-0.0.1.tgz",
4647
"@polkadot/api": "14.1.1",
4748
"@polkadot/api-augment": "14.1.1",
4849
"@polkadot/keyring": "13.3.1",

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ export { TransactionBuilderFactory } from './transactionBuilderFactory';
1616
export { SingletonRegistry } from './singletonRegistry';
1717
export { NativeTransferBuilder } from './nativeTransferBuilder';
1818
export { RemoveProxyBuilder } from './proxyBuilder';
19+
export { explainTransactionFromHex } from './wasmParser';
20+
export type { DotWasmExplanation, DotInput } from './wasmParser';
1921
export { Interface, Utils };
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { TransactionType } from '@bitgo/sdk-core';
2+
import {
3+
explainTransaction,
4+
TransactionType as WasmTransactionType,
5+
type ExplainedTransaction as WasmExplainedTransaction,
6+
} from '@bitgo/wasm-dot';
7+
import type { TransactionExplanation, Material } from './iface';
8+
9+
/**
10+
* Input entry for a DOT transaction.
11+
* For account-model chains, there's typically one input (the sender).
12+
*/
13+
export interface DotInput {
14+
address: string;
15+
value: number;
16+
valueString: string;
17+
}
18+
19+
/**
20+
* Extended explanation returned by WASM-based parsing.
21+
* Includes fields needed by wallet-platform that aren't in the base TransactionExplanation.
22+
*/
23+
export interface DotWasmExplanation extends TransactionExplanation {
24+
sender: string;
25+
nonce: number;
26+
isSigned: boolean;
27+
methodName: string;
28+
inputs: DotInput[];
29+
}
30+
31+
/** Map WASM TransactionType to sdk-core TransactionType */
32+
function mapTransactionType(wasmType: WasmTransactionType): TransactionType {
33+
switch (wasmType) {
34+
case WasmTransactionType.Send:
35+
return TransactionType.Send;
36+
case WasmTransactionType.StakingActivate:
37+
return TransactionType.StakingActivate;
38+
case WasmTransactionType.StakingUnlock:
39+
return TransactionType.StakingUnlock;
40+
case WasmTransactionType.StakingWithdraw:
41+
return TransactionType.StakingWithdraw;
42+
case WasmTransactionType.StakingUnvote:
43+
return TransactionType.StakingUnvote;
44+
case WasmTransactionType.StakingClaim:
45+
return TransactionType.StakingClaim;
46+
case WasmTransactionType.AddressInitialization:
47+
return TransactionType.AddressInitialization;
48+
case WasmTransactionType.Batch:
49+
return TransactionType.Batch;
50+
default:
51+
return TransactionType.Send;
52+
}
53+
}
54+
55+
/**
56+
* Explain a DOT transaction from raw hex using the WASM parser.
57+
*
58+
* This is the single source of truth for parsing DOT transactions from bytes.
59+
* Uses explainTransaction() which handles type derivation, output/input extraction,
60+
* and recursive batch/proxy unwrapping.
61+
*
62+
* @param txHex - Hex-encoded extrinsic bytes
63+
* @param material - Chain metadata needed for decoding
64+
* @param senderAddress - Optional sender address fallback (for unsigned txs)
65+
* @returns Parsed and explained transaction
66+
*/
67+
export function explainTransactionFromHex(
68+
txHex: string,
69+
material: Material,
70+
senderAddress?: string
71+
): DotWasmExplanation {
72+
const explained: WasmExplainedTransaction = explainTransaction(txHex, {
73+
context: { material, sender: senderAddress },
74+
});
75+
76+
const sender = explained.sender || senderAddress || '';
77+
const type = mapTransactionType(explained.type);
78+
const methodName = `${explained.method.pallet}.${explained.method.name}`;
79+
80+
// Map WASM outputs to BitGoJS format
81+
const outputs = explained.outputs.map((o) => ({
82+
address: o.address,
83+
amount: o.amount === 'ALL' ? '0' : o.amount,
84+
}));
85+
86+
// Map WASM inputs to BitGoJS format (with numeric value for legacy compat)
87+
const inputs: DotInput[] = explained.inputs.map((i) => {
88+
const value = i.value === 'ALL' ? 0 : parseInt(i.value || '0', 10);
89+
return { address: i.address, value, valueString: i.value };
90+
});
91+
92+
return {
93+
displayOrder: ['outputAmount', 'changeAmount', 'outputs', 'changeOutputs', 'fee', 'type', 'sequenceId', 'id'],
94+
id: explained.id || '',
95+
outputs,
96+
outputAmount: explained.outputAmount,
97+
changeOutputs: [],
98+
changeAmount: '0',
99+
fee: { fee: explained.tip || '0', type: 'tip' },
100+
type,
101+
sender,
102+
nonce: explained.nonce,
103+
isSigned: explained.isSigned,
104+
methodName,
105+
inputs,
106+
};
107+
}

0 commit comments

Comments
 (0)