torchsdk 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,17 +9,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
9
9
  return (mod && mod.__esModule) ? mod : { "default": mod };
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.buildSwapFeesToSolTransaction = exports.buildHarvestFeesTransaction = exports.buildVaultSwapTransaction = exports.buildMigrateTransaction = exports.buildWithdrawTokensTransaction = exports.buildReclaimFailedTokenTransaction = exports.buildClaimProtocolRewardsTransaction = exports.buildLiquidateTransaction = exports.buildRepayTransaction = exports.buildBorrowTransaction = exports.buildTransferAuthorityTransaction = exports.buildUnlinkWalletTransaction = exports.buildLinkWalletTransaction = exports.buildWithdrawVaultTransaction = exports.buildDepositVaultTransaction = exports.buildCreateVaultTransaction = exports.buildStarTransaction = exports.buildCreateTokenTransaction = exports.buildSellTransaction = exports.buildDirectBuyTransaction = exports.buildBuyTransaction = void 0;
12
+ exports.buildSwapFeesToSolTransaction = exports.buildHarvestFeesTransaction = exports.buildMigrateTransaction = exports.buildWithdrawTokensTransaction = exports.buildReclaimFailedTokenTransaction = exports.buildClaimProtocolRewardsTransaction = exports.buildLiquidateTransaction = exports.buildRepayTransaction = exports.buildBorrowTransaction = exports.buildTransferAuthorityTransaction = exports.buildUnlinkWalletTransaction = exports.buildLinkWalletTransaction = exports.buildWithdrawVaultTransaction = exports.buildDepositVaultTransaction = exports.buildCreateVaultTransaction = exports.buildStarTransaction = exports.buildCreateTokenTransaction = exports.buildSellTransaction = exports.buildDirectBuyTransaction = exports.buildBuyTransaction = exports.clearAltCache = exports.fetchAddressLookupTable = void 0;
13
13
  const web3_js_1 = require("@solana/web3.js");
14
14
  const spl_token_1 = require("@solana/spl-token");
15
15
  const anchor_1 = require("@coral-xyz/anchor");
16
16
  const program_1 = require("./program");
17
17
  const constants_1 = require("./constants");
18
18
  const tokens_1 = require("./tokens");
19
+ const quotes_1 = require("./quotes");
19
20
  const torch_market_json_1 = __importDefault(require("./torch_market.json"));
20
21
  // ============================================================================
21
22
  // Helpers
22
23
  // ============================================================================
