Skip to content

Commit f89be52

Browse files
committed
feat(sdk-coin-ton): add single nominator withdraw txn (Ticket: SC-370)
1 parent a10dac8 commit f89be52

File tree

11 files changed

+634
-186
lines changed

11 files changed

+634
-186
lines changed

modules/sdk-coin-ton/src/lib/iface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { TransactionFee } from '@bitgo/sdk-core';
2+
import { ITransactionExplanation } from './transactionExplanation';
3+
14
/**
25
* The transaction data returned from the toJson() function of a transaction
36
*/
@@ -7,9 +10,12 @@ export interface TxData {
710
destination: string;
811
destinationAlias: string;
912
amount: string;
13+
withdrawAmount: string;
1014
seqno: number;
1115
expirationTime: number;
1216
publicKey: string;
1317
signature: string;
1418
bounceable: boolean;
1519
}
20+
21+
export type TransactionExplanation = ITransactionExplanation<TransactionFee>;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { TransactionBuilder } from './transactionBuilder';
2+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3+
import { Recipient, TransactionType } from '@bitgo/sdk-core';
4+
5+
export class SingleNominatorWithdrawBuilder extends TransactionBuilder {
6+
constructor(_coinConfig: Readonly<CoinConfig>) {
7+
super(_coinConfig);
8+
}
9+
10+
protected get transactionType(): TransactionType {
11+
return TransactionType.SingleNominatorWithdraw;
12+
}
13+
14+
setWithdrawAmount(amount: string): SingleNominatorWithdrawBuilder {
15+
this.transaction.withdrawAmount = amount;
16+
return this;
17+
}
18+
19+
send(recipient: Recipient): SingleNominatorWithdrawBuilder {
20+
this.transaction.recipient = recipient;
21+
return this;
22+
}
23+
24+
setMessage(msg: string): SingleNominatorWithdrawBuilder {
25+
throw new Error('Method not implemented.');
26+
}
27+
}

