torchsdk 3.5.0 → 3.5.1
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.
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +52 -0
- package/dist/constants.js.map +1 -0
- package/dist/ephemeral.d.ts +33 -0
- package/dist/ephemeral.d.ts.map +1 -0
- package/dist/ephemeral.js +39 -0
- package/dist/ephemeral.js.map +1 -0
- package/dist/gateway.d.ts +17 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +48 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/program.d.ts +150 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +216 -0
- package/dist/program.js.map +1 -0
- package/dist/quotes.d.ts +16 -0
- package/dist/quotes.d.ts.map +1 -0
- package/dist/quotes.js +79 -0
- package/dist/quotes.js.map +1 -0
- package/dist/said.d.ts +27 -0
- package/dist/said.d.ts.map +1 -0
- package/dist/said.js +89 -0
- package/dist/said.js.map +1 -0
- package/dist/tokens.d.ts +64 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +650 -0
- package/dist/tokens.js.map +1 -0
- package/dist/torch_market.json +6537 -0
- package/dist/transactions.d.ts +183 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/transactions.js +994 -0
- package/dist/transactions.js.map +1 -0
- package/dist/types.d.ts +283 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Transaction builders
|
|
4
|
+
*
|
|
5
|
+
* Build unsigned transactions for buy, sell, create, star, vault, and lending.
|
|
6
|
+
* Agents sign these locally and submit to the network.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.buildVaultSwapTransaction = exports.buildWithdrawTokensTransaction = 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;
|
|
13
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
14
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
15
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
16
|
+
const program_1 = require("./program");
|
|
17
|
+
const constants_1 = require("./constants");
|
|
18
|
+
const tokens_1 = require("./tokens");
|
|
19
|
+
const torch_market_json_1 = __importDefault(require("./torch_market.json"));
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Helpers
|
|
22
|
+
// ============================================================================
|
|
23
|
+
const makeDummyProvider = (connection, payer) => {
|
|
24
|
+
const dummyWallet = {
|
|
25
|
+
publicKey: payer,
|
|
26
|
+
signTransaction: async (t) => t,
|
|
27
|
+
signAllTransactions: async (t) => t,
|
|
28
|
+
};
|
|
29
|
+
return new anchor_1.AnchorProvider(connection, dummyWallet, {});
|
|
30
|
+
};
|
|
31
|
+
const finalizeTransaction = async (connection, tx, feePayer) => {
|
|
32
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
33
|
+
tx.recentBlockhash = blockhash;
|
|
34
|
+
tx.feePayer = feePayer;
|
|
35
|
+
};
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Buy
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Internal buy builder shared by both vault and direct variants
|
|
40
|
+
const buildBuyTransactionInternal = async (connection, mintStr, buyerStr, amount_sol, slippage_bps, vote, message, vaultCreatorStr) => {
|
|
41
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
42
|
+
const buyer = new web3_js_1.PublicKey(buyerStr);
|
|
43
|
+
const tokenData = await (0, tokens_1.fetchTokenRaw)(connection, mint);
|
|
44
|
+
if (!tokenData)
|
|
45
|
+
throw new Error(`Token not found: ${mintStr}`);
|
|
46
|
+
const { bondingCurve } = tokenData;
|
|
47
|
+
if (bondingCurve.bonding_complete)
|
|
48
|
+
throw new Error('Bonding curve complete, trade on DEX');
|
|
49
|
+
// Calculate expected output
|
|
50
|
+
const virtualSol = BigInt(bondingCurve.virtual_sol_reserves.toString());
|
|
51
|
+
const virtualTokens = BigInt(bondingCurve.virtual_token_reserves.toString());
|
|
52
|
+
const realSol = BigInt(bondingCurve.real_sol_reserves.toString());
|
|
53
|
+
const bondingTarget = BigInt(bondingCurve.bonding_target.toString());
|
|
54
|
+
const solAmount = BigInt(amount_sol);
|
|
55
|
+
const result = (0, program_1.calculateTokensOut)(solAmount, virtualSol, virtualTokens, realSol, 100, 100, bondingTarget);
|
|
56
|
+
// Apply slippage
|
|
57
|
+
if (slippage_bps < 10 || slippage_bps > 1000) {
|
|
58
|
+
throw new Error(`slippage_bps must be between 10 (0.1%) and 1000 (10%), got ${slippage_bps}`);
|
|
59
|
+
}
|
|
60
|
+
const slippage = slippage_bps;
|
|
61
|
+
const minTokens = (result.tokensToUser * BigInt(10000 - slippage)) / BigInt(10000);
|
|
62
|
+
// Derive PDAs
|
|
63
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
64
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
65
|
+
const [userPositionPda] = (0, program_1.getUserPositionPda)(bondingCurvePda, buyer);
|
|
66
|
+
const [userStatsPda] = (0, program_1.getUserStatsPda)(buyer);
|
|
67
|
+
const [globalConfigPda] = (0, program_1.getGlobalConfigPda)();
|
|
68
|
+
const [protocolTreasuryPda] = (0, program_1.getProtocolTreasuryPda)();
|
|
69
|
+
const bondingCurveTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bondingCurvePda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
70
|
+
const treasuryTokenAccount = (0, program_1.getTreasuryTokenAccount)(mint, treasuryPda);
|
|
71
|
+
const buyerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, buyer, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
72
|
+
const tx = new web3_js_1.Transaction();
|
|
73
|
+
// Create buyer ATA if needed
|
|
74
|
+
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(buyer, buyerTokenAccount, buyer, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
75
|
+
const provider = makeDummyProvider(connection, buyer);
|
|
76
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
77
|
+
// Fetch global config for dev wallet
|
|
78
|
+
const globalConfigAccount = (await program.account.globalConfig.fetch(globalConfigPda));
|
|
79
|
+
// Vault accounts (optional — pass null when not using vault)
|
|
80
|
+
let torchVaultAccount = null;
|
|
81
|
+
let vaultWalletLinkAccount = null;
|
|
82
|
+
let vaultTokenAccount = null;
|
|
83
|
+
if (vaultCreatorStr) {
|
|
84
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
85
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
86
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(buyer);
|
|
87
|
+
// [V18] Tokens go to vault ATA instead of buyer's wallet
|
|
88
|
+
vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
89
|
+
// Create vault ATA if needed (vault PDA owns it)
|
|
90
|
+
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));
|
|
91
|
+
}
|
|
92
|
+
const buyIx = await program.methods
|
|
93
|
+
.buy({
|
|
94
|
+
solAmount: new anchor_1.BN(amount_sol.toString()),
|
|
95
|
+
minTokensOut: new anchor_1.BN(minTokens.toString()),
|
|
96
|
+
vote: vote === 'return' ? true : vote === 'burn' ? false : null,
|
|
97
|
+
})
|
|
98
|
+
.accounts({
|
|
99
|
+
buyer,
|
|
100
|
+
globalConfig: globalConfigPda,
|
|
101
|
+
devWallet: globalConfigAccount.devWallet || globalConfigAccount.dev_wallet,
|
|
102
|
+
protocolTreasury: protocolTreasuryPda,
|
|
103
|
+
mint,
|
|
104
|
+
bondingCurve: bondingCurvePda,
|
|
105
|
+
tokenVault: bondingCurveTokenAccount,
|
|
106
|
+
tokenTreasury: treasuryPda,
|
|
107
|
+
treasuryTokenAccount,
|
|
108
|
+
buyerTokenAccount,
|
|
109
|
+
userPosition: userPositionPda,
|
|
110
|
+
userStats: userStatsPda,
|
|
111
|
+
torchVault: torchVaultAccount,
|
|
112
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
113
|
+
vaultTokenAccount,
|
|
114
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
115
|
+
associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
116
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
117
|
+
})
|
|
118
|
+
.instruction();
|
|
119
|
+
tx.add(buyIx);
|
|
120
|
+
// Bundle optional message as SPL Memo instruction
|
|
121
|
+
if (message && message.trim().length > 0) {
|
|
122
|
+
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
123
|
+
throw new Error(`Message must be ${MAX_MESSAGE_LENGTH} characters or less`);
|
|
124
|
+
}
|
|
125
|
+
const memoIx = new web3_js_1.TransactionInstruction({
|
|
126
|
+
programId: constants_1.MEMO_PROGRAM_ID,
|
|
127
|
+
keys: [{ pubkey: buyer, isSigner: true, isWritable: false }],
|
|
128
|
+
data: Buffer.from(message.trim(), 'utf-8'),
|
|
129
|
+
});
|
|
130
|
+
tx.add(memoIx);
|
|
131
|
+
}
|
|
132
|
+
await finalizeTransaction(connection, tx, buyer);
|
|
133
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
134
|
+
return {
|
|
135
|
+
transaction: tx,
|
|
136
|
+
message: `Buy ${Number(result.tokensToUser) / 1e6} tokens for ${Number(solAmount) / 1e9} SOL${vaultLabel}`,
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Build an unsigned vault-funded buy transaction.
|
|
141
|
+
*
|
|
142
|
+
* The vault pays for the buy. This is the recommended path for AI agents.
|
|
143
|
+
*
|
|
144
|
+
* @param connection - Solana RPC connection
|
|
145
|
+
* @param params - Buy parameters with required vault creator pubkey
|
|
146
|
+
* @returns Unsigned transaction and descriptive message
|
|
147
|
+
*/
|
|
148
|
+
const buildBuyTransaction = async (connection, params) => {
|
|
149
|
+
const { mint, buyer, amount_sol, slippage_bps = 100, vote, message, vault } = params;
|
|
150
|
+
return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, vault);
|
|
151
|
+
};
|
|
152
|
+
exports.buildBuyTransaction = buildBuyTransaction;
|
|
153
|
+
/**
|
|
154
|
+
* Build an unsigned direct buy transaction (no vault).
|
|
155
|
+
*
|
|
156
|
+
* The buyer pays from their own wallet. Use this for human-operated wallets only.
|
|
157
|
+
* For AI agents, use buildBuyTransaction with a vault instead.
|
|
158
|
+
*
|
|
159
|
+
* @param connection - Solana RPC connection
|
|
160
|
+
* @param params - Buy parameters (no vault)
|
|
161
|
+
* @returns Unsigned transaction and descriptive message
|
|
162
|
+
*/
|
|
163
|
+
const buildDirectBuyTransaction = async (connection, params) => {
|
|
164
|
+
const { mint, buyer, amount_sol, slippage_bps = 100, vote, message } = params;
|
|
165
|
+
return buildBuyTransactionInternal(connection, mint, buyer, amount_sol, slippage_bps, vote, message, undefined);
|
|
166
|
+
};
|
|
167
|
+
exports.buildDirectBuyTransaction = buildDirectBuyTransaction;
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Sell
|
|
170
|
+
// ============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Build an unsigned sell transaction.
|
|
173
|
+
*
|
|
174
|
+
* @param connection - Solana RPC connection
|
|
175
|
+
* @param params - Sell parameters (mint, seller, amount_tokens in raw units, optional slippage_bps)
|
|
176
|
+
* @returns Unsigned transaction and descriptive message
|
|
177
|
+
*/
|
|
178
|
+
const buildSellTransaction = async (connection, params) => {
|
|
179
|
+
const { mint: mintStr, seller: sellerStr, amount_tokens, slippage_bps = 100, message, vault: vaultCreatorStr } = params;
|
|
180
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
181
|
+
const seller = new web3_js_1.PublicKey(sellerStr);
|
|
182
|
+
const tokenData = await (0, tokens_1.fetchTokenRaw)(connection, mint);
|
|
183
|
+
if (!tokenData)
|
|
184
|
+
throw new Error(`Token not found: ${mintStr}`);
|
|
185
|
+
const { bondingCurve } = tokenData;
|
|
186
|
+
if (bondingCurve.bonding_complete)
|
|
187
|
+
throw new Error('Bonding curve complete, trade on DEX');
|
|
188
|
+
// Calculate expected output
|
|
189
|
+
const virtualSol = BigInt(bondingCurve.virtual_sol_reserves.toString());
|
|
190
|
+
const virtualTokens = BigInt(bondingCurve.virtual_token_reserves.toString());
|
|
191
|
+
const tokenAmount = BigInt(amount_tokens);
|
|
192
|
+
const result = (0, program_1.calculateSolOut)(tokenAmount, virtualSol, virtualTokens);
|
|
193
|
+
// Apply slippage
|
|
194
|
+
if (slippage_bps < 10 || slippage_bps > 1000) {
|
|
195
|
+
throw new Error(`slippage_bps must be between 10 (0.1%) and 1000 (10%), got ${slippage_bps}`);
|
|
196
|
+
}
|
|
197
|
+
const slippage = slippage_bps;
|
|
198
|
+
const minSol = (result.solToUser * BigInt(10000 - slippage)) / BigInt(10000);
|
|
199
|
+
// Derive PDAs
|
|
200
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
201
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
202
|
+
const [userPositionPda] = (0, program_1.getUserPositionPda)(bondingCurvePda, seller);
|
|
203
|
+
const [userStatsPda] = (0, program_1.getUserStatsPda)(seller);
|
|
204
|
+
const bondingCurveTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bondingCurvePda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
205
|
+
const sellerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, seller, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
206
|
+
// [V18] Vault accounts (optional — pass null when not using vault)
|
|
207
|
+
let torchVaultAccount = null;
|
|
208
|
+
let vaultWalletLinkAccount = null;
|
|
209
|
+
let vaultTokenAccount = null;
|
|
210
|
+
if (vaultCreatorStr) {
|
|
211
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
212
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
213
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(seller);
|
|
214
|
+
vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
215
|
+
}
|
|
216
|
+
const tx = new web3_js_1.Transaction();
|
|
217
|
+
// Create vault ATA if needed (idempotent — safe for first vault sell on a mint)
|
|
218
|
+
if (vaultTokenAccount && torchVaultAccount) {
|
|
219
|
+
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));
|
|
220
|
+
}
|
|
221
|
+
const provider = makeDummyProvider(connection, seller);
|
|
222
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
223
|
+
const sellIx = await program.methods
|
|
224
|
+
.sell({
|
|
225
|
+
tokenAmount: new anchor_1.BN(amount_tokens.toString()),
|
|
226
|
+
minSolOut: new anchor_1.BN(minSol.toString()),
|
|
227
|
+
})
|
|
228
|
+
.accounts({
|
|
229
|
+
seller,
|
|
230
|
+
mint,
|
|
231
|
+
bondingCurve: bondingCurvePda,
|
|
232
|
+
tokenVault: bondingCurveTokenAccount,
|
|
233
|
+
sellerTokenAccount,
|
|
234
|
+
userPosition: userPositionPda,
|
|
235
|
+
tokenTreasury: treasuryPda,
|
|
236
|
+
userStats: userStatsPda,
|
|
237
|
+
torchVault: torchVaultAccount,
|
|
238
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
239
|
+
vaultTokenAccount,
|
|
240
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
241
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
242
|
+
})
|
|
243
|
+
.instruction();
|
|
244
|
+
tx.add(sellIx);
|
|
245
|
+
// Bundle optional message as SPL Memo instruction
|
|
246
|
+
if (message && message.trim().length > 0) {
|
|
247
|
+
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
248
|
+
throw new Error(`Message must be ${MAX_MESSAGE_LENGTH} characters or less`);
|
|
249
|
+
}
|
|
250
|
+
const memoIx = new web3_js_1.TransactionInstruction({
|
|
251
|
+
programId: constants_1.MEMO_PROGRAM_ID,
|
|
252
|
+
keys: [{ pubkey: seller, isSigner: true, isWritable: false }],
|
|
253
|
+
data: Buffer.from(message.trim(), 'utf-8'),
|
|
254
|
+
});
|
|
255
|
+
tx.add(memoIx);
|
|
256
|
+
}
|
|
257
|
+
await finalizeTransaction(connection, tx, seller);
|
|
258
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
259
|
+
return {
|
|
260
|
+
transaction: tx,
|
|
261
|
+
message: `Sell ${Number(tokenAmount) / 1e6} tokens for ${Number(result.solToUser) / 1e9} SOL${vaultLabel}`,
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
exports.buildSellTransaction = buildSellTransaction;
|
|
265
|
+
// ============================================================================
|
|
266
|
+
// Create Token
|
|
267
|
+
// ============================================================================
|
|
268
|
+
/**
|
|
269
|
+
* Build an unsigned create token transaction.
|
|
270
|
+
*
|
|
271
|
+
* Returns the transaction (partially signed by the mint keypair) and the mint keypair
|
|
272
|
+
* so the agent can extract the mint address.
|
|
273
|
+
*
|
|
274
|
+
* @param connection - Solana RPC connection
|
|
275
|
+
* @param params - Create parameters (creator, name, symbol, metadata_uri)
|
|
276
|
+
* @returns Partially-signed transaction, mint PublicKey, and mint Keypair
|
|
277
|
+
*/
|
|
278
|
+
const buildCreateTokenTransaction = async (connection, params) => {
|
|
279
|
+
const { creator: creatorStr, name, symbol, metadata_uri, sol_target = 0 } = params;
|
|
280
|
+
const creator = new web3_js_1.PublicKey(creatorStr);
|
|
281
|
+
if (name.length > 32)
|
|
282
|
+
throw new Error('Name must be 32 characters or less');
|
|
283
|
+
if (symbol.length > 10)
|
|
284
|
+
throw new Error('Symbol must be 10 characters or less');
|
|
285
|
+
// Grind for vanity "tm" suffix
|
|
286
|
+
let mint;
|
|
287
|
+
const maxAttempts = 500000;
|
|
288
|
+
let attempts = 0;
|
|
289
|
+
while (true) {
|
|
290
|
+
mint = web3_js_1.Keypair.generate();
|
|
291
|
+
attempts++;
|
|
292
|
+
if (mint.publicKey.toBase58().endsWith('tm'))
|
|
293
|
+
break;
|
|
294
|
+
if (attempts >= maxAttempts)
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
// Derive PDAs
|
|
298
|
+
const [globalConfig] = (0, program_1.getGlobalConfigPda)();
|
|
299
|
+
const [bondingCurve] = (0, program_1.getBondingCurvePda)(mint.publicKey);
|
|
300
|
+
const [treasury] = (0, program_1.getTokenTreasuryPda)(mint.publicKey);
|
|
301
|
+
const bondingCurveTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint.publicKey, bondingCurve, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
302
|
+
const treasuryTokenAccount = (0, program_1.getTreasuryTokenAccount)(mint.publicKey, treasury);
|
|
303
|
+
const tx = new web3_js_1.Transaction();
|
|
304
|
+
const provider = makeDummyProvider(connection, creator);
|
|
305
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
306
|
+
const createIx = await program.methods
|
|
307
|
+
.createToken({ name, symbol, uri: metadata_uri, solTarget: new anchor_1.BN(sol_target) })
|
|
308
|
+
.accounts({
|
|
309
|
+
creator,
|
|
310
|
+
globalConfig,
|
|
311
|
+
mint: mint.publicKey,
|
|
312
|
+
bondingCurve,
|
|
313
|
+
tokenVault: bondingCurveTokenAccount,
|
|
314
|
+
treasury,
|
|
315
|
+
treasuryTokenAccount,
|
|
316
|
+
token2022Program: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
317
|
+
associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
318
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
319
|
+
rent: web3_js_1.SYSVAR_RENT_PUBKEY,
|
|
320
|
+
})
|
|
321
|
+
.instruction();
|
|
322
|
+
tx.add(createIx);
|
|
323
|
+
await finalizeTransaction(connection, tx, creator);
|
|
324
|
+
// Partially sign with mint keypair
|
|
325
|
+
tx.partialSign(mint);
|
|
326
|
+
return {
|
|
327
|
+
transaction: tx,
|
|
328
|
+
mint: mint.publicKey,
|
|
329
|
+
mintKeypair: mint,
|
|
330
|
+
message: `Create token "${name}" ($${symbol})`,
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
exports.buildCreateTokenTransaction = buildCreateTokenTransaction;
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Star
|
|
336
|
+
// ============================================================================
|
|
337
|
+
/**
|
|
338
|
+
* Build an unsigned star transaction (costs 0.05 SOL).
|
|
339
|
+
*
|
|
340
|
+
* @param connection - Solana RPC connection
|
|
341
|
+
* @param params - Star parameters (mint, user)
|
|
342
|
+
* @returns Unsigned transaction and descriptive message
|
|
343
|
+
*/
|
|
344
|
+
const buildStarTransaction = async (connection, params) => {
|
|
345
|
+
const { mint: mintStr, user: userStr, vault: vaultCreatorStr } = params;
|
|
346
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
347
|
+
const user = new web3_js_1.PublicKey(userStr);
|
|
348
|
+
const tokenData = await (0, tokens_1.fetchTokenRaw)(connection, mint);
|
|
349
|
+
if (!tokenData)
|
|
350
|
+
throw new Error(`Token not found: ${mintStr}`);
|
|
351
|
+
const { bondingCurve } = tokenData;
|
|
352
|
+
if (user.equals(bondingCurve.creator)) {
|
|
353
|
+
throw new Error('Cannot star your own token');
|
|
354
|
+
}
|
|
355
|
+
// Check if already starred
|
|
356
|
+
const [starRecordPda] = (0, program_1.getStarRecordPda)(user, mint);
|
|
357
|
+
const starRecord = await connection.getAccountInfo(starRecordPda);
|
|
358
|
+
if (starRecord)
|
|
359
|
+
throw new Error('Already starred this token');
|
|
360
|
+
// Derive PDAs
|
|
361
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
362
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
363
|
+
// [V18] Vault accounts (optional — vault pays star cost)
|
|
364
|
+
let torchVaultAccount = null;
|
|
365
|
+
let vaultWalletLinkAccount = null;
|
|
366
|
+
if (vaultCreatorStr) {
|
|
367
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
368
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
369
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(user);
|
|
370
|
+
}
|
|
371
|
+
const tx = new web3_js_1.Transaction();
|
|
372
|
+
const provider = makeDummyProvider(connection, user);
|
|
373
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
374
|
+
const starIx = await program.methods
|
|
375
|
+
.starToken()
|
|
376
|
+
.accounts({
|
|
377
|
+
user,
|
|
378
|
+
mint,
|
|
379
|
+
bondingCurve: bondingCurvePda,
|
|
380
|
+
tokenTreasury: treasuryPda,
|
|
381
|
+
creator: bondingCurve.creator,
|
|
382
|
+
starRecord: starRecordPda,
|
|
383
|
+
torchVault: torchVaultAccount,
|
|
384
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
385
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
386
|
+
})
|
|
387
|
+
.instruction();
|
|
388
|
+
tx.add(starIx);
|
|
389
|
+
await finalizeTransaction(connection, tx, user);
|
|
390
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
391
|
+
return {
|
|
392
|
+
transaction: tx,
|
|
393
|
+
message: `Star token (costs 0.05 SOL)${vaultLabel}`,
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
exports.buildStarTransaction = buildStarTransaction;
|
|
397
|
+
// ============================================================================
|
|
398
|
+
// Message
|
|
399
|
+
// ============================================================================
|
|
400
|
+
const MAX_MESSAGE_LENGTH = 500;
|
|
401
|
+
// ============================================================================
|
|
402
|
+
// Vault (V2.0)
|
|
403
|
+
// ============================================================================
|
|
404
|
+
/**
|
|
405
|
+
* Build an unsigned create vault transaction.
|
|
406
|
+
*
|
|
407
|
+
* Creates a TorchVault PDA and auto-links the creator's wallet.
|
|
408
|
+
*
|
|
409
|
+
* @param connection - Solana RPC connection
|
|
410
|
+
* @param params - Creator public key
|
|
411
|
+
* @returns Unsigned transaction
|
|
412
|
+
*/
|
|
413
|
+
const buildCreateVaultTransaction = async (connection, params) => {
|
|
414
|
+
const creator = new web3_js_1.PublicKey(params.creator);
|
|
415
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(creator);
|
|
416
|
+
const [walletLinkPda] = (0, program_1.getVaultWalletLinkPda)(creator);
|
|
417
|
+
const provider = makeDummyProvider(connection, creator);
|
|
418
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
419
|
+
const ix = await program.methods
|
|
420
|
+
.createVault()
|
|
421
|
+
.accounts({
|
|
422
|
+
creator,
|
|
423
|
+
vault: vaultPda,
|
|
424
|
+
walletLink: walletLinkPda,
|
|
425
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
426
|
+
})
|
|
427
|
+
.instruction();
|
|
428
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
429
|
+
await finalizeTransaction(connection, tx, creator);
|
|
430
|
+
return {
|
|
431
|
+
transaction: tx,
|
|
432
|
+
message: `Create vault for ${params.creator.slice(0, 8)}...`,
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
exports.buildCreateVaultTransaction = buildCreateVaultTransaction;
|
|
436
|
+
/**
|
|
437
|
+
* Build an unsigned deposit vault transaction.
|
|
438
|
+
*
|
|
439
|
+
* Anyone can deposit SOL into any vault.
|
|
440
|
+
*
|
|
441
|
+
* @param connection - Solana RPC connection
|
|
442
|
+
* @param params - Depositor, vault creator, amount in lamports
|
|
443
|
+
* @returns Unsigned transaction
|
|
444
|
+
*/
|
|
445
|
+
const buildDepositVaultTransaction = async (connection, params) => {
|
|
446
|
+
const depositor = new web3_js_1.PublicKey(params.depositor);
|
|
447
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
448
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
449
|
+
const provider = makeDummyProvider(connection, depositor);
|
|
450
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
451
|
+
const ix = await program.methods
|
|
452
|
+
.depositVault(new anchor_1.BN(params.amount_sol.toString()))
|
|
453
|
+
.accounts({
|
|
454
|
+
depositor,
|
|
455
|
+
vault: vaultPda,
|
|
456
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
457
|
+
})
|
|
458
|
+
.instruction();
|
|
459
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
460
|
+
await finalizeTransaction(connection, tx, depositor);
|
|
461
|
+
return {
|
|
462
|
+
transaction: tx,
|
|
463
|
+
message: `Deposit ${params.amount_sol / 1e9} SOL into vault`,
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
exports.buildDepositVaultTransaction = buildDepositVaultTransaction;
|
|
467
|
+
/**
|
|
468
|
+
* Build an unsigned withdraw vault transaction.
|
|
469
|
+
*
|
|
470
|
+
* Only the vault authority can withdraw.
|
|
471
|
+
*
|
|
472
|
+
* @param connection - Solana RPC connection
|
|
473
|
+
* @param params - Authority, vault creator, amount in lamports
|
|
474
|
+
* @returns Unsigned transaction
|
|
475
|
+
*/
|
|
476
|
+
const buildWithdrawVaultTransaction = async (connection, params) => {
|
|
477
|
+
const authority = new web3_js_1.PublicKey(params.authority);
|
|
478
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
479
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
480
|
+
const provider = makeDummyProvider(connection, authority);
|
|
481
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
482
|
+
const ix = await program.methods
|
|
483
|
+
.withdrawVault(new anchor_1.BN(params.amount_sol.toString()))
|
|
484
|
+
.accounts({
|
|
485
|
+
authority,
|
|
486
|
+
vault: vaultPda,
|
|
487
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
488
|
+
})
|
|
489
|
+
.instruction();
|
|
490
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
491
|
+
await finalizeTransaction(connection, tx, authority);
|
|
492
|
+
return {
|
|
493
|
+
transaction: tx,
|
|
494
|
+
message: `Withdraw ${params.amount_sol / 1e9} SOL from vault`,
|
|
495
|
+
};
|
|
496
|
+
};
|
|
497
|
+
exports.buildWithdrawVaultTransaction = buildWithdrawVaultTransaction;
|
|
498
|
+
/**
|
|
499
|
+
* Build an unsigned link wallet transaction.
|
|
500
|
+
*
|
|
501
|
+
* Only the vault authority can link wallets.
|
|
502
|
+
*
|
|
503
|
+
* @param connection - Solana RPC connection
|
|
504
|
+
* @param params - Authority, vault creator, wallet to link
|
|
505
|
+
* @returns Unsigned transaction
|
|
506
|
+
*/
|
|
507
|
+
const buildLinkWalletTransaction = async (connection, params) => {
|
|
508
|
+
const authority = new web3_js_1.PublicKey(params.authority);
|
|
509
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
510
|
+
const walletToLink = new web3_js_1.PublicKey(params.wallet_to_link);
|
|
511
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
512
|
+
const [walletLinkPda] = (0, program_1.getVaultWalletLinkPda)(walletToLink);
|
|
513
|
+
const provider = makeDummyProvider(connection, authority);
|
|
514
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
515
|
+
const ix = await program.methods
|
|
516
|
+
.linkWallet()
|
|
517
|
+
.accounts({
|
|
518
|
+
authority,
|
|
519
|
+
vault: vaultPda,
|
|
520
|
+
walletToLink,
|
|
521
|
+
walletLink: walletLinkPda,
|
|
522
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
523
|
+
})
|
|
524
|
+
.instruction();
|
|
525
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
526
|
+
await finalizeTransaction(connection, tx, authority);
|
|
527
|
+
return {
|
|
528
|
+
transaction: tx,
|
|
529
|
+
message: `Link wallet ${params.wallet_to_link.slice(0, 8)}... to vault`,
|
|
530
|
+
};
|
|
531
|
+
};
|
|
532
|
+
exports.buildLinkWalletTransaction = buildLinkWalletTransaction;
|
|
533
|
+
/**
|
|
534
|
+
* Build an unsigned unlink wallet transaction.
|
|
535
|
+
*
|
|
536
|
+
* Only the vault authority can unlink wallets. Rent returns to authority.
|
|
537
|
+
*
|
|
538
|
+
* @param connection - Solana RPC connection
|
|
539
|
+
* @param params - Authority, vault creator, wallet to unlink
|
|
540
|
+
* @returns Unsigned transaction
|
|
541
|
+
*/
|
|
542
|
+
const buildUnlinkWalletTransaction = async (connection, params) => {
|
|
543
|
+
const authority = new web3_js_1.PublicKey(params.authority);
|
|
544
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
545
|
+
const walletToUnlink = new web3_js_1.PublicKey(params.wallet_to_unlink);
|
|
546
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
547
|
+
const [walletLinkPda] = (0, program_1.getVaultWalletLinkPda)(walletToUnlink);
|
|
548
|
+
const provider = makeDummyProvider(connection, authority);
|
|
549
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
550
|
+
const ix = await program.methods
|
|
551
|
+
.unlinkWallet()
|
|
552
|
+
.accounts({
|
|
553
|
+
authority,
|
|
554
|
+
vault: vaultPda,
|
|
555
|
+
walletToUnlink,
|
|
556
|
+
walletLink: walletLinkPda,
|
|
557
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
558
|
+
})
|
|
559
|
+
.instruction();
|
|
560
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
561
|
+
await finalizeTransaction(connection, tx, authority);
|
|
562
|
+
return {
|
|
563
|
+
transaction: tx,
|
|
564
|
+
message: `Unlink wallet ${params.wallet_to_unlink.slice(0, 8)}... from vault`,
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
exports.buildUnlinkWalletTransaction = buildUnlinkWalletTransaction;
|
|
568
|
+
/**
|
|
569
|
+
* Build an unsigned transfer authority transaction.
|
|
570
|
+
*
|
|
571
|
+
* Transfers vault admin control to a new wallet.
|
|
572
|
+
*
|
|
573
|
+
* @param connection - Solana RPC connection
|
|
574
|
+
* @param params - Current authority, vault creator, new authority
|
|
575
|
+
* @returns Unsigned transaction
|
|
576
|
+
*/
|
|
577
|
+
const buildTransferAuthorityTransaction = async (connection, params) => {
|
|
578
|
+
const authority = new web3_js_1.PublicKey(params.authority);
|
|
579
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
580
|
+
const newAuthority = new web3_js_1.PublicKey(params.new_authority);
|
|
581
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
582
|
+
const provider = makeDummyProvider(connection, authority);
|
|
583
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
584
|
+
const ix = await program.methods
|
|
585
|
+
.transferAuthority()
|
|
586
|
+
.accounts({
|
|
587
|
+
authority,
|
|
588
|
+
vault: vaultPda,
|
|
589
|
+
newAuthority,
|
|
590
|
+
})
|
|
591
|
+
.instruction();
|
|
592
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
593
|
+
await finalizeTransaction(connection, tx, authority);
|
|
594
|
+
return {
|
|
595
|
+
transaction: tx,
|
|
596
|
+
message: `Transfer vault authority to ${params.new_authority.slice(0, 8)}...`,
|
|
597
|
+
};
|
|
598
|
+
};
|
|
599
|
+
exports.buildTransferAuthorityTransaction = buildTransferAuthorityTransaction;
|
|
600
|
+
// ============================================================================
|
|
601
|
+
// Borrow (V2.4)
|
|
602
|
+
// ============================================================================
|
|
603
|
+
/**
|
|
604
|
+
* Build an unsigned borrow transaction.
|
|
605
|
+
*
|
|
606
|
+
* Lock tokens as collateral in the collateral vault and receive SOL from treasury.
|
|
607
|
+
* Token must be migrated (has Raydium pool for price calculation).
|
|
608
|
+
*
|
|
609
|
+
* @param connection - Solana RPC connection
|
|
610
|
+
* @param params - Borrow parameters (mint, borrower, collateral_amount, sol_to_borrow)
|
|
611
|
+
* @returns Unsigned transaction and descriptive message
|
|
612
|
+
*/
|
|
613
|
+
const buildBorrowTransaction = async (connection, params) => {
|
|
614
|
+
const { mint: mintStr, borrower: borrowerStr, collateral_amount, sol_to_borrow, vault: vaultCreatorStr } = params;
|
|
615
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
616
|
+
const borrower = new web3_js_1.PublicKey(borrowerStr);
|
|
617
|
+
// Derive PDAs
|
|
618
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
619
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
620
|
+
const [collateralVaultPda] = (0, program_1.getCollateralVaultPda)(mint);
|
|
621
|
+
const [loanPositionPda] = (0, program_1.getLoanPositionPda)(mint, borrower);
|
|
622
|
+
const borrowerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, borrower, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
623
|
+
// Get Raydium pool accounts for price calculation
|
|
624
|
+
const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
|
|
625
|
+
// [V18] Vault accounts (optional — collateral from vault ATA, SOL to vault)
|
|
626
|
+
let torchVaultAccount = null;
|
|
627
|
+
let vaultWalletLinkAccount = null;
|
|
628
|
+
let vaultTokenAccount = null;
|
|
629
|
+
if (vaultCreatorStr) {
|
|
630
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
631
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
632
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(borrower);
|
|
633
|
+
vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
634
|
+
}
|
|
635
|
+
const tx = new web3_js_1.Transaction();
|
|
636
|
+
// Create vault ATA if needed
|
|
637
|
+
if (vaultTokenAccount && torchVaultAccount) {
|
|
638
|
+
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));
|
|
639
|
+
}
|
|
640
|
+
const provider = makeDummyProvider(connection, borrower);
|
|
641
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
642
|
+
const borrowIx = await program.methods
|
|
643
|
+
.borrow({
|
|
644
|
+
collateralAmount: new anchor_1.BN(collateral_amount.toString()),
|
|
645
|
+
solToBorrow: new anchor_1.BN(sol_to_borrow.toString()),
|
|
646
|
+
})
|
|
647
|
+
.accounts({
|
|
648
|
+
borrower,
|
|
649
|
+
mint,
|
|
650
|
+
bondingCurve: bondingCurvePda,
|
|
651
|
+
treasury: treasuryPda,
|
|
652
|
+
collateralVault: collateralVaultPda,
|
|
653
|
+
borrowerTokenAccount,
|
|
654
|
+
loanPosition: loanPositionPda,
|
|
655
|
+
poolState: raydium.poolState,
|
|
656
|
+
tokenVault0: raydium.token0Vault,
|
|
657
|
+
tokenVault1: raydium.token1Vault,
|
|
658
|
+
torchVault: torchVaultAccount,
|
|
659
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
660
|
+
vaultTokenAccount,
|
|
661
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
662
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
663
|
+
})
|
|
664
|
+
.instruction();
|
|
665
|
+
tx.add(borrowIx);
|
|
666
|
+
await finalizeTransaction(connection, tx, borrower);
|
|
667
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
668
|
+
return {
|
|
669
|
+
transaction: tx,
|
|
670
|
+
message: `Borrow ${Number(sol_to_borrow) / 1e9} SOL with ${Number(collateral_amount) / 1e6} tokens as collateral${vaultLabel}`,
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
exports.buildBorrowTransaction = buildBorrowTransaction;
|
|
674
|
+
// ============================================================================
|
|
675
|
+
// Repay (V2.4)
|
|
676
|
+
// ============================================================================
|
|
677
|
+
/**
|
|
678
|
+
* Build an unsigned repay transaction.
|
|
679
|
+
*
|
|
680
|
+
* Repay SOL debt. Interest is paid first, then principal.
|
|
681
|
+
* Full repay returns all collateral and closes the position.
|
|
682
|
+
*
|
|
683
|
+
* @param connection - Solana RPC connection
|
|
684
|
+
* @param params - Repay parameters (mint, borrower, sol_amount)
|
|
685
|
+
* @returns Unsigned transaction and descriptive message
|
|
686
|
+
*/
|
|
687
|
+
const buildRepayTransaction = async (connection, params) => {
|
|
688
|
+
const { mint: mintStr, borrower: borrowerStr, sol_amount, vault: vaultCreatorStr } = params;
|
|
689
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
690
|
+
const borrower = new web3_js_1.PublicKey(borrowerStr);
|
|
691
|
+
// Derive PDAs
|
|
692
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
693
|
+
const [collateralVaultPda] = (0, program_1.getCollateralVaultPda)(mint);
|
|
694
|
+
const [loanPositionPda] = (0, program_1.getLoanPositionPda)(mint, borrower);
|
|
695
|
+
const borrowerTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, borrower, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
696
|
+
// [V18] Vault accounts (optional — SOL from vault, collateral returns to vault ATA)
|
|
697
|
+
let torchVaultAccount = null;
|
|
698
|
+
let vaultWalletLinkAccount = null;
|
|
699
|
+
let vaultTokenAccount = null;
|
|
700
|
+
if (vaultCreatorStr) {
|
|
701
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
702
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
703
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(borrower);
|
|
704
|
+
vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
705
|
+
}
|
|
706
|
+
const tx = new web3_js_1.Transaction();
|
|
707
|
+
// Create vault ATA if needed (collateral returns here)
|
|
708
|
+
if (vaultTokenAccount && torchVaultAccount) {
|
|
709
|
+
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));
|
|
710
|
+
}
|
|
711
|
+
const provider = makeDummyProvider(connection, borrower);
|
|
712
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
713
|
+
const repayIx = await program.methods
|
|
714
|
+
.repay(new anchor_1.BN(sol_amount.toString()))
|
|
715
|
+
.accounts({
|
|
716
|
+
borrower,
|
|
717
|
+
mint,
|
|
718
|
+
treasury: treasuryPda,
|
|
719
|
+
collateralVault: collateralVaultPda,
|
|
720
|
+
borrowerTokenAccount,
|
|
721
|
+
loanPosition: loanPositionPda,
|
|
722
|
+
torchVault: torchVaultAccount,
|
|
723
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
724
|
+
vaultTokenAccount,
|
|
725
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
726
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
727
|
+
})
|
|
728
|
+
.instruction();
|
|
729
|
+
tx.add(repayIx);
|
|
730
|
+
await finalizeTransaction(connection, tx, borrower);
|
|
731
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
732
|
+
return {
|
|
733
|
+
transaction: tx,
|
|
734
|
+
message: `Repay ${Number(sol_amount) / 1e9} SOL${vaultLabel}`,
|
|
735
|
+
};
|
|
736
|
+
};
|
|
737
|
+
exports.buildRepayTransaction = buildRepayTransaction;
|
|
738
|
+
// ============================================================================
|
|
739
|
+
// Liquidate (V2.4)
|
|
740
|
+
// ============================================================================
|
|
741
|
+
/**
|
|
742
|
+
* Build an unsigned liquidate transaction.
|
|
743
|
+
*
|
|
744
|
+
* Permissionless — anyone can call when a borrower's LTV exceeds the
|
|
745
|
+
* liquidation threshold. Liquidator pays SOL and receives collateral + bonus.
|
|
746
|
+
*
|
|
747
|
+
* @param connection - Solana RPC connection
|
|
748
|
+
* @param params - Liquidate parameters (mint, liquidator, borrower)
|
|
749
|
+
* @returns Unsigned transaction and descriptive message
|
|
750
|
+
*/
|
|
751
|
+
const buildLiquidateTransaction = async (connection, params) => {
|
|
752
|
+
const { mint: mintStr, liquidator: liquidatorStr, borrower: borrowerStr, vault: vaultCreatorStr } = params;
|
|
753
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
754
|
+
const liquidator = new web3_js_1.PublicKey(liquidatorStr);
|
|
755
|
+
const borrower = new web3_js_1.PublicKey(borrowerStr);
|
|
756
|
+
// Derive PDAs
|
|
757
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
758
|
+
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
759
|
+
const [collateralVaultPda] = (0, program_1.getCollateralVaultPda)(mint);
|
|
760
|
+
const [loanPositionPda] = (0, program_1.getLoanPositionPda)(mint, borrower);
|
|
761
|
+
const liquidatorTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, liquidator, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
762
|
+
// Get Raydium pool accounts for price calculation
|
|
763
|
+
const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
|
|
764
|
+
// [V20] Vault accounts (optional — SOL from vault, collateral to vault ATA)
|
|
765
|
+
let torchVaultAccount = null;
|
|
766
|
+
let vaultWalletLinkAccount = null;
|
|
767
|
+
let vaultTokenAccount = null;
|
|
768
|
+
if (vaultCreatorStr) {
|
|
769
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
770
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
771
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(liquidator);
|
|
772
|
+
vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultAccount, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
773
|
+
}
|
|
774
|
+
const tx = new web3_js_1.Transaction();
|
|
775
|
+
// Create liquidator ATA if needed
|
|
776
|
+
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));
|
|
777
|
+
// Create vault ATA if needed (collateral goes here)
|
|
778
|
+
if (vaultTokenAccount && torchVaultAccount) {
|
|
779
|
+
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));
|
|
780
|
+
}
|
|
781
|
+
const provider = makeDummyProvider(connection, liquidator);
|
|
782
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
783
|
+
const liquidateIx = await program.methods
|
|
784
|
+
.liquidate()
|
|
785
|
+
.accounts({
|
|
786
|
+
liquidator,
|
|
787
|
+
borrower,
|
|
788
|
+
mint,
|
|
789
|
+
bondingCurve: bondingCurvePda,
|
|
790
|
+
treasury: treasuryPda,
|
|
791
|
+
collateralVault: collateralVaultPda,
|
|
792
|
+
liquidatorTokenAccount,
|
|
793
|
+
loanPosition: loanPositionPda,
|
|
794
|
+
poolState: raydium.poolState,
|
|
795
|
+
tokenVault0: raydium.token0Vault,
|
|
796
|
+
tokenVault1: raydium.token1Vault,
|
|
797
|
+
torchVault: torchVaultAccount,
|
|
798
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
799
|
+
vaultTokenAccount,
|
|
800
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
801
|
+
associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
802
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
803
|
+
})
|
|
804
|
+
.instruction();
|
|
805
|
+
tx.add(liquidateIx);
|
|
806
|
+
await finalizeTransaction(connection, tx, liquidator);
|
|
807
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
808
|
+
return {
|
|
809
|
+
transaction: tx,
|
|
810
|
+
message: `Liquidate loan position for ${borrowerStr.slice(0, 8)}...${vaultLabel}`,
|
|
811
|
+
};
|
|
812
|
+
};
|
|
813
|
+
exports.buildLiquidateTransaction = buildLiquidateTransaction;
|
|
814
|
+
// ============================================================================
|
|
815
|
+
// Claim Protocol Rewards
|
|
816
|
+
// ============================================================================
|
|
817
|
+
/**
|
|
818
|
+
* Build an unsigned claim protocol rewards transaction.
|
|
819
|
+
*
|
|
820
|
+
* Claims the user's proportional share of protocol treasury rewards
|
|
821
|
+
* based on trading volume in the previous epoch. Requires >= 10 SOL volume.
|
|
822
|
+
*
|
|
823
|
+
* @param connection - Solana RPC connection
|
|
824
|
+
* @param params - Claim parameters (user, optional vault)
|
|
825
|
+
* @returns Unsigned transaction and descriptive message
|
|
826
|
+
*/
|
|
827
|
+
const buildClaimProtocolRewardsTransaction = async (connection, params) => {
|
|
828
|
+
const { user: userStr, vault: vaultCreatorStr } = params;
|
|
829
|
+
const user = new web3_js_1.PublicKey(userStr);
|
|
830
|
+
// Derive PDAs
|
|
831
|
+
const [userStatsPda] = (0, program_1.getUserStatsPda)(user);
|
|
832
|
+
const [protocolTreasuryPda] = (0, program_1.getProtocolTreasuryPda)();
|
|
833
|
+
// [V20] Vault accounts (optional — rewards go to vault instead of user)
|
|
834
|
+
let torchVaultAccount = null;
|
|
835
|
+
let vaultWalletLinkAccount = null;
|
|
836
|
+
if (vaultCreatorStr) {
|
|
837
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
838
|
+
[torchVaultAccount] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
839
|
+
[vaultWalletLinkAccount] = (0, program_1.getVaultWalletLinkPda)(user);
|
|
840
|
+
}
|
|
841
|
+
const tx = new web3_js_1.Transaction();
|
|
842
|
+
const provider = makeDummyProvider(connection, user);
|
|
843
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
844
|
+
const claimIx = await program.methods
|
|
845
|
+
.claimProtocolRewards()
|
|
846
|
+
.accounts({
|
|
847
|
+
user,
|
|
848
|
+
userStats: userStatsPda,
|
|
849
|
+
protocolTreasury: protocolTreasuryPda,
|
|
850
|
+
torchVault: torchVaultAccount,
|
|
851
|
+
vaultWalletLink: vaultWalletLinkAccount,
|
|
852
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
853
|
+
})
|
|
854
|
+
.instruction();
|
|
855
|
+
tx.add(claimIx);
|
|
856
|
+
await finalizeTransaction(connection, tx, user);
|
|
857
|
+
const vaultLabel = vaultCreatorStr ? ' (via vault)' : '';
|
|
858
|
+
return {
|
|
859
|
+
transaction: tx,
|
|
860
|
+
message: `Claim protocol rewards${vaultLabel}`,
|
|
861
|
+
};
|
|
862
|
+
};
|
|
863
|
+
exports.buildClaimProtocolRewardsTransaction = buildClaimProtocolRewardsTransaction;
|
|
864
|
+
// ============================================================================
|
|
865
|
+
// Withdraw Tokens (V18)
|
|
866
|
+
// ============================================================================
|
|
867
|
+
/**
|
|
868
|
+
* Build an unsigned withdraw tokens transaction.
|
|
869
|
+
*
|
|
870
|
+
* Withdraw tokens from a vault ATA to any destination token account.
|
|
871
|
+
* Authority only. Composability escape hatch for external DeFi.
|
|
872
|
+
*
|
|
873
|
+
* @param connection - Solana RPC connection
|
|
874
|
+
* @param params - Authority, vault creator, mint, destination, amount in raw units
|
|
875
|
+
* @returns Unsigned transaction
|
|
876
|
+
*/
|
|
877
|
+
const buildWithdrawTokensTransaction = async (connection, params) => {
|
|
878
|
+
const authority = new web3_js_1.PublicKey(params.authority);
|
|
879
|
+
const vaultCreator = new web3_js_1.PublicKey(params.vault_creator);
|
|
880
|
+
const mint = new web3_js_1.PublicKey(params.mint);
|
|
881
|
+
const destination = new web3_js_1.PublicKey(params.destination);
|
|
882
|
+
const [vaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
883
|
+
const vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, vaultPda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
884
|
+
const destinationTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, destination, false, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
885
|
+
const tx = new web3_js_1.Transaction();
|
|
886
|
+
// Create destination ATA if needed
|
|
887
|
+
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(authority, destinationTokenAccount, destination, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
888
|
+
const provider = makeDummyProvider(connection, authority);
|
|
889
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
890
|
+
const ix = await program.methods
|
|
891
|
+
.withdrawTokens(new anchor_1.BN(params.amount.toString()))
|
|
892
|
+
.accounts({
|
|
893
|
+
authority,
|
|
894
|
+
vault: vaultPda,
|
|
895
|
+
mint,
|
|
896
|
+
vaultTokenAccount,
|
|
897
|
+
destinationTokenAccount,
|
|
898
|
+
tokenProgram: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
899
|
+
})
|
|
900
|
+
.instruction();
|
|
901
|
+
tx.add(ix);
|
|
902
|
+
await finalizeTransaction(connection, tx, authority);
|
|
903
|
+
return {
|
|
904
|
+
transaction: tx,
|
|
905
|
+
message: `Withdraw ${params.amount} tokens from vault to ${params.destination.slice(0, 8)}...`,
|
|
906
|
+
};
|
|
907
|
+
};
|
|
908
|
+
exports.buildWithdrawTokensTransaction = buildWithdrawTokensTransaction;
|
|
909
|
+
// ============================================================================
|
|
910
|
+
// Vault Swap (V19)
|
|
911
|
+
// ============================================================================
|
|
912
|
+
/**
|
|
913
|
+
* Build an unsigned vault-routed DEX swap transaction.
|
|
914
|
+
*
|
|
915
|
+
* Executes a Raydium CPMM swap through the vault PDA for migrated Torch tokens.
|
|
916
|
+
* Full custody preserved — all value flows through the vault.
|
|
917
|
+
*
|
|
918
|
+
* @param connection - Solana RPC connection
|
|
919
|
+
* @param params - Swap parameters (mint, signer, vault_creator, amount_in, minimum_amount_out, is_buy)
|
|
920
|
+
* @returns Unsigned transaction and descriptive message
|
|
921
|
+
*/
|
|
922
|
+
const buildVaultSwapTransaction = async (connection, params) => {
|
|
923
|
+
const { mint: mintStr, signer: signerStr, vault_creator: vaultCreatorStr, amount_in, minimum_amount_out, is_buy } = params;
|
|
924
|
+
const mint = new web3_js_1.PublicKey(mintStr);
|
|
925
|
+
const signer = new web3_js_1.PublicKey(signerStr);
|
|
926
|
+
const vaultCreator = new web3_js_1.PublicKey(vaultCreatorStr);
|
|
927
|
+
// Derive vault PDAs
|
|
928
|
+
const [torchVaultPda] = (0, program_1.getTorchVaultPda)(vaultCreator);
|
|
929
|
+
const [vaultWalletLinkPda] = (0, program_1.getVaultWalletLinkPda)(signer);
|
|
930
|
+
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
931
|
+
// Vault's token ATA (Token-2022)
|
|
932
|
+
const vaultTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, torchVaultPda, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
933
|
+
// Vault's WSOL ATA (SPL Token — persistent, reused across swaps)
|
|
934
|
+
const vaultWsolAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(constants_1.WSOL_MINT, torchVaultPda, true, spl_token_1.TOKEN_PROGRAM_ID);
|
|
935
|
+
// Raydium pool accounts
|
|
936
|
+
const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
|
|
937
|
+
const tx = new web3_js_1.Transaction();
|
|
938
|
+
// Create vault token ATA if needed (for first buy of a migrated token)
|
|
939
|
+
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(signer, vaultTokenAccount, torchVaultPda, mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
940
|
+
// Create vault WSOL ATA if needed (persistent — reused across swaps)
|
|
941
|
+
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(signer, vaultWsolAccount, torchVaultPda, constants_1.WSOL_MINT, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
942
|
+
const provider = makeDummyProvider(connection, signer);
|
|
943
|
+
const program = new anchor_1.Program(torch_market_json_1.default, provider);
|
|
944
|
+
// On buy: fund WSOL with lamports from vault in a separate instruction
|
|
945
|
+
// (isolates direct lamport manipulation from CPIs to avoid runtime balance errors)
|
|
946
|
+
if (is_buy) {
|
|
947
|
+
const fundIx = await program.methods
|
|
948
|
+
.fundVaultWsol(new anchor_1.BN(amount_in.toString()))
|
|
949
|
+
.accounts({
|
|
950
|
+
signer,
|
|
951
|
+
torchVault: torchVaultPda,
|
|
952
|
+
vaultWalletLink: vaultWalletLinkPda,
|
|
953
|
+
vaultWsolAccount,
|
|
954
|
+
})
|
|
955
|
+
.instruction();
|
|
956
|
+
tx.add(fundIx);
|
|
957
|
+
}
|
|
958
|
+
const swapIx = await program.methods
|
|
959
|
+
.vaultSwap(new anchor_1.BN(amount_in.toString()), new anchor_1.BN(minimum_amount_out.toString()), is_buy)
|
|
960
|
+
.accounts({
|
|
961
|
+
signer,
|
|
962
|
+
torchVault: torchVaultPda,
|
|
963
|
+
vaultWalletLink: vaultWalletLinkPda,
|
|
964
|
+
mint,
|
|
965
|
+
bondingCurve: bondingCurvePda,
|
|
966
|
+
vaultTokenAccount,
|
|
967
|
+
vaultWsolAccount,
|
|
968
|
+
raydiumProgram: constants_1.RAYDIUM_CPMM_PROGRAM,
|
|
969
|
+
raydiumAuthority: raydium.raydiumAuthority,
|
|
970
|
+
ammConfig: constants_1.RAYDIUM_AMM_CONFIG,
|
|
971
|
+
poolState: raydium.poolState,
|
|
972
|
+
poolTokenVault0: raydium.token0Vault,
|
|
973
|
+
poolTokenVault1: raydium.token1Vault,
|
|
974
|
+
observationState: raydium.observationState,
|
|
975
|
+
wsolMint: constants_1.WSOL_MINT,
|
|
976
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
977
|
+
token2022Program: spl_token_1.TOKEN_2022_PROGRAM_ID,
|
|
978
|
+
associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
979
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
980
|
+
})
|
|
981
|
+
.instruction();
|
|
982
|
+
tx.add(swapIx);
|
|
983
|
+
await finalizeTransaction(connection, tx, signer);
|
|
984
|
+
const direction = is_buy ? 'Buy' : 'Sell';
|
|
985
|
+
const amountLabel = is_buy
|
|
986
|
+
? `${amount_in / 1e9} SOL`
|
|
987
|
+
: `${amount_in / 1e6} tokens`;
|
|
988
|
+
return {
|
|
989
|
+
transaction: tx,
|
|
990
|
+
message: `${direction} ${amountLabel} via vault DEX swap`,
|
|
991
|
+
};
|
|
992
|
+
};
|
|
993
|
+
exports.buildVaultSwapTransaction = buildVaultSwapTransaction;
|
|
994
|
+
//# sourceMappingURL=transactions.js.map
|