24
+ const MAX_MESSAGE_LENGTH = 500;
23
25
  const makeDummyProvider = (connection, payer) => {
24
26
  const dummyWallet = {
25
27
  publicKey: payer,
@@ -28,24 +30,106 @@ const makeDummyProvider = (connection, payer) => {
28
30
  };
29
31
  return new anchor_1.AnchorProvider(connection, dummyWallet, {});
30
32
  };
33
+ /** Derive vault + wallet link PDAs. Returns nulls if vaultCreatorStr is undefined. */
34
+ const deriveVaultAccounts = (vaultCreatorStr, signer) => {
35
+ if (!vaultCreatorStr)
36
+ return { torchVault: null, walletLink: null };
37
+ const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
38
+ const [torchVault] = (0, program_1.getTorchVaultPda)(vaultCreator);
39
+ const [walletLink] = (0, program_1.getVaultWalletLinkPda)(signer);
40
+ return { torchVault, walletLink };
41
+ };
42
+ /** Create vault token ATA instruction (idempotent). */
43
+ const createVaultTokenAtaIx = (payer, mint, vaultPda) => (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(payer, (0, spl_token_1.getAssociatedTokenAddressSync)(mint, vaultPda, true, spl_token_1.TOKEN_2022_PROGRAM_ID), vaultPda, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
44
+ /** Get the vault's token ATA address. */
45
+ const getVaultTokenAta = (mint, vaultPda) => (0, spl_token_1.getAssociatedTokenAddressSync)(mint, vaultPda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
46
+ /** Add an SPL Memo instruction to a transaction. */
47
+ const addMemoIx = (tx, signer, message, maxLength = MAX_MESSAGE_LENGTH) => {
48
+ if (!message || message.trim().length === 0)
49
+ return;
50
+ const trimmed = message.trim().slice(0, maxLength);
51
+ if (trimmed.length < message.trim().length && maxLength === MAX_MESSAGE_LENGTH) {
52
+ throw new Error(`Message must be ${MAX_MESSAGE_LENGTH} characters or less`);
53
+ }
54
+ tx.add(new web3_js_1.TransactionInstruction({
55
+ programId: constants_1.MEMO_PROGRAM_ID,
56
+ keys: [{ pubkey: signer, isSigner: true, isWritable: false }],
57
+ data: Buffer.from(trimmed, 'utf-8'),
58
+ }));
59
+ };
60
+ // ── Address Lookup Table ─────────────────────────────────────────────
61
+ /** Cached ALT account — fetched once per connection, reused across builders. */
62
+ let cachedAlt = null;
63
+ /**
64
+ * Fetch the Torch ALT for this network. Cached per connection instance.
65
+ */
66
+ const fetchAddressLookupTable = async (connection) => {
67
+ if (cachedAlt && cachedAlt.connection === connection)
68
+ return cachedAlt.account;
69
+ const altAddress = (0, constants_1.getAddressLookupTableAddress)();
70
+ try {
71
+ const result = await connection.getAddressLookupTable(altAddress);
72
+ cachedAlt = { connection, account: result.value };
73
+ return result.value;
74
+ }
75
+ catch {
76
+ cachedAlt = { connection, account: null };
77
+ return null;
78
+ }
79
+ };
80
+ exports.fetchAddressLookupTable = fetchAddressLookupTable;
81
+ /** Clear the cached ALT (useful after deploying a new table). */
82
+ const clearAltCache = () => { cachedAlt = null; };
83
+ exports.clearAltCache = clearAltCache;
84
+ // ── Transaction finalization ────────────────────────────────────────
85
+ /**
86
+ * Compile instructions into a VersionedTransaction (v0 message).
87
+ * Uses the Torch ALT when available for address compression.
88
+ */
31
89
  const finalizeTransaction = async (connection, tx, feePayer) => {
32
90
  const { blockhash } = await connection.getLatestBlockhash();
33
- tx.recentBlockhash = blockhash;
34
- tx.feePayer = feePayer;
91
+ const alt = await (0, exports.fetchAddressLookupTable)(connection);
92
+ const lookupTables = alt ? [alt] : [];
93
+ const message = new web3_js_1.TransactionMessage({
94
+ payerKey: feePayer,
95
+ recentBlockhash: blockhash,
96
+ instructions: tx.instructions,
97
+ }).compileToV0Message(lookupTables);
98
+ return new web3_js_1.VersionedTransaction(message);
35
99
  };
36
100
  // ============================================================================
37
101
  // Buy
38
102
  // ============================================================================
39
103
  // Internal buy builder shared by both vault and direct variants
40
- const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount_sol, slippage_bps, vote, message, vaultCreatorStr) => {
104
+ const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount_sol, slippage_bps, vote, message, vaultCreatorStr, quote) => {
41
105
  const mint = new web3_js_1.PublicKey(mintStr);
42
106
  const buyer = new web3_js_1.PublicKey(buyerStr);
43
107
  const tokenData = await (0, tokens_1.fetchTokenRaw)(connection, mint);
44
108
  if (!tokenData)
45
109
  throw new Error(`Token not found: ${mintStr}`);
46
110
  const { bondingCurve, treasury } = tokenData;
47
- if (bondingCurve.bonding_complete)
48
- throw new Error('Bonding curve complete, trade on DEX');
111
+ // Migrated token — route through vault swap on Raydium DEX
112
+ if (quote?.source === 'dex' || bondingCurve.bonding_complete) {
113
+ if (!vaultCreatorStr) {
114
+ throw new Error('Migrated tokens require vault-based trading. Use buildBuyTransaction with a vault parameter.');
115
+ }
116
+ const resolvedQuote = quote ?? await (0, quotes_1.getBuyQuote)(connection, mintStr, amount_sol);
117
+ const slippage = slippage_bps ?? 100;
118
+ const minOut = BigInt(resolvedQuote.min_output_tokens) * BigInt(10000 - slippage) / BigInt(10000);
119
+ const result = await buildVaultSwapTransaction(connection, {
120
+ mint: mintStr,
121
+ signer: buyerStr,
122
+ vault_creator: vaultCreatorStr,
123
+ amount_in: amount_sol,
124
+ minimum_amount_out: Number(minOut),
125
+ is_buy: true,
126
+ message,
127
+ });
128
+ return {
129
+ ...result,
130
+ message: `Buy ~${resolvedQuote.tokens_to_user / 1e6} tokens for ${amount_sol / 1e9} SOL (via DEX)`,
131
+ };
132
+ }
49
133
  // Calculate expected output
50
134
  const virtualSol = BigInt(bondingCurve.virtual_sol_reserves.toString());
51
135
  const virtualTokens = BigInt(bondingCurve.virtual_token_reserves.toString());
@@ -81,17 +165,11 @@ const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount
81
165
  // Fetch global config for dev wallet
82
166
  const globalConfigAccount = (await program.account.globalConfig.fetch(globalConfigPda));
83
167
  // Vault accounts (optional — pass null when not using vault)
84
- let torchVaultAccount = null;
85
- let vaultWalletLinkAccount = null;
168
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, buyer);
86
169
  let vaultTokenAccount = null;
87
- if (vaultCreatorStr) {
88
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
89
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
90
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(buyer);
91
- // [V18] Tokens go to vault ATA instead of buyer's wallet
92
- vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
93
- // Create vault ATA if needed (vault PDA owns it)
94
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(buyer, vaultTokenAccount, torchVaultAccount, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
170
+ if (torchVaultAccount) {
171
+ vaultTokenAccount = getVaultTokenAta(mint, torchVaultAccount);
172
+ tx.add(createVaultTokenAtaIx(buyer, mint, torchVaultAccount));
95
173
  }
96
174
  const buyIx = await program.methods
97
175
  .buy({
@@ -122,19 +200,8 @@ const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount
122
200
  })
123
201
  .instruction();
124
202
  tx.add(buyIx);
125
- // Bundle optional message as SPL Memo instruction
126
- if (message && message.trim().length > 0) {
127
- if (message.length > MAX_MESSAGE_LENGTH) {
128
- throw new Error(`Message must be ${MAX_MESSAGE_LENGTH} characters or less`);
129
- }
130
- const memoIx = new web3_js_1.TransactionInstruction({
131
- programId: constants_1.MEMO_PROGRAM_ID,
132
- keys: [{ pubkey: buyer, isSigner: true, isWritable: false }],
133
- data: Buffer.from(message.trim(), 'utf-8'),
134
- });
135
- tx.add(memoIx);
136
- }
137
- await finalizeTransaction(connection, tx, buyer);
203
+ addMemoIx(tx, buyer, message);
204
+ const versionedTx = await finalizeTransaction(connection, tx, buyer);
138
205
  // [V28] Build separate migration transaction when this buy completes bonding.
139
206
  // Split into two txs because buy + migration exceeds the 1232-byte legacy limit.
140
207
  // Program handles treasury reimbursement internally, so this is just a standard migration call.
@@ -149,7 +216,7 @@ const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount
149
216
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
150
217
  const migrateLabel = willCompleteBonding ? ' + migrate to DEX' : '';
151
218
  return {
152
- transaction: tx,
219
+ transaction: versionedTx,
153
220
  migrationTransaction,
154
221
  message: `Buy ${Number(result.tokensToUser) / 1e6} tokens for ${Number(solAmount) / 1e9} SOL${vaultLabel}${migrateLabel}`,
155
222
  };
@@ -164,8 +231,8 @@ const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount
164
231
  * @returns Unsigned transaction and descriptive message
165
232
  */
166
233
  const buildBuyTransaction = async (connection, params) => {
167
- const { mint, buyer, amount_sol, slippage_bps = 100, vote, message, vault } = params;
168
- return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, vault);
234
+ const { mint, buyer, amount_sol, slippage_bps = 100, vote, message, vault, quote } = params;
235
+ return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, vault, quote);
169
236
  };
170
237
  exports.buildBuyTransaction = buildBuyTransaction;
171
238
  /**
@@ -179,8 +246,8 @@ exports.buildBuyTransaction = buildBuyTransaction;
179
246
  * @returns Unsigned transaction and descriptive message
180
247
  */
181
248
  const buildDirectBuyTransaction = async (connection, params) => {
182
- const { mint, buyer, amount_sol, slippage_bps = 100, vote, message } = params;
183
- return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, undefined);
249
+ const { mint, buyer, amount_sol, slippage_bps = 100, vote, message, quote } = params;
250
+ return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, undefined, quote);
184
251
  };
185
252
  exports.buildDirectBuyTransaction = buildDirectBuyTransaction;
186
253
  // ============================================================================
@@ -194,15 +261,35 @@ exports.buildDirectBuyTransaction = buildDirectBuyTransaction;
194
261
  * @returns Unsigned transaction and descriptive message
195
262
  */
196
263
  const buildSellTransaction = async (connection, params) => {
197
- const { mint: mintStr, seller: sellerStr, amount_tokens, slippage_bps = 100, message, vault: vaultCreatorStr } = params;
264
+ const { mint: mintStr, seller: sellerStr, amount_tokens, slippage_bps = 100, message, vault: vaultCreatorStr, quote } = params;
198
265
  const mint = new web3_js_1.PublicKey(mintStr);
199
266
  const seller = new web3_js_1.PublicKey(sellerStr);
200
267
  const tokenData = await (0, tokens_1.fetchTokenRaw)(connection, mint);
201
268
  if (!tokenData)
202
269
  throw new Error(`Token not found: ${mintStr}`);
203
270
  const { bondingCurve } = tokenData;
204
- if (bondingCurve.bonding_complete)
205
- throw new Error('Bonding curve complete, trade on DEX');
271
+ // Migrated token — route through vault swap on Raydium DEX
272
+ if (quote?.source === 'dex' || bondingCurve.bonding_complete) {
273
+ if (!vaultCreatorStr) {
274
+ throw new Error('Migrated tokens require vault-based trading. Use buildSellTransaction with a vault parameter.');
275
+ }
276
+ const resolvedQuote = quote ?? await (0, quotes_1.getSellQuote)(connection, mintStr, amount_tokens);
277
+ const slippage = slippage_bps ?? 100;
278
+ const minOut = BigInt(resolvedQuote.min_output_sol) * BigInt(10000 - slippage) / BigInt(10000);
279
+ const result = await buildVaultSwapTransaction(connection, {
280
+ mint: mintStr,
281
+ signer: sellerStr,
282
+ vault_creator: vaultCreatorStr,
283
+ amount_in: amount_tokens,
284
+ minimum_amount_out: Number(minOut),
285
+ is_buy: false,
286
+ message,
287
+ });
288
+ return {
289
+ ...result,
290
+ message: `Sell ${amount_tokens / 1e6} tokens for ~${resolvedQuote.output_sol / 1e9} SOL (via DEX)`,
291
+ };
292
+ }
206
293
  // Calculate expected output
207
294
  const virtualSol = BigInt(bondingCurve.virtual_sol_reserves.toString());
208
295
  const virtualTokens = BigInt(bondingCurve.virtual_token_reserves.toString());
@@ -229,20 +316,12 @@ const buildSellTransaction = async (connection, params) => {
229
316
  const userStatsAccount = userStatsInfo ? userStatsPda : null;
230
317
  const bondingCurveTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bondingCurvePda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
231
318
  const sellerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, seller, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
232
- // [V18] Vault accounts (optional — pass null when not using vault)
233
- let torchVaultAccount = null;
234
- let vaultWalletLinkAccount = null;
235
- let vaultTokenAccount = null;
236
- if (vaultCreatorStr) {
237
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
238
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
239
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(seller);
240
- vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
241
- }
319
+ // Vault accounts (optional — pass null when not using vault)
320
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, seller);
321
+ const vaultTokenAccount = torchVaultAccount ? getVaultTokenAta(mint, torchVaultAccount) : null;
242
322
  const tx = new web3_js_1.Transaction();
243
- // Create vault ATA if needed (idempotent — safe for first vault sell on a mint)
244
- if (vaultTokenAccount && torchVaultAccount) {
245
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(seller, vaultTokenAccount, torchVaultAccount, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
323
+ if (torchVaultAccount) {
324
+ tx.add(createVaultTokenAtaIx(seller, mint, torchVaultAccount));
246
325
  }
247
326
  const provider = makeDummyProvider(connection, seller);
248
327
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -268,22 +347,11 @@ const buildSellTransaction = async (connection, params) => {
268
347
  })
269
348
  .instruction();
270
349
  tx.add(sellIx);
271
- // Bundle optional message as SPL Memo instruction
272
- if (message && message.trim().length > 0) {
273
- if (message.length > MAX_MESSAGE_LENGTH) {
274
- throw new Error(`Message must be ${MAX_MESSAGE_LENGTH} characters or less`);
275
- }
276
- const memoIx = new web3_js_1.TransactionInstruction({
277
- programId: constants_1.MEMO_PROGRAM_ID,
278
- keys: [{ pubkey: seller, isSigner: true, isWritable: false }],
279
- data: Buffer.from(message.trim(), 'utf-8'),
280
- });
281
- tx.add(memoIx);
282
- }
283
- await finalizeTransaction(connection, tx, seller);
350
+ addMemoIx(tx, seller, message);
351
+ const versionedTx = await finalizeTransaction(connection, tx, seller);
284
352
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
285
353
  return {
286
- transaction: tx,
354
+ transaction: versionedTx,
287
355
  message: `Sell ${Number(tokenAmount) / 1e6} tokens for ${Number(result.solToUser) / 1e9} SOL${vaultLabel}`,
288
356
  };
289
357
  };
@@ -351,11 +419,11 @@ const buildCreateTokenTransaction = async (connection, params) => {
351
419
  })
352
420
  .instruction();
353
421
  tx.add(createIx);
354
- await finalizeTransaction(connection, tx, creator);
422
+ const versionedTx = await finalizeTransaction(connection, tx, creator);
355
423
  // Partially sign with mint keypair
356
- tx.partialSign(mint);
424
+ versionedTx.sign([mint]);
357
425
  return {
358
- transaction: tx,
426
+ transaction: versionedTx,
359
427
  mint: mint.publicKey,
360
428
  mintKeypair: mint,
361
429
  message: `Create token "${name}" ($${symbol})`,
@@ -391,14 +459,8 @@ const buildStarTransaction = async (connection, params) => {
391
459
  // Derive PDAs
392
460
  const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
393
461
  const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
394
- // [V18] Vault accounts (optional — vault pays star cost)
395
- let torchVaultAccount = null;
396
- let vaultWalletLinkAccount = null;
397
- if (vaultCreatorStr) {
398
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
399
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
400
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(user);
401
- }
462
+ // Vault accounts (optional — vault pays star cost)
463
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, user);
402
464
  const tx = new web3_js_1.Transaction();
403
465
  const provider = makeDummyProvider(connection, user);
404
466
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -417,10 +479,10 @@ const buildStarTransaction = async (connection, params) => {
417
479
  })
418
480
  .instruction();
419
481
  tx.add(starIx);
420
- await finalizeTransaction(connection, tx, user);
482
+ const versionedTx = await finalizeTransaction(connection, tx, user);
421
483
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
422
484
  return {
423
- transaction: tx,
485
+ transaction: versionedTx,
424
486
  message: `Star token (costs 0.05 SOL)${vaultLabel}`,
425
487
  };
426
488
  };
