Skip to content

Commit b55c773

Browse files
Merge pull request #6721 from BitGo/WIN-6761
fix(sdk-coin-tao): fix toJson & explainTransaction for token transfers
2 parents f596947 + 34a8175 commit b55c773

File tree

6 files changed

+142
-14
lines changed

6 files changed

+142
-14
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Interface } from '@bitgo/abstract-substrate';
2+
3+
export interface TransferStakeTxData extends Interface.TxData {
4+
destinationColdkey: string;
5+
hotkey: string;
6+
originNetuid: string;
7+
destinationNetuid: string;
8+
alphaAmount: string;
9+
}

modules/sdk-coin-tao/src/lib/tokenTransferBuilder.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { Interface, Schema, Transaction, TransactionBuilder } from '@bitgo/abstract-substrate';
2+
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
13
import { BaseCoin as CoinConfig } from '@bitgo/statics';
2-
import { defineMethod, UnsignedTransaction, DecodedSignedTx, DecodedSigningPayload } from '@substrate/txwrapper-core';
4+
import { DecodedSignedTx, DecodedSigningPayload, defineMethod, UnsignedTransaction } from '@substrate/txwrapper-core';
35
import BigNumber from 'bignumber.js';
4-
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
5-
import { TransactionBuilder, Interface, Schema, Transaction } from '@bitgo/abstract-substrate';
6+
import { TokenTransferTransaction } from './tokenTransferTransaction';
67

