Skip to content

Commit a2eac5d

Browse files
authored
Merge pull request #6706 from BitGo/TMS-1375
refactor: improve stablecoin example
2 parents 882d034 + 92cd0ed commit a2eac5d

File tree

2 files changed

+389
-129
lines changed

2 files changed

+389
-129
lines changed

examples/ts/stablecoin/initiate-burn-order.ts

Lines changed: 193 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,90 +10,219 @@ require('dotenv').config({ path: '../../../.env' });
1010

1111
/**
1212
* Step 1. GET /assets API
13-
* Step 2. GET /constants API
13+
* Retrieves:
14+
* - The list of assets supported for burning
15+
*
16+
* Step 2. GET /{token}/constants API
17+
* Retrieves:
18+
* - Treasury Account Wallet ID for the specified stablecoin
19+
*
1420
* Step 3. POST /order API
15-
* Step 4. sendMany
21+
* Creates a Burn Order with the specified parameters
22+
*
23+
* Step 4. Transfer the assets to be burnt, to the treasury account
24+
* Initiates the send transaction to the Treasury Account using sendMany
1625
*/
1726

18-
// Add the parameters here
27+
// IMPORTANT - Update the parameters here
1928
const environment = 'test';
2029
const accessToken = '';
2130
const enterpriseId = '';
2231
const walletId = ''; // GoAccount Wallet ID
2332
const walletPassphrase = ''; // Wallet passphrase
24-
const usdcoin = 'tfiatusd';
25-
const stablecoin = 'tbsc:usd1';
26-
const ofcStablecoin = 'ofctbsc:usd1';
27-
const fromAmount = '3000000000000000000'; // in base units, e.g., 3000000000000000000 for 3 TBSC:USD1
2833

34+
const usdcoin = 'tfiatusd'; // USD asset token
35+
const stablecoin = 'tbsc:usd1'; // Stablecoin to burn
36+
const ofcStablecoin = `ofc${stablecoin}`; // ofc stablecoin (for initiating the send from the specified GoAccount wallet to the Treasury Go Account)
37+
const fromAmountInFullUnits = '100'; // Amount in full units of the stablecoin (3 tbsc:usd1) - Must be an integer
38+
// Note: fromAmount will be calculated dynamically using asset decimals
39+
40+
// Initialize BitGo SDK
2941
const bitgo = new BitGoJS.BitGo({ env: environment });
3042
bitgo.authenticateWithAccessToken({ accessToken: accessToken });
31-
3243
const basecoin = bitgo.coin(ofcStablecoin);
3344