@@ -428,7 +490,6 @@ exports.buildStarTransaction = buildStarTransaction;
428
490
  // ============================================================================
429
491
  // Message
430
492
  // ============================================================================
431
- const MAX_MESSAGE_LENGTH = 500;
432
493
  // ============================================================================
433
494
  // Vault (V2.0)
434
495
  // ============================================================================
@@ -457,9 +518,9 @@ const buildCreateVaultTransaction = async (connection, params) => {
457
518
  })
458
519
  .instruction();
459
520
  const tx = new web3_js_1.Transaction().add(ix);
460
- await finalizeTransaction(connection, tx, creator);
521
+ const versionedTx = await finalizeTransaction(connection, tx, creator);
461
522
  return {
462
- transaction: tx,
523
+ transaction: versionedTx,
463
524
  message: `Create vault for ${params.creator.slice(0, 8)}...`,
464
525
  };
465
526
  };
@@ -488,9 +549,9 @@ const buildDepositVaultTransaction = async (connection, params) => {
488
549
  })
489
550
  .instruction();
490
551
  const tx = new web3_js_1.Transaction().add(ix);
491
- await finalizeTransaction(connection, tx, depositor);
552
+ const versionedTx = await finalizeTransaction(connection, tx, depositor);
492
553
  return {
493
- transaction: tx,
554
+ transaction: versionedTx,
494
555
  message: `Deposit ${params.amount_sol / 1e9} SOL into vault`,
495
556
  };