modules/sdk-coin-ton/src/lib/transaction.ts

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import {
2-
BaseKey,
3-
BaseTransaction,
4-
Entry,
5-
Recipient,
6-
TransactionRecipient,
7-
TransactionType,
8-
TransactionExplanation,
9-
} from '@bitgo/sdk-core';
10-
import { TxData } from './iface';
1+
import { BaseKey, BaseTransaction, Entry, Recipient, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
2+
import { TxData, TransactionExplanation } from './iface';
113
import { BaseCoin as CoinConfig } from '@bitgo/statics';
124
import TonWeb from 'tonweb';
135
import { BN } from 'bn.js';
@@ -20,7 +12,8 @@ export class Transaction extends BaseTransaction {
2012
public bounceable: boolean;
2113
public fromAddressBounceable: boolean;
2214
public toAddressBounceable: boolean;
23-
public message: string;
15+
public message: string | Cell;
16+
public withdrawAmount: string;
2417
seqno: number;
2518
expireTime: number;
2619
sender: string;
@@ -55,6 +48,7 @@ export class Transaction extends BaseTransaction {
5548
destination: this.recipient.address,
5649
destinationAlias: otherFormat,
5750
amount: this.recipient.amount,
51+
withdrawAmount: this.withdrawAmount,
5852
seqno: this.seqno,
5953
expirationTime: this.expireTime,
6054
publicKey: this.publicKey,
@@ -67,8 +61,15 @@ export class Transaction extends BaseTransaction {
6761
return Buffer.from(this.unsignedMessage, 'hex');
6862
}
6963

64+
/**
65+
* Set the transaction type.
66+
* @param {TransactionType} transactionType The transaction type to be set.
67+
*/
68+
set transactionType(transactionType: TransactionType) {
69+
this._type = transactionType;
70+
}
71+
7072
async build(): Promise<void> {
71-
this._type = TransactionType.Send;
7273
const signingMessage = this.createSigningMessage(WALLET_ID, this.seqno, this.expireTime);
7374
const sendMode = 3;
7475
signingMessage.bits.writeUint8(sendMode);
@@ -118,7 +119,7 @@ export class Transaction extends BaseTransaction {
118119
true,
119120
this.bounceable
120121
);
121-
return TonWeb.Contract.createCommonMsgInfo(orderHeader, undefined, payloadCell);
122+
return TonWeb.Contract.createCommonMsgInfo(orderHeader, undefined, payloadCell); // compare with commonmsg tonweb and ton
122123
}
123124

124125
async createExternalMessage(signingMessage: Cell, seqno: number, signature: string): Promise<Cell> {
@@ -164,12 +165,13 @@ export class Transaction extends BaseTransaction {
164165
try {
165166
const cell = TonWeb.boc.Cell.oneFromBoc(TonWeb.utils.base64ToBytes(rawTransaction));
166167

167-
const parsed = this.parseTransfer(cell);
168+
const parsed = this.parseTransaction(cell);
168169
parsed.value = parsed.value.toString();
169170
parsed.fromAddress = parsed.fromAddress.toString(true, true, this.fromAddressBounceable);
170171
parsed.toAddress = parsed.toAddress.toString(true, true, this.toAddressBounceable);
171172
this.sender = parsed.fromAddress;
172173
this.recipient = { address: parsed.toAddress, amount: parsed.value };
174+
this.withdrawAmount = parsed.withdrawAmount;
173175
this.seqno = parsed.seqno;
174176
this.publicKey = parsed.publicKey as string;
175177
this.expireTime = parsed.expireAt;
@@ -183,10 +185,11 @@ export class Transaction extends BaseTransaction {
183185

184186
/** @inheritDoc */
185187
explainTransaction(): TransactionExplanation {
186-
const displayOrder = ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee'];
188+
const displayOrder = ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'];
187189

188190
const outputs: TransactionRecipient[] = [this.recipient];
189191
const outputAmount = this.recipient.amount;
192+
const withdrawAmount = this.withdrawAmount;
190193
return {
191194
displayOrder,
192195
id: this.id,
@@ -195,10 +198,11 @@ export class Transaction extends BaseTransaction {
195198
changeOutputs: [],
196199
changeAmount: '0',
197200
fee: { fee: 'UNKNOWN' },
201+
withdrawAmount,
198202
};
199203
}
200204

201-
private parseTransfer(cell: Cell): any {
205+
private parseTransaction(cell: Cell): any {
202206
const slice = (cell as any).beginParse();
203207

204208
// header
@@ -239,11 +243,11 @@ export class Transaction extends BaseTransaction {
239243
return {
240244
fromAddress: externalDestAddress,
241245
publicKey,
242-
...this.parseTransferBody(bodySlice),
246+
...this.parseTransactionBody(bodySlice),
243247
};
244248
}
245249

246-
private parseTransferBody(slice: any): any {
250+
private parseTransactionBody(slice: any): any {
247251
const signature = Buffer.from(slice.loadBits(512)).toString('hex');
248252
// signing message
249253

@@ -288,23 +292,37 @@ export class Transaction extends BaseTransaction {
288292

289293
// order body
290294
let payload;
291-
295+
let withdrawAmount;
296+
this.transactionType = TransactionType.Send;
292297
if (order.getFreeBits() > 0) {
293298
if (order.loadBit()) {
294299
order = order.loadRef();
295300
}
296301

297302
if (order.getFreeBits() > 32) {
298-
const op = order.loadUint(32);
299-
const payloadBytes = order.loadBits(order.getFreeBits());
300-
payload = op.eq(new BN(0)) ? new TextDecoder().decode(payloadBytes) : '';
303+
const opcode = order.loadUint(32).toNumber();
304+
if (opcode === 0) {
305+
const payloadBytes = order.loadBits(order.getFreeBits());
306+
payload = new TextDecoder().decode(payloadBytes);
307+
} else if (opcode === 4096) {
308+
const queryId = order.loadUint(64).toNumber();
309+
withdrawAmount = (order.loadCoins().toNumber() / 1e9).toString();
310+
payload = new TonWeb.boc.Cell();
311+
payload.bits.writeUint(opcode, 32);
312+
payload.bits.writeUint(queryId, 64);
313+
payload.bits.writeCoins(TonWeb.utils.toNano(withdrawAmount));
314+
this.transactionType = TransactionType.SingleNominatorWithdraw;
315+
} else {
316+
payload = '';
317+
}
301318
}
302319
}
303320
return {
304321
toAddress: destAddress,
305322
value,
306323
bounce,
307324
seqno,
325+
withdrawAmount,
308326
expireAt,
309327
payload,
310328
signature,

modules/sdk-coin-ton/src/lib/transactionBuilder.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import BigNumber from 'bignumber.js';
1414
import { BaseCoin as CoinConfig } from '@bitgo/statics';
1515
import TonWeb from 'tonweb';
1616

17+
const WITHDRAW_OPCODE = 4096;
18+
1719
export abstract class TransactionBuilder extends BaseTransactionBuilder {
1820
protected _transaction: Transaction;
1921
private _signatures: Signature[] = [];
@@ -106,12 +108,14 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
106108

107109
/** @inheritdoc */
108110
protected fromImplementation(rawTransaction: string): Transaction {
111+
this.transaction.transactionType = this.transactionType;
109112
this.transaction.fromRawTransaction(rawTransaction);
110113
return this.transaction;
111114
}
112115

113116
/** @inheritdoc */
114117
protected async buildImplementation(): Promise<Transaction> {
118+
this.transaction.transactionType = this.transactionType;
115119
await this.transaction.build();
116120
this.transaction.loadInputsAndOutputs();
117121
return this.transaction;
@@ -156,6 +160,15 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
156160
return this;
157161
}
158162

163+
setWithdrawMessage(): TransactionBuilder {
164+
const message = new TonWeb.boc.Cell();
165+
message.bits.writeUint(WITHDRAW_OPCODE, 32);
166+
message.bits.writeUint(0, 64);
167+
message.bits.writeCoins(TonWeb.utils.toNano(this.transaction.withdrawAmount));
168+
this.transaction.message = message;
169+
return this;
170+
}
171+
159172
sequenceNumber(number: number): TransactionBuilder {
160173
this.transaction.seqno = number;
161174
return this;

modules/sdk-coin-ton/src/lib/transactionBuilderFactory.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,49 @@
1-
import { BaseTransactionBuilderFactory } from '@bitgo/sdk-core';
1+
import { BaseTransactionBuilderFactory, InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
22
import { TransactionBuilder } from './transactionBuilder';
33
import { TransferBuilder } from './transferBuilder';
44
import { BaseCoin as CoinConfig } from '@bitgo/statics';
5+
import { SingleNominatorWithdrawBuilder } from './singleNominatorWithdrawBuilder';
6+
import { Transaction } from './transaction';
57

68
export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
79
constructor(_coinConfig: Readonly<CoinConfig>) {
810
super(_coinConfig);
911
}
1012
/** @inheritdoc */
1113
from(raw: string): TransactionBuilder {
12-
const builder = new TransferBuilder(this._coinConfig);
13-
builder.from(raw);
14-
return builder;
14+
let builder: TransactionBuilder;
15+
const tx = new Transaction(this._coinConfig);
16+
tx.fromRawTransaction(raw);
17+
try {
18+
switch (tx.type) {
19+
case TransactionType.Send:
20+
builder = this.getTransferBuilder();
21+
break;
22+
case TransactionType.SingleNominatorWithdraw:
23+
builder = this.getSingleNominatorWithdrawBuilder();
24+
break;
25+
default:
26+
throw new InvalidTransactionError('unsupported transaction');
27+
}
28+
builder.from(raw);
29+
return builder;
30+
} catch (e) {
31+
throw e;
32+
}
1533
}
1634

1735
/** @inheritdoc */
1836
getTransferBuilder(): TransferBuilder {
1937
return new TransferBuilder(this._coinConfig);
2038
}
2139

40+
/**
41+
* Returns a specific builder to create a TON withdraw transaction
42+
*/
43+
getSingleNominatorWithdrawBuilder(): SingleNominatorWithdrawBuilder {
44+
return new SingleNominatorWithdrawBuilder(this._coinConfig);
45+
}
46+
2247
/** @inheritdoc */
2348
getWalletInitializationBuilder(): void {
2449
throw new Error('Method not implemented.');
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ITransactionRecipient } from '@bitgo/sdk-core';
2+
3+
export interface ITransactionExplanation<TFee = any, TAmount = any> {
4+
displayOrder: string[];
5+
id: string;
6+
outputs: ITransactionRecipient[];
7+
outputAmount: TAmount;
8+
changeOutputs: ITransactionRecipient[];
9+
changeAmount: TAmount;
10+
fee: TFee;
11+
proxy?: string;
12+
producers?: string[];
13+
withdrawAmount?: string;
14+
}

modules/sdk-coin-ton/test/resources/ton.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const recipients: Recipient[] = [
3434
},
3535
];
3636

37-
export const signedTransaction = {
37+
export const signedSendTransaction = {
3838
tx: 'te6cckEBAgEAqQAB4YgBJAxo7vqHF++LJ4bC/kJ8A1uVRskrKlrKJZ8rIB0tF+gCadlSX+hPo2mmhZyi0p3zTVUYVRkcmrCm97cSUFSa2vzvCArM3APg+ww92r3IcklNjnzfKOgysJVQXiCvj9SAaU1NGLsotvRwAAAAMAAcAQBmQgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr5zEtAAAAAAAAAAAAAAAAAAAdfZO7w==',
3939
txBounceable:
4040
'te6cckEBAgEAqQAB4YgBJAxo7vqHF++LJ4bC/kJ8A1uVRskrKlrKJZ8rIB0tF+gCadlSX+hPo2mmhZyi0p3zTVUYVRkcmrCm97cSUFSa2vzvCArM3APg+ww92r3IcklNjnzfKOgysJVQXiCvj9SAaU1NGLsotvRwAAAAMAAcAQBmYgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr5zEtAAAAAAAAAAAAAAAAAAAYubM0w==',
@@ -51,3 +51,21 @@ export const signedTransaction = {
5151
amount: '10000000',
5252
},
5353
};
54+
55+
export const signedTransferTransaction = {
56+
tx: 'te6cckEBAgEAugAB4YgANk3QfSfW3PrGRiBaolJGaRREphqOMSBd5rAGj1yJ88gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1NGLsotvRwAAAAMAAcAQCHQgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr5zEtAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAQ7msoAg2I8aq',
57+
txBounceable:
58+
'te6cckEBAgEAugAB4YgANk3QfSfW3PrGRiBaolJGaRREphqOMSBd5rAGj1yJ88gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1NGLsotvRwAAAAMAAcAQCHYgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr5zEtAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAQ7msoAhFP9Yn',
59+
txId: 'GUD-auBCZ3PJFfAPkSfeqe8rj2OiHMTXudH4IEWdDgo=',
60+
txIdBounceable: 'GUD-auBCZ3PJFfAPkSfeqe8rj2OiHMTXudH4IEWdDgo=',
61+
signable: 'cX/GEZo6PX0rrw35kBsOk91u0CyWEzASSjM0yzfAHp4=',
62+
bounceableSignable: 'cX/GEZo6PX0rrw35kBsOk91u0CyWEzASSjM0yzfAHp4=',
63+
recipient: {
64+
address: 'EQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBXwtG',
65+
amount: '10000000',
66+
},
67+
recipientBounceable: {
68+
address: 'UQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBX1aD',
69+
amount: '10000000',
70+
},
71+
};

0 commit comments

Comments
 (0)