Skip to content

Commit 4d6f451

Browse files
committed
feat(sdk-coin-polyx): add decodeTransaction in utils
Ticket: WIN-7228
1 parent 8c1d4e6 commit 4d6f451

File tree

2 files changed

+201
-1
lines changed

2 files changed

+201
-1
lines changed

modules/abstract-substrate/src/lib/utils.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { hexToU8a, isHex, u8aToHex, u8aToU8a } from '@polkadot/util';
88
import { base64Decode, signatureVerify } from '@polkadot/util-crypto';
99
import { UnsignedTransaction } from '@substrate/txwrapper-core';
1010
import { DecodedSignedTx, DecodedSigningPayload, TypeRegistry } from '@substrate/txwrapper-core/lib/types';
11-
import { construct } from '@substrate/txwrapper-polkadot';
11+
import { construct, decode } from '@substrate/txwrapper-polkadot';
1212
import bs58 from 'bs58';
1313
import base32 from 'hi-base32';
1414
import nacl from 'tweetnacl';
@@ -30,6 +30,7 @@ import {
3030
BatchArgs,
3131
MoveStakeArgs,
3232
} from './iface';
33+
import { SingletonRegistry } from './singletonRegistry';
3334

3435
export class Utils implements BaseUtils {
3536
/** @inheritdoc */
@@ -317,6 +318,31 @@ export class Utils implements BaseUtils {
317318
getMaterial(networkType: NetworkType): Material {
318319
throw new Error('Method not implemented.');
319320
}
321+
322+
/**
323+
* Decodes a substrate transaction from raw transaction hex
324+
*
325+
* @param {string} txHex - The raw transaction hex string to decode (signed or unsigned)
326+
* @param {Material} material - Network material containing metadata and chain information
327+
* @param {boolean} [isImmortalEra] - Whether the transaction uses immortal era (optional)
328+
* @returns {DecodedSignedTx | DecodedSigningPayload} The decoded transaction object
329+
*/
330+
decodeTransaction(txHex: string, material: Material, isImmortalEra = false): DecodedSignedTx | DecodedSigningPayload {
331+
try {
332+
const registry = SingletonRegistry.getInstance(material);
333+
334+
// Attempt to decode as a signed transaction or unsigned transaction
335+
const decoded = decode(txHex, {
336+
metadataRpc: material.metadata,
337+
registry,
338+
isImmortalEra,
339+
});
340+
341+
return decoded;
342+
} catch (error) {
343+
throw new Error(`Failed to decode transaction: ${error}`);
344+
}
345+
}
320346
}
321347

322348
const utils = new Utils();
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import should from 'should';
2+
import { utils } from '../../src';
3+
import { rawTx, stakingTx } from '../resources';
4+
import { testnetMaterial } from '../../src/resources';
5+
import { Interface } from '@bitgo/abstract-substrate';
6+
7+
type Material = Interface.Material;
8+
9+
describe('Polyx Utils', () => {
10+
// Create a proper Material object from testnetMaterial
11+
const material: Material = {
12+
genesisHash: testnetMaterial.genesisHash,
13+
chainName: testnetMaterial.chainName,
14+
specName: testnetMaterial.specName,
15+
specVersion: testnetMaterial.specVersion,
16+
txVersion: testnetMaterial.txVersion,
17+
metadata: testnetMaterial.metadata as `0x${string}`,
18+
};
19+
20+
describe('decodeTransaction', () => {
21+
it('should decode a signed transfer transaction and identify method name', () => {
22+
const decoded = utils.decodeTransaction(rawTx.transfer.signed, material);
23+
24+
should.exist(decoded);
25+
decoded.should.have.property('method');
26+
decoded.method.should.have.property('pallet');
27+
decoded.method.should.have.property('name');
28+
29+
// Check that it's a balances transfer
30+
decoded.method.pallet.should.equal('balances');
31+
decoded.method.name.should.equal('transferWithMemo');
32+
});
33+
34+
it('should decode an unsigned transfer transaction and identify method name', () => {
35+
const decoded = utils.decodeTransaction(rawTx.transfer.unsigned, material);
36+
37+
should.exist(decoded);
38+
decoded.should.have.property('method');
39+
decoded.method.should.have.property('pallet');
40+
decoded.method.should.have.property('name');
41+
42+
// Check that it's a balances transfer
43+
decoded.method.pallet.should.equal('balances');
44+
decoded.method.name.should.equal('transferWithMemo');
45+
});
46+
47+
it('should decode a CDD registration transaction and identify method name', () => {
48+
const decoded = utils.decodeTransaction(rawTx.cddTransaction.signed, material);
49+
50+
should.exist(decoded);
51+
decoded.should.have.property('method');
52+
decoded.method.should.have.property('pallet');
53+
decoded.method.should.have.property('name');
54+
55+
// Check that it's a CDD transaction
56+
decoded.method.pallet.should.equal('identity');
57+
decoded.method.name.should.equal('cddRegisterDidWithCdd');
58+
});
59+
60+
it('should decode a staking bond transaction and identify method name', () => {
61+
const decoded = utils.decodeTransaction(stakingTx.bond.signed, material);
62+
63+
should.exist(decoded);
64+
decoded.should.have.property('method');
65+
decoded.method.should.have.property('pallet');
66+
decoded.method.should.have.property('name');
67+
68+
// Check that it's a staking bond
69+
decoded.method.pallet.should.equal('staking');
70+
decoded.method.name.should.equal('bond');
71+
});
72+
73+
it('should decode a staking unbond transaction and identify method name', () => {
74+
const decoded = utils.decodeTransaction(stakingTx.unbond.signed, material);
75+
76+
should.exist(decoded);
77+
decoded.should.have.property('method');
78+
decoded.method.should.have.property('pallet');
79+
decoded.method.should.have.property('name');
80+
81+
// Check that it's a staking unbond
82+
decoded.method.pallet.should.equal('staking');
83+
decoded.method.name.should.equal('unbond');
84+
});
85+
86+
it('should decode a batch transaction and identify method name', () => {
87+
const decoded = utils.decodeTransaction(stakingTx.batch.bondAndNominate.signed, material);
88+
89+
should.exist(decoded);
90+
decoded.should.have.property('method');
91+
decoded.method.should.have.property('pallet');
92+
decoded.method.should.have.property('name');
93+
94+
// Check that it's a batch transaction
95+
decoded.method.pallet.should.equal('utility');
96+
decoded.method.name.should.equal('batchAll');
97+
98+
// Check the batch calls (handle potential null/undefined)
99+
decoded.method.should.have.property('args');
100+
decoded.method.args.should.have.property('calls');
101+
if (Array.isArray(decoded.method.args.calls)) {
102+
decoded.method.args.calls.should.be.an.Array();
103+
decoded.method.args.calls.length.should.equal(2);
104+
105+
// First call should be bond
106+
if (decoded.method.args.calls[0]) {
107+
decoded.method.args.calls[0].should.have.property('callIndex');
108+
}
109+
// Second call should be nominate
110+
if (decoded.method.args.calls[1]) {
111+
decoded.method.args.calls[1].should.have.property('callIndex');
112+
}
113+
}
114+
});
115+
116+
it('should decode a preApprove asset transaction and identify method name', () => {
117+
const decoded = utils.decodeTransaction(rawTx.preApproveAsset.signed, material);
118+
119+
should.exist(decoded);
120+
decoded.should.have.property('method');
121+
decoded.method.should.have.property('pallet');
122+
decoded.method.should.have.property('name');
123+
124+
// Check that it's an asset preApprove
125+
decoded.method.pallet.should.equal('asset');
126+
decoded.method.name.should.equal('preApproveAsset');
127+
});
128+
129+
it('should decode a token transfer transaction and identify method name', () => {
130+
const decoded = utils.decodeTransaction(rawTx.tokenTransfer.signed, material);
131+
132+
should.exist(decoded);
133+
decoded.should.have.property('method');
134+
decoded.method.should.have.property('pallet');
135+
decoded.method.should.have.property('name');
136+
137+
// Check that it's a settlement transaction
138+
decoded.method.pallet.should.equal('settlement');
139+
decoded.method.name.should.equal('addAndAffirmWithMediators');
140+
});
141+
142+
it('should decode unstake transaction and identify method name', () => {
143+
const decoded = utils.decodeTransaction(rawTx.unstake.signed, material);
144+
145+
should.exist(decoded);
146+
decoded.should.have.property('method');
147+
decoded.method.should.have.property('pallet');
148+
decoded.method.should.have.property('name');
149+
150+
// Check that it's a batch unstaking transaction
151+
decoded.method.pallet.should.equal('utility');
152+
decoded.method.name.should.equal('batchAll');
153+
});
154+
155+
it('should decode withdraw unbonded transaction and identify method name', () => {
156+
const decoded = utils.decodeTransaction(rawTx.withdrawUnbonded.signed, material);
157+
158+
should.exist(decoded);
159+
decoded.should.have.property('method');
160+
decoded.method.should.have.property('pallet');
161+
decoded.method.should.have.property('name');
162+
163+
// Check that it's a staking withdraw unbonded
164+
decoded.method.pallet.should.equal('staking');
165+
decoded.method.name.should.equal('withdrawUnbonded');
166+
});
167+
168+
it('should throw error for invalid transaction hex', () => {
169+
should.throws(() => {
170+
utils.decodeTransaction('0xinvalid', material);
171+
}, /Failed to decode transaction/);
172+
});
173+
});
174+
});

0 commit comments

Comments
 (0)