496
557
  };
@@ -519,9 +580,9 @@ const buildWithdrawVaultTransaction = async (connection, params) => {
519
580
  })
520
581
  .instruction();
521
582
  const tx = new web3_js_1.Transaction().add(ix);
522
- await finalizeTransaction(connection, tx, authority);
583
+ const versionedTx = await finalizeTransaction(connection, tx, authority);
523
584
  return {
524
- transaction: tx,
585
+ transaction: versionedTx,
525
586
  message: `Withdraw ${params.amount_sol / 1e9} SOL from vault`,
526
587
  };
527
588
  };
@@ -554,9 +615,9 @@ const buildLinkWalletTransaction = async (connection, params) => {
554
615
  })
555
616
  .instruction();
556
617
  const tx = new web3_js_1.Transaction().add(ix);
557
- await finalizeTransaction(connection, tx, authority);
618
+ const versionedTx = await finalizeTransaction(connection, tx, authority);
558
619
  return {
559
- transaction: tx,
620
+ transaction: versionedTx,
560
621
  message: `Link wallet ${params.wallet_to_link.slice(0, 8)}... to vault`,
561
622
  };
562
623
  };
@@ -589,9 +650,9 @@ const buildUnlinkWalletTransaction = async (connection, params) => {
589
650
  })