78
export class TokenTransferBuilder extends TransactionBuilder {
89
protected _destinationColdkey: string;
@@ -13,6 +14,7 @@ export class TokenTransferBuilder extends TransactionBuilder {
1314

1415
constructor(_coinConfig: Readonly<CoinConfig>) {
1516
super(_coinConfig);
17+
this._transaction = new TokenTransferTransaction(_coinConfig);
1618
}
1719

1820
/**
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Interface as SubstrateInterface, Transaction as SubstrateTransaction } from '@bitgo/abstract-substrate';
2+
import { InvalidTransactionError, TransactionRecipient } from '@bitgo/sdk-core';
3+
import { decode } from '@substrate/txwrapper-polkadot';
4+
import { TransferStakeTxData } from './iface';
5+
import utils from './utils';
6+
7+
export class TokenTransferTransaction extends SubstrateTransaction {
8+
/** @inheritdoc */
9+
toJson(): SubstrateInterface.TxData {
10+
if (!this._substrateTransaction) {
11+
throw new InvalidTransactionError('Empty transaction');
12+
}
13+
14+
const decodedTx = decode(this._substrateTransaction, {
15+
metadataRpc: this._substrateTransaction.metadataRpc,
16+
registry: this._registry,
17+
isImmortalEra: utils.isZeroHex(this._substrateTransaction.era),
18+
}) as unknown as SubstrateInterface.DecodedTx;
19+
const txMethod = decodedTx.method.args as SubstrateInterface.TransferStakeArgs;
20+
21+
const result = super.toJson() as TransferStakeTxData;
22+
result.destinationColdkey = txMethod.destinationColdkey;
23+
result.hotkey = txMethod.hotkey;
24+
result.originNetuid = txMethod.originNetuid;
25+
result.destinationNetuid = txMethod.destinationNetuid;
26+
result.alphaAmount = txMethod.alphaAmount;
27+
28+
return result;
29+
}
30+
31+
/** @inheritdoc */
32+
loadInputsAndOutputs(): void {
33+
super.loadInputsAndOutputs();
34+
35+
const decodedTx = decode(this._substrateTransaction, {
36+
metadataRpc: this._substrateTransaction.metadataRpc,
37+
registry: this._registry,
38+
isImmortalEra: utils.isZeroHex(this._substrateTransaction.era),
39+
}) as unknown as SubstrateInterface.DecodedTx;
40+
const txMethod = decodedTx.method.args as SubstrateInterface.TransferStakeArgs;
41+
42+
this._inputs.push({
43+
address: this._sender,
44+
value: txMethod.alphaAmount,
45+
coin: utils.getTaoTokenBySubnetId(txMethod.originNetuid).name,
46+
});
47+
48+
this._outputs.push({
49+
address: txMethod.destinationColdkey,
50+
value: txMethod.alphaAmount,
51+
coin: utils.getTaoTokenBySubnetId(txMethod.destinationNetuid).name,
52+
});
53+
}
54+
55+
/** @inheritdoc */
56+
explainTransaction(): SubstrateInterface.TransactionExplanation {
57+
const result = this.toJson();
58+
const outputs: TransactionRecipient[] = this._outputs.map((output) => {
59+
return {
60+
address: output.address,
61+
amount: output.value,
62+
tokenName: output.coin,
63+
};
64+
});
65+
66+
const explanationResult: SubstrateInterface.TransactionExplanation = {
67+
id: result.id,
68+
outputAmount: result.amount?.toString() || '0',
69+
changeAmount: '0',
70+
changeOutputs: [],
71+
outputs,
72+
fee: {
73+
fee: result.tip?.toString() || '',
74+
type: 'tip',
75+
},
76+
type: this.type,
77+
};
78+
return explanationResult;
79+
}
80+
}

modules/sdk-coin-tao/src/lib/utils.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
import { Utils as SubstrateUtils, Interface } from '@bitgo/abstract-substrate';
2-
import { NetworkType } from '@bitgo/statics';
1+
import { Interface, Utils as SubstrateUtils } from '@bitgo/abstract-substrate';
2+
import { BaseCoin as CoinConfig, coins, NetworkType, TaoCoin } from '@bitgo/statics';
3+
import assert from 'assert';
34
import { mainnetMaterial, testnetMaterial } from '../resources';
45

56
export class Utils extends SubstrateUtils {
67
getMaterial(networkType: NetworkType): Interface.Material {
78
return (networkType === NetworkType.MAINNET ? mainnetMaterial : testnetMaterial) as unknown as Interface.Material;
89
}
10+
11+
getTaoTokenBySubnetId(subnetId: string): Readonly<CoinConfig> {
12+
const tokens = coins.filter((coin) => coin instanceof TaoCoin && coin.subnetId === subnetId).map((coin) => coin);
13+
14+
assert(tokens.length > 0, `No Tao token found for subnetId: ${subnetId}`);
15+
assert(tokens.length === 1, `Multiple Tao tokens found for subnetId: ${subnetId}`);
16+
return tokens[0];
17+
}
918
}
1019

1120
const utils = new Utils();

modules/sdk-coin-tao/test/resources/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ export const rawTx = {
176176
'0x9806129f7b0675db59d19b4bd9c8c72eaabba75a9863d02b30115b8b3c3ca5c20f025422130000d501210300c823000009000000e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d',
177177
},
178178
transferStake: {
179-
unsigned:
180-
'0xdd02840061b18c6dc02ddcabdeac56cb4f21a971cc41cc97640f6f85b073480008c53a0d00aadae7fa1f53e7a5c900b330ff71bee6782cf3c29a2c6f9599162381cd021ad581c74ded89f49ec79adefed64af8ff16649553523dda9cb4f017cbf15681e50ed5012103000007569f7b0675db59d19b4bd9c8c72eaabba75a9863d02b30115b8b3c3ca5c20f02548a90be061598f4b592afbd546bcb6beadb3c02f5c129df2e11b698f9543dbd41010002000300000000002000',
179+
signed:
180+
'0xdd02840061b18c6dc02ddcabdeac56cb4f21a971cc41cc97640f6f85b073480008c53a0d00aadae7fa1f53e7a5c900b330ff71bee6782cf3c29a2c6f9599162381cd021ad581c74ded89f49ec79adefed64af8ff16649553523dda9cb4f017cbf15681e50ed5012103000007569f7b0675db59d19b4bd9c8c72eaabba75a9863d02b30115b8b3c3ca5c20f02548a90be061598f4b592afbd546bcb6beadb3c02f5c129df2e11b698f9543dbd41010001000300000000002000',
181181
},
182182
};
183183

