-
Notifications
You must be signed in to change notification settings - Fork 299
feat(sdk-coin-ton): add ton whales withdrawal builder #7798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+244
−1
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
modules/sdk-coin-ton/src/lib/tonWhalesWithdrawalBuilder.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { BaseCoin as CoinConfig } from '@bitgo/statics'; | ||
| import { Recipient, TransactionType } from '@bitgo/sdk-core'; | ||
| import { TransactionBuilder } from './transactionBuilder'; | ||
| import { Transaction } from './transaction'; | ||
| import { TON_WHALES_WITHDRAW_OPCODE } from './constants'; | ||
|
|
||
| export class TonWhalesWithdrawalBuilder extends TransactionBuilder { | ||
| constructor(_coinConfig: Readonly<CoinConfig>) { | ||
| super(_coinConfig); | ||
| this._transaction = new Transaction(_coinConfig); | ||
| } | ||
|
|
||
| protected get transactionType(): TransactionType { | ||
| return TransactionType.TonWhalesWithdrawal; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the payload for the withdrawal request. | ||
| * Structure: OpCode (32) + QueryId (64) + GasLimit (Coins) + UnstakeAmount (Coins) | ||
| * * @param unstakeAmount The amount of NanoTON to unstake (inside payload) | ||
| * @param unstakeAmount The amount to unstake | ||
| * @param queryId Optional custom query ID | ||
| */ | ||
| setWithdrawalMessage(unstakeAmount: string, queryId?: string): TonWhalesWithdrawalBuilder { | ||
| const qId = queryId || '0000000000000000'; | ||
|
|
||
| this.transaction.message = TON_WHALES_WITHDRAW_OPCODE + qId + unstakeAmount; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the message to withdraw EVERYTHING from the pool. | ||
| * This sets the unstakeAmount to "0", which is the specific signal for full withdrawal. | ||
| */ | ||
| setFullWithdrawalMessage(queryId?: string): TonWhalesWithdrawalBuilder { | ||
| return this.setWithdrawalMessage('0', queryId); | ||
| } | ||
|
|
||
| /** | ||
| * Sets the value attached to the transaction (The Fees). | ||
| * NOTE: This is NOT the unstake amount. This is the fee paid to the pool | ||
| * to process the request (e.g. withdrawFee + receiptPrice). | ||
| * * @param amount NanoTON amount to attach to the message | ||
| */ | ||
| setForwardAmount(amount: string): TonWhalesWithdrawalBuilder { | ||
| if (!this.transaction.recipient) { | ||
| this.transaction.recipient = { address: '', amount: amount }; | ||
| } else { | ||
| this.transaction.recipient.amount = amount; | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| send(recipient: Recipient): TonWhalesWithdrawalBuilder { | ||
| this.transaction.recipient = recipient; | ||
| return this; | ||
| } | ||
|
|
||
| setMessage(msg: string): TonWhalesWithdrawalBuilder { | ||
| throw new Error('Use setWithdrawalMessage for specific payload construction'); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
modules/sdk-coin-ton/test/unit/tonWhalesWithdrawalBuilder.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import should from 'should'; | ||
| import { TransactionType } from '@bitgo/sdk-core'; | ||
| import { TransactionBuilderFactory } from '../../src'; // Adjust path as needed | ||
| import { coins } from '@bitgo/statics'; | ||
| import * as testData from '../resources/ton'; | ||
| import { TON_WHALES_WITHDRAW_OPCODE } from '../../src/lib/constants'; | ||
|
|
||
| describe('Ton Whales Withdrawal Builder', () => { | ||
| const factory = new TransactionBuilderFactory(coins.get('tton')); | ||
|
|
||
| // Define the scenarios we want to test | ||
| const scenarios = [ | ||
| { | ||
| name: 'Partial Withdrawal (10 TON)', | ||
| fixture: testData.signedTonWhalesWithdrawalTransaction, | ||
| }, | ||
| { | ||
| name: 'Full Withdrawal (Amount 0)', | ||
| fixture: testData.signedTonWhalesFullWithdrawalTransaction, | ||
| }, | ||
| ]; | ||
|
|
||
| scenarios.forEach((scenario) => { | ||
| describe(scenario.name, () => { | ||
| const fixture = scenario.fixture; | ||
|
|
||
| it('should parse a raw transaction and extract correct parameters', async function () { | ||
| const txBuilder = factory.from(fixture.tx); | ||
| const builtTx = await txBuilder.build(); | ||
| const jsonTx = builtTx.toJson(); | ||
|
|
||
| // Verify Business Logic Fields | ||
| should.equal(builtTx.type, TransactionType.TonWhalesWithdrawal); | ||
|
|
||
| // NOTE: In withdrawals, recipient.amount is the FEE, withdrawAmount is the STAKE | ||
| should.equal(jsonTx.amount, fixture.recipient.amount); | ||
| should.equal(jsonTx.withdrawAmount, fixture.withdrawAmount); | ||
|
|
||
| should.equal(jsonTx.destination, fixture.recipient.address); | ||
| should.equal(jsonTx.sender, fixture.sender); | ||
|
|
||
| // Verify Network Constraints | ||
| should.equal(jsonTx.seqno, fixture.seqno); | ||
| should.equal(jsonTx.expirationTime, fixture.expireTime); | ||
| should.equal(jsonTx.bounceable, fixture.bounceable); | ||
|
|
||
| // Verify Payload Structure | ||
| // Logic: DecimalOpCode + HexQueryId + DecimalAmount | ||
| const msg = builtTx['message'] || ''; | ||
| should.equal(msg.startsWith(TON_WHALES_WITHDRAW_OPCODE), true); | ||
|
|
||
| // Ensure the payload ENDS with the decimal amount (either "1000..." or "0") | ||
| should.equal(msg.endsWith(fixture.withdrawAmount), true); | ||
| }); | ||
|
|
||
| it('should parse and rebuild the transaction resulting in the same hex', async function () { | ||
| const txBuilder = factory.from(fixture.tx); | ||
| const builtTx = await txBuilder.build(); | ||
|
|
||
| // Verify the parser extracted the signature | ||
| const signature = builtTx.signature[0]; | ||
| should.exist(signature); | ||
| signature.should.not.be.empty(); | ||
|
|
||
| // Rebuild from the parsed object | ||
| const builder2 = factory.from(builtTx.toBroadcastFormat()); | ||
| const builtTx2 = await builder2.build(); | ||
|
|
||
| // The output of the second build should match the original raw transaction | ||
| should.equal(builtTx2.toBroadcastFormat(), fixture.tx); | ||
| should.equal(builtTx2.type, TransactionType.TonWhalesWithdrawal); | ||
| }); | ||
|
|
||
| it('should build a transaction from scratch that byte-for-byte matches the raw fixture', async function () { | ||
| // Get the specific Withdrawal Builder | ||
| const builder = factory.getTonWhalesWithdrawalBuilder(); | ||
|
|
||
| // Set Header Info from Fixture | ||
| builder.sender(fixture.sender); | ||
| builder.publicKey(fixture.publicKey); | ||
| builder.sequenceNumber(fixture.seqno); | ||
| builder.expireTime(fixture.expireTime); | ||
| builder.bounceable(fixture.bounceable); | ||
|
|
||
| // Set Destination and ATTACHED VALUE (The Fee) | ||
| builder.send({ | ||
| address: fixture.recipient.address, | ||
| amount: fixture.recipient.amount, | ||
| }); | ||
|
|
||
| // Set Payload Data (The Unstake Amount) | ||
| // Note: This works for both partial (amount > 0) and full (amount = "0") | ||
| builder.setWithdrawalMessage(fixture.withdrawAmount, fixture.queryId); | ||
|
|
||
| // Attach Signature from Fixture (Mocking the HSM signing process) | ||
| if (fixture.signature) { | ||
| builder.addSignature({ pub: fixture.publicKey }, Buffer.from(fixture.signature, 'hex')); | ||
| } | ||
|
|
||
| // Build Signed Transaction | ||
| const signedBuiltTx = await builder.build(); | ||
|
|
||
| // Byte-for-byte equality with the Sandbox output | ||
| should.equal(signedBuiltTx.toBroadcastFormat(), fixture.tx); | ||
| should.equal(signedBuiltTx.type, TransactionType.TonWhalesWithdrawal); | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.