590
651
  .instruction();
591
652
  const tx = new web3_js_1.Transaction().add(ix);
592
- await finalizeTransaction(connection, tx, authority);
653
+ const versionedTx = await finalizeTransaction(connection, tx, authority);
593
654
  return {
594
- transaction: tx,
655
+ transaction: versionedTx,
595
656
  message: `Unlink wallet ${params.wallet_to_unlink.slice(0, 8)}... from vault`,
596
657
  };
597
658
  };
@@ -621,9 +682,9 @@ const buildTransferAuthorityTransaction = async (connection, params) => {
621
682
  })
622
683
  .instruction();
623
684
  const tx = new web3_js_1.Transaction().add(ix);
624
- await finalizeTransaction(connection, tx, authority);
685
+ const versionedTx = await finalizeTransaction(connection, tx, authority);
625
686
  return {
626
- transaction: tx,
687
+ transaction: versionedTx,
627
688
  message: `Transfer vault authority to ${params.new_authority.slice(0, 8)}...`,
628
689
  };
629
690
  };
@@ -653,20 +714,12 @@ const buildBorrowTransaction = async (connection, params) => {
653
714
  const borrowerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, borrower, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
654
715
  // Get Raydium pool accounts for price calculation
655
716
  const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
656
- // [V18] Vault accounts (optional — collateral from vault ATA, SOL to vault)
657
- let torchVaultAccount = null;
658
- let vaultWalletLinkAccount = null;
659
- let vaultTokenAccount = null;
660
- if (vaultCreatorStr) {
661
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
662
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
663
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(borrower);
664
- vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
665
- }
717
+ // Vault accounts (optional — collateral from vault ATA, SOL to vault)
718
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, borrower);
719
+ const vaultTokenAccount = torchVaultAccount ? getVaultTokenAta(mint, torchVaultAccount) : null;
666
720
  const tx = new web3_js_1.Transaction();