modules/sdk-coin-tao/test/unit/transactionBuilder/tokenTransferBuilder.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import assert from 'assert';
22
import should from 'should';
3-
import { spy, assert as SinonAssert } from 'sinon';
3+
import { assert as SinonAssert, spy } from 'sinon';
4+
import { TokenTransferBuilder } from '../../../src/lib/tokenTransferBuilder';
5+
import utils from '../../../src/lib/utils';
46
import { accounts, mockTssSignature, rawTx } from '../../resources';
57
import { buildTestConfig } from './base';
6-
import utils from '../../../src/lib/utils';
7-
import { TokenTransferBuilder } from '../../../src/lib/tokenTransferBuilder';
88

99
describe('Tao Token Transfer Builder', function () {
1010
const referenceBlock = '0x149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d';
@@ -52,7 +52,7 @@ describe('Tao Token Transfer Builder', function () {
5252
.destinationColdkey('5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq')
5353
.hotkey('5FCPTnjevGqAuTttetBy4a24Ej3pH9fiQ8fmvP1ZkrVsLUoT')
5454
.originNetuid('1')
55-
.destinationNetuid('2')
55+
.destinationNetuid('1')
5656
.sender({ address: sender.address })
5757
.validity({ firstValid: 3933, maxDuration: 64 })
5858
.referenceBlock(referenceBlock)
@@ -62,15 +62,43 @@ describe('Tao Token Transfer Builder', function () {
6262

6363
const tx = await builder.build();
6464
const serializedTx = tx.toBroadcastFormat();
65-
serializedTx.should.equal(rawTx.transferStake.unsigned);
65+
66+
serializedTx.should.equal(rawTx.transferStake.signed);
67+
tx.toJson().should.deepEqual({
68+
id: '0xe5ce9ff1bbdf54d1dbd5adee8648027aa7efa99d319b041afb4b57be2042fc11',
69+
sender: '5EGoFA95omzemRssELLDjVenNZ68aXyUeqtKQScXSEBvVJkr',
70+
referenceBlock: '0x149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d',
71+
blockNumber: 3933,
72+
genesisHash: '0x8f9cf856bf558a14440e75569c9e58594757048d7b3a84b5d25f6bd978263105',
73+
nonce: 200,
74+
specVersion: 224,
75+
transactionVersion: 1,
76+
eraPeriod: 64,
77+
chainName: 'Bittensor',
78+
tip: 0,
79+
destinationColdkey: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq',
80+
hotkey: '5FCPTnjevGqAuTttetBy4a24Ej3pH9fiQ8fmvP1ZkrVsLUoT',
81+
originNetuid: '1',
82+
destinationNetuid: '1',
83+
alphaAmount: '9007199254740995',
84+
});
85+
tx.explainTransaction().should.containDeep({
86+
outputs: [
87+
{
88+
address: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq',
89+
amount: '9007199254740995',
90+
tokenName: 'ttao:apex',
91+
},
92+
],
93+
});
6694
});
6795

6896
it('should re-build from raw signed tx', async function () {
69-
builder.from(rawTx.transferStake.unsigned);
97+
builder.from(rawTx.transferStake.signed);
7098
builder.validity({ firstValid: 3933, maxDuration: 64 }).referenceBlock(referenceBlock);
7199
const tx = await builder.build();
72100
const serializedTx = tx.toBroadcastFormat();
73-
serializedTx.should.equal(rawTx.transferStake.unsigned);
101+
serializedTx.should.equal(rawTx.transferStake.signed);
74102
});
75103
});
76104
});

0 commit comments

Comments
 (0)