45+
function createStablecoinUrl(path: string): string {
46+
return common.Environments[bitgo.getEnv()].uri + '/api/stablecoin/v1' + path;
47+
}
48+
49+
/**
50+
* Fetch treasury wallet ID from the constants API
51+
* @param token - The stablecoin token to get constants for
52+
* @returns The treasury account wallet ID
53+
*/
54+
async function fetchTreasuryWalletId(token: string): Promise<string> {
55+
console.log(`\n🔍 STEP 2: Fetching treasury wallet ID from constants API for ${token}...`);
56+
const constants = await bitgo.get(createStablecoinUrl(`/${token}/constants`));
57+
const treasuryAccountWalletId = constants.body.trustAccountWalletId;
58+
59+
if (!treasuryAccountWalletId) {
60+
throw new Error(`Treasury account wallet ID not found in constants for ${token}`);
61+
}
62+
63+
console.log(`🏦 Treasury Account Wallet ID (from constants): ${treasuryAccountWalletId}`);
64+
return treasuryAccountWalletId;
65+
}
66+
67+
/**
68+
* Main function to execute the stablecoin burn order process
69+
*/
3470
async function main() {
3571
try {
36-
// Custom API path helper function
37-
const stablecoinUrl = (path: string) => {
38-
return common.Environments[bitgo.getEnv()].uri + '/api/stablecoin/v1' + path;
39-
};
40-
41-
// STEP - 1: Gets the list of assets
42-
const assets = await bitgo.get(stablecoinUrl('/assets'));
43-
console.log('assets:', assets.body);
44-
45-
// Finds the USD and Stablecoin assets from above response to use in initiating the burn order
46-
const usdAsset = assets.body.find((asset: any) => asset.token === usdcoin);
47-
const stablecoinAsset = assets.body.find((asset: any) => asset.token === stablecoin);
48-
if (!usdAsset || !stablecoinAsset) {
49-
throw new Error(`Asset ${usdcoin}/${stablecoin} not found`);
50-
}
51-
52-
// STEP - 2: Gets the constants for the stablecoin
53-
const constants = await bitgo.get(stablecoinUrl(`/${stablecoin}/constants`)).send();
54-
console.log('constants:', constants.body);
55-
// Extract the treasury account wallet ID from the constants response
56-
const trustAccountWalletId = constants.body.trustAccountWalletId;
57-
if (!trustAccountWalletId) {
58-
throw new Error(`Trust account wallet ID not found for ${stablecoin}`);
59-
}
72+
console.log('🚀 Starting Stablecoin Burn Order Process...');
73+
console.log('='.repeat(50));
74+
console.log(`Environment: ${environment}`);
75+
console.log(`Stablecoin: ${stablecoin}`);
76+
console.log(`Amount to burn: ${fromAmountInFullUnits} full units`);
77+
console.log('='.repeat(50));
6078

61-
// STEP - 3: Initiates the burn order
62-
const orderRequestBody = {
63-
sourceWalletId: walletId,
64-
destinationWalletId: walletId,
65-
destinationType: "go_account",
66-
fromAssetId: stablecoinAsset.id,
67-
toAssetId: usdAsset.id,
68-
fromAmount,
69-
type: "burn",
70-
};
71-
const postOrderResponse = await bitgo.post(stablecoinUrl(`/enterprise/${enterpriseId}/order`)).send(orderRequestBody);
72-
const newOrder = postOrderResponse.body;
73-
console.log('Order created:', newOrder);
74-
75-
// STEP - 4: Sends the transaction to the Treasury Account using sendMany
76-
const walletInstance = await basecoin.wallets().get({ id: walletId });
77-
const transaction = await walletInstance.sendMany({
78-
recipients: [
79-
{
80-
address: trustAccountWalletId,
81-
amount: fromAmount,
82-
}
83-
],
84-
sequenceId: newOrder.id, // IMPORTANT: Use the order ID as the sequence ID
85-
walletPassphrase,
86-
});
87-
console.log('Burn order process completed successfully!');
88-
console.log('New Transaction:', JSON.stringify(transaction, null, 4));
89-
90-
const order = await bitgo.get(stablecoinUrl(`/enterprise/${enterpriseId}/orders?ids=${newOrder.id}`)).send();
91-
console.log('Order details:', JSON.stringify(order.body, null, 4));
79+
// Execute the burn order process step by step
80+
const { usdAsset, stablecoinAsset, fromAmount } = await fetchAndValidateAssets();
81+
const treasuryAccountWalletId = await fetchTreasuryWalletId(stablecoin);
82+
const newOrder = await createBurnOrder(stablecoinAsset, usdAsset, fromAmount);
83+
await sendTokensToTreasury(treasuryAccountWalletId, newOrder.id, fromAmount);
84+
const order = await fetchOrderDetails(newOrder.id);
9285

86+
displayOrderSummary(order.id, order.status, order.fromAmount);
9387
} catch (error) {
94-
console.error('Error in burn order process:', error);
88+
console.log('\n❌ ERROR: Burn order process failed!');
89+
console.log('='.repeat(50));
90+
console.error(`💥 Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
91+
92+
if (error instanceof Error && error.stack) {
93+
console.log('\n🔍 Stack trace:');
94+
console.error(error.stack);
95+
}
96+
97+
console.log('='.repeat(50));
9598
throw error;
9699
}
97100
}
98101

102+
/**
103+
* Fetch and validate supported assets for the burn operation
104+
* @returns Object containing USD and stablecoin asset information with calculated amount
105+
*/
106+
async function fetchAndValidateAssets() {
107+
console.log('\n🔍 STEP 1: Fetching available assets...');
108+
109+
const assets = await bitgo.get(createStablecoinUrl('/assets'));
110+
console.log(`✅ Found ${assets.body.length} assets`);
111+
112+
const usdAsset = assets.body.find((asset: any) => asset.token === usdcoin);
113+
const stablecoinAsset = assets.body.find((asset: any) => asset.token === stablecoin);
114+
115+
if (!usdAsset || !stablecoinAsset) {
116+
throw new Error(`Required assets not found: ${usdcoin}/${stablecoin}`);
117+
}
118+
119+
console.log(`📋 USD Asset: ${usdAsset.token} (ID: ${usdAsset.id})`);
120+
console.log(`🪙 Stablecoin Asset: ${stablecoinAsset.token} (ID: ${stablecoinAsset.id})`);
121+
122+
// Calculate fromAmount using stablecoin asset decimals
123+
const decimals = stablecoinAsset.decimals;
124+
125+
if (decimals === undefined) {
126+
throw new Error(`Decimals not found for ${stablecoin}`);
127+
}
128+
129+
// Calculate fromAmount using asset decimals
130+
const fromAmount = (parseInt(fromAmountInFullUnits, 10) * Math.pow(10, decimals)).toString();
131+
132+
console.log(`💰 Amount Calculation:`);
133+
console.log(` • Full Units: ${fromAmountInFullUnits} ${stablecoinAsset.token}`);
134+
console.log(` • Decimals: ${decimals}`);
135+
console.log(` • Base Units: ${fromAmount}`);
136+
137+
return { usdAsset, stablecoinAsset, fromAmount };
138+
}
139+
140+
/**
141+
* Create a burn order with the specified parameters
142+
* @param stablecoinAsset - The stablecoin asset to burn
143+
* @param usdAsset - The USD asset to receive
144+
* @param fromAmount - The amount in base units to burn
145+
* @returns The created order object
146+
*/
147+
async function createBurnOrder(stablecoinAsset: any, usdAsset: any, fromAmount: string) {
148+
console.log('\n🔥 STEP 3: Creating burn order...');
149+
150+
const orderRequestBody = {
151+
sourceWalletId: walletId,
152+
destinationWalletId: walletId,
153+
destinationType: 'go_account',
154+
fromAssetId: stablecoinAsset.id,
155+
toAssetId: usdAsset.id,
156+
fromAmount,
157+
type: 'burn',
158+
};
159+
160+
console.log('📝 Order Request Details:');
161+
console.log(` • Type: ${orderRequestBody.type}`);
162+
console.log(` • From Asset: ${stablecoin}${usdcoin}`);
163+
console.log(` • Amount: ${fromAmount} (base units)`);
164+
165+
const postOrderResponse = await bitgo
166+
.post(createStablecoinUrl(`/enterprise/${enterpriseId}/order`))
167+
.send(orderRequestBody);
168+
const order = postOrderResponse.body;
169+
170+
console.log(`✅ Burn order created successfully!`);
171+
console.log(` • Order ID: ${order.id}`);
172+
173+
return order;
174+
}
175+
176+
/**
177+
* Send stablecoin tokens to the treasury account for burning
178+
* @param treasuryAccountWalletId - The treasury wallet ID to send tokens to
179+
* @param orderId - The order ID to use as sequence ID
180+
* @param fromAmount - The amount in base units of stablecoin to send
181+
* @returns The transaction object
182+
*/
183+
async function sendTokensToTreasury(treasuryAccountWalletId: string, orderId: string, fromAmount: string) {
184+
console.log('\n💸 STEP 4: Sending stablecoin to treasury account...');
185+
186+
const walletInstance = await basecoin.wallets().get({ id: walletId });
187+
188+
console.log('🔄 Initiating transaction...');
189+
console.log(` • From Wallet: ${walletId}`);
190+
console.log(` • To Treasury: ${treasuryAccountWalletId}`);
191+
console.log(` • Amount: ${fromAmount} (base units)`);
192+
console.log(` • Sequence ID: ${orderId}`);
193+
194+
const transaction = await walletInstance.sendMany({
195+
recipients: [
196+
{
197+
address: treasuryAccountWalletId,
198+
amount: fromAmount,
199+
},
200+
],
201+
sequenceId: orderId, // IMPORTANT - Use order ID as sequence ID for tracking
202+
walletPassphrase,
203+
});
204+
205+
console.log('✅ Transaction sent successfully!');
206+
207+
return transaction;
208+
}
209+
210+
async function fetchOrderDetails(orderId: string) {
211+
console.log('\n🔍 STEP 5: Fetching final order details...');
212+
const orderResponse = await bitgo.get(createStablecoinUrl(`/enterprise/${enterpriseId}/orders/${orderId}`)).send();
213+
return orderResponse.body;
214+
}
215+
216+
function displayOrderSummary(orderId: string, status: string, fromAmount: string) {
217+
console.log('\n🎉 BURN ORDER INITIATION COMPLETED SUCCESSFULLY!');
218+
console.log('='.repeat(50));
219+
console.log('📊 SUMMARY:');
220+
console.log(` • Order ID: ${orderId}`);
221+
console.log(` • Type: Burn Order`);
222+
console.log(` • Status: ${status}`);
223+
console.log(` • Asset: ${stablecoin}${usdcoin}`);
224+
console.log(` • Amount: ${fromAmount} base units`);
225+
console.log('='.repeat(50));
226+
}
227+
99228
main().catch((e) => console.error(e));

0 commit comments

Comments
 (0)