667
- // Create vault ATA if needed
668
- if (vaultTokenAccount && torchVaultAccount) {
669
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(borrower, vaultTokenAccount, torchVaultAccount, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
721
+ if (torchVaultAccount) {
722
+ tx.add(createVaultTokenAtaIx(borrower, mint, torchVaultAccount));
670
723
  }
671
724
  const provider = makeDummyProvider(connection, borrower);
672
725
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -694,10 +747,10 @@ const buildBorrowTransaction = async (connection, params) => {
694
747
  })
695
748
  .instruction();
696
749
  tx.add(borrowIx);
697
- await finalizeTransaction(connection, tx, borrower);
750
+ const versionedTx = await finalizeTransaction(connection, tx, borrower);
698
751
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
699
752
  return {
700
- transaction: tx,
753
+ transaction: versionedTx,
701
754
  message: `Borrow ${Number(sol_to_borrow) / 1e9} SOL with ${Number(collateral_amount) / 1e6} tokens as collateral${vaultLabel}`,
702
755
  };
703
756
  };
@@ -724,20 +777,12 @@ const buildRepayTransaction = async (connection, params) => {
724
777
  const [collateralVaultPda] = (0, program_1.getCollateralVaultPda)(mint);
725
778
  const [loanPositionPda] = (0, program_1.getLoanPositionPda)(mint, borrower);
726
779
  const borrowerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, borrower, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
727
- // [V18] Vault accounts (optional — SOL from vault, collateral returns to vault ATA)
728
- let torchVaultAccount = null;
729
- let vaultWalletLinkAccount = null;
730
- let vaultTokenAccount = null;
731
- if (vaultCreatorStr) {
732
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
733
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
734
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(borrower);
735
- vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
736
- }
780
+ // Vault accounts (optional — SOL from vault, collateral returns to vault ATA)
781
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, borrower);
782
+ const vaultTokenAccount = torchVaultAccount ? getVaultTokenAta(mint, torchVaultAccount) : null;
737
783
  const tx = new web3_js_1.Transaction();
738
- // Create vault ATA if needed (collateral returns here)
739
- if (vaultTokenAccount && torchVaultAccount) {
740
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(borrower, vaultTokenAccount, torchVaultAccount, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
784
+ if (torchVaultAccount) {
785
+ tx.add(createVaultTokenAtaIx(borrower, mint, torchVaultAccount));
741
786
  }
742
787
  const provider = makeDummyProvider(connection, borrower);
743
788
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -758,10 +803,10 @@ const buildRepayTransaction = async (connection, params) => {
758
803
  })
759
804
  .instruction();
760
805
  tx.add(repayIx);
761
- await finalizeTransaction(connection, tx, borrower);
806
+ const versionedTx = await finalizeTransaction(connection, tx, borrower);
762
807
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
763
808
  return {
764
- transaction: tx,
809
+ transaction: versionedTx,
765
810
  message: `Repay ${Number(sol_amount) / 1e9} SOL${vaultLabel}`,
766
811
  };
767
812
  };
@@ -792,22 +837,14 @@ const buildLiquidateTransaction = async (connection, params) => {
792
837
  const liquidatorTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, liquidator, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
793
838
  // Get Raydium pool accounts for price calculation
794
839
  const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
795
- // [V20] Vault accounts (optional — SOL from vault, collateral to vault ATA)
796
- let torchVaultAccount = null;
797
- let vaultWalletLinkAccount = null;
798
- let vaultTokenAccount = null;
799
- if (vaultCreatorStr) {
800
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
801
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
802
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(liquidator);
803
- vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
804
- }
840
+ // Vault accounts (optional — SOL from vault, collateral to vault ATA)
841
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, liquidator);
842
+ const vaultTokenAccount = torchVaultAccount ? getVaultTokenAta(mint, torchVaultAccount) : null;
805
843
  const tx = new web3_js_1.Transaction();
806
844
  // Create liquidator ATA if needed
807
845
  tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(liquidator, liquidatorTokenAccount, liquidator, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
808
- // Create vault ATA if needed (collateral goes here)
809
- if (vaultTokenAccount && torchVaultAccount) {
810
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(liquidator, vaultTokenAccount, torchVaultAccount, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
846
+ if (torchVaultAccount) {
847
+ tx.add(createVaultTokenAtaIx(liquidator, mint, torchVaultAccount));
811
848
  }
812
849
  const provider = makeDummyProvider(connection, liquidator);
813
850
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -834,10 +871,10 @@ const buildLiquidateTransaction = async (connection, params) => {
834
871
  })
835
872
  .instruction();
836
873
  tx.add(liquidateIx);
837
- await finalizeTransaction(connection, tx, liquidator);
874
+ const versionedTx = await finalizeTransaction(connection, tx, liquidator);
838
875
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
839
876
  return {
840
- transaction: tx,
877
+ transaction: versionedTx,
841
878
  message: `Liquidate loan position for ${borrowerStr.slice(0, 8)}...${vaultLabel}`,
842
879
  };
843
880
  };
@@ -861,14 +898,8 @@ const buildClaimProtocolRewardsTransaction = async (connection, params) => {
861
898
  // Derive PDAs
862
899
  const [userStatsPda] = (0, program_1.getUserStatsPda)(user);
863
900
  const [protocolTreasuryPda] = (0, program_1.getProtocolTreasuryPda)();
864
- // [V20] Vault accounts (optional — rewards go to vault instead of user)
865
- let torchVaultAccount = null;
866
- let vaultWalletLinkAccount = null;
867
- if (vaultCreatorStr) {
868
- const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
869
- [torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
870
- [vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(user);
871
- }
901
+ // Vault accounts (optional — rewards go to vault instead of user)
902
+ const { torchVault: torchVaultAccount, walletLink: vaultWalletLinkAccount } = deriveVaultAccounts(vaultCreatorStr, user);
872
903
  const tx = new web3_js_1.Transaction();
873
904
  const provider = makeDummyProvider(connection, user);
874
905
  const program = new anchor_1.Program(torch_market_json_1.default, provider);
@@ -884,10 +915,10 @@ const buildClaimProtocolRewardsTransaction = async (connection, params) => {
884
915
  })
885
916
  .instruction();
886
917
  tx.add(claimIx);
887
- await finalizeTransaction(connection, tx, user);
918
+ const versionedTx = await finalizeTransaction(connection, tx, user);
888
919
  const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
889
920
  return {
890
- transaction: tx,
921
+ transaction: versionedTx,
891
922
  message: `Claim protocol rewards${vaultLabel}`,
892
923
  };
893
924
  };
@@ -924,9 +955,9 @@ const buildReclaimFailedTokenTransaction = async (connection, params) => {
924
955
  })
925
956
  .instruction();
926
957
  tx.add(ix);
927
- await finalizeTransaction(connection, tx, payer);
958
+ const versionedTx = await finalizeTransaction(connection, tx, payer);
928
959
  return {
929
- transaction: tx,
960
+ transaction: versionedTx,
930
961
  message: `Reclaim failed token ${mintStr.slice(0, 8)}...`,
931
962
  };
932
963
  };
@@ -969,9 +1000,9 @@ const buildWithdrawTokensTransaction = async (connection, params) => {
969
1000
  })
970
1001
  .instruction();
971
1002
  tx.add(ix);
972
- await finalizeTransaction(connection, tx, authority);
1003
+ const versionedTx = await finalizeTransaction(connection, tx, authority);
973
1004
  return {
974
- transaction: tx,
1005
+ transaction: versionedTx,
975
1006
  message: `Withdraw ${params.amount} tokens from vault to ${params.destination.slice(0, 8)}...`,
976
1007
  };
977
1008
  };
@@ -1075,9 +1106,9 @@ const buildMigrateTransaction = async (connection, params) => {
1075
1106
  })
1076
1107
  .instruction();
1077
1108
  tx.add(fundIx, migrateIx);
1078
- await finalizeTransaction(connection, tx, payer);
1109
+ const versionedTx = await finalizeTransaction(connection, tx, payer);
1079
1110
  return {
1080
- transaction: tx,
1111
+ transaction: versionedTx,
1081
1112
  message: `Migrate token ${mintStr.slice(0, 8)}... to Raydium DEX`,
1082
1113
  };
1083
1114
  };
@@ -1156,30 +1187,18 @@ const buildVaultSwapTransaction = async (connection, params) => {
1156
1187
  })
1157
1188
  .instruction();
1158
1189
  tx.add(swapIx);
1159
- // Bundle optional message as SPL Memo instruction
1160
- // Vault swap txs are large (~868 bytes base), so truncate message to fit
1161
- // within the 1232-byte legacy transaction limit
1162
- if (message && message.trim().length > 0) {
1163
- const MAX_VAULT_SWAP_MESSAGE = 280;
1164
- const trimmed = message.trim().slice(0, MAX_VAULT_SWAP_MESSAGE);
1165
- const memoIx = new web3_js_1.TransactionInstruction({
1166
- programId: constants_1.MEMO_PROGRAM_ID,
1167
- keys: [{ pubkey: signer, isSigner: true, isWritable: false }],
1168
- data: Buffer.from(trimmed, 'utf-8'),
1169
- });
1170
- tx.add(memoIx);
1171
- }
1172
- await finalizeTransaction(connection, tx, signer);
1190
+ // Vault swap txs are larger, so truncate message to 280 chars
1191
+ addMemoIx(tx, signer, message, 280);
1192
+ const versionedTx = await finalizeTransaction(connection, tx, signer);
1173
1193
  const direction = is_buy ? 'Buy' : 'Sell';
1174
1194
  const amountLabel = is_buy
1175
1195
  ? `${amount_in / 1e9} SOL`
1176
1196
  : `${amount_in / 1e6} tokens`;
1177
1197
  return {
1178
- transaction: tx,
1198
+ transaction: versionedTx,
1179
1199
  message: `${direction} ${amountLabel} via vault DEX swap`,
1180
1200
  };
1181
1201
  };
1182
- exports.buildVaultSwapTransaction = buildVaultSwapTransaction;
1183
1202
  // ============================================================================
1184
1203
  // Treasury Cranks
1185
1204
  // ============================================================================
@@ -1262,9 +1281,9 @@ const buildHarvestFeesTransaction = async (connection, params) => {
1262
1281
  })))
1263
1282
  .instruction();
1264
1283
  tx.add(harvestIx);
1265
- await finalizeTransaction(connection, tx, payer);
1284
+ const versionedTx = await finalizeTransaction(connection, tx, payer);
1266
1285
  return {
1267
- transaction: tx,
1286
+ transaction: versionedTx,
1268
1287
  message: `Harvest transfer fees for ${mintStr.slice(0, 8)}... (${sourceAccounts.length} source accounts)`,
1269
1288
  };
1270
1289
  };
@@ -1389,11 +1408,11 @@ const buildSwapFeesToSolTransaction = async (connection, params) => {
1389
1408
  tx.add(await buildHarvestIx(sourceAccounts));
1390
1409
  }
1391
1410
  tx.add(await buildSwapIx());
1392
- await finalizeTransaction(connection, tx, payer);
1411
+ const versionedTx = await finalizeTransaction(connection, tx, payer);
1393
1412
  // Check if it fits in a single transaction
1394
1413
  let fitsInSingleTx = false;
1395
1414
  try {
1396
- const serialized = tx.serialize({ requireAllSignatures: false, verifySignatures: false });
1415
+ const serialized = versionedTx.serialize();
1397
1416
  fitsInSingleTx = serialized.length <= PACKET_DATA_SIZE;
1398
1417
  }
1399
1418
  catch {
@@ -1401,7 +1420,7 @@ const buildSwapFeesToSolTransaction = async (connection, params) => {
1401
1420
  }
1402
1421
  if (fitsInSingleTx) {
1403
1422
  return {
1404
- transaction: tx,
1423
+ transaction: versionedTx,
1405
1424
  message: `Swap harvested fees to SOL for ${mintStr.slice(0, 8)}...${harvest ? ' (harvest + swap)' : ''}`,
1406
1425
  };
1407
1426
  }
@@ -1411,16 +1430,16 @@ const buildSwapFeesToSolTransaction = async (connection, params) => {
1411
1430
  const computeUnits = 200000 + 20000 * sourceAccounts.length;
1412
1431
  harvestTx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits }));
1413
1432
  harvestTx.add(await buildHarvestIx(sourceAccounts));
1414
- await finalizeTransaction(connection, harvestTx, payer);
1433
+ const versionedHarvestTx = await finalizeTransaction(connection, harvestTx, payer);
1415
1434
  // Transaction 2: swap only (no harvest — tokens already in treasury ATA)
1416
1435
  const swapTx = new web3_js_1.Transaction();
1417
1436
  swapTx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }));
1418
1437
  swapTx.add(createWsolAtaIx);
1419
1438
  swapTx.add(await buildSwapIx());
1420
- await finalizeTransaction(connection, swapTx, payer);
1439
+ const versionedSwapTx = await finalizeTransaction(connection, swapTx, payer);
1421
1440
  return {
1422
- transaction: harvestTx,
1423
- additionalTransactions: [swapTx],
1441
+ transaction: versionedHarvestTx,
1442
+ additionalTransactions: [versionedSwapTx],
1424
1443
  message: `Harvest + swap fees to SOL for ${mintStr.slice(0, 8)}... (split: ${sourceAccounts.length} sources)`,
1425
1444
  };
1426
1445
  };