torchsdk 3.4.0 → 3.5.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.
- package/package.json +1 -1
- package/dist/constants.d.ts +0 -25
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -52
- package/dist/constants.js.map +0 -1
- package/dist/ephemeral.d.ts +0 -33
- package/dist/ephemeral.d.ts.map +0 -1
- package/dist/ephemeral.js +0 -39
- package/dist/ephemeral.js.map +0 -1
- package/dist/gateway.d.ts +0 -17
- package/dist/gateway.d.ts.map +0 -1
- package/dist/gateway.js +0 -48
- package/dist/gateway.js.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -62
- package/dist/index.js.map +0 -1
- package/dist/program.d.ts +0 -154
- package/dist/program.d.ts.map +0 -1
- package/dist/program.js +0 -226
- package/dist/program.js.map +0 -1
- package/dist/quotes.d.ts +0 -16
- package/dist/quotes.d.ts.map +0 -1
- package/dist/quotes.js +0 -79
- package/dist/quotes.js.map +0 -1
- package/dist/said.d.ts +0 -27
- package/dist/said.d.ts.map +0 -1
- package/dist/said.js +0 -89
- package/dist/said.js.map +0 -1
- package/dist/tokens.d.ts +0 -64
- package/dist/tokens.d.ts.map +0 -1
- package/dist/tokens.js +0 -650
- package/dist/tokens.js.map +0 -1
- package/dist/torch_market.json +0 -6452
- package/dist/transactions.d.ts +0 -183
- package/dist/transactions.d.ts.map +0 -1
- package/dist/transactions.js +0 -994
- package/dist/transactions.js.map +0 -1
- package/dist/types.d.ts +0 -283
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
package/dist/tokens.js
DELETED
|
@@ -1,650 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Token data fetching
|
|
4
|
-
*
|
|
5
|
-
* Read-only functions for querying token state from Solana.
|
|
6
|
-
*/
|
|
7
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
-
if (k2 === undefined) k2 = k;
|
|
9
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
-
}
|
|
13
|
-
Object.defineProperty(o, k2, desc);
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
-
}) : function(o, v) {
|
|
21
|
-
o["default"] = v;
|
|
22
|
-
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
-
var ownKeys = function(o) {
|
|
25
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
-
var ar = [];
|
|
27
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
-
return ar;
|
|
29
|
-
};
|
|
30
|
-
return ownKeys(o);
|
|
31
|
-
};
|
|
32
|
-
return function (mod) {
|
|
33
|
-
if (mod && mod.__esModule) return mod;
|
|
34
|
-
var result = {};
|
|
35
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
-
__setModuleDefault(result, mod);
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
})();
|
|
40
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
-
};
|
|
43
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.fetchTokenRaw = exports.getVaultWalletLink = exports.getVaultForWallet = exports.getVault = exports.getLoanPosition = exports.getLendingInfo = exports.getMessages = exports.getHolders = exports.getToken = exports.getTokens = void 0;
|
|
45
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
46
|
-
const anchor_1 = require("@coral-xyz/anchor");
|
|
47
|
-
const spl_token_1 = require("@solana/spl-token");
|
|
48
|
-
const program_1 = require("./program");
|
|
49
|
-
const constants_1 = require("./constants");
|
|
50
|
-
const gateway_1 = require("./gateway");
|
|
51
|
-
const torch_market_json_1 = __importDefault(require("./torch_market.json"));
|
|
52
|
-
const getTokenStatus = (bc) => {
|
|
53
|
-
if (bc.migrated)
|
|
54
|
-
return 'migrated';
|
|
55
|
-
if (bc.bonding_complete)
|
|
56
|
-
return 'complete';
|
|
57
|
-
return 'bonding';
|
|
58
|
-
};
|
|
59
|
-
const fetchAllRawTokens = async (connection) => {
|
|
60
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
61
|
-
const accounts = await connection.getProgramAccounts(constants_1.PROGRAM_ID, {
|
|
62
|
-
filters: [{ memcmp: { offset: 0, bytes: '4y6pru6YvC7' } }],
|
|
63
|
-
});
|
|
64
|
-
const tokens = [];
|
|
65
|
-
for (const acc of accounts) {
|
|
66
|
-
try {
|
|
67
|
-
const decoded = coder.accounts.decode('BondingCurve', acc.account.data);
|
|
68
|
-
const mintStr = decoded.mint.toString();
|
|
69
|
-
if (constants_1.BLACKLISTED_MINTS.includes(mintStr))
|
|
70
|
-
continue;
|
|
71
|
-
if (decoded.reclaimed)
|
|
72
|
-
continue;
|
|
73
|
-
tokens.push({
|
|
74
|
-
mint: mintStr,
|
|
75
|
-
bondingCurve: decoded,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// Not a bonding curve account
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return tokens;
|
|
83
|
-
};
|
|
84
|
-
const toTokenSummary = (raw) => {
|
|
85
|
-
const bc = raw.bondingCurve;
|
|
86
|
-
const virtualSol = BigInt(bc.virtual_sol_reserves.toString());
|
|
87
|
-
const virtualTokens = BigInt(bc.virtual_token_reserves.toString());
|
|
88
|
-
const realSol = BigInt(bc.real_sol_reserves.toString());
|
|
89
|
-
const realTokens = BigInt(bc.real_token_reserves.toString());
|
|
90
|
-
const voteVault = BigInt(bc.vote_vault_balance.toString());
|
|
91
|
-
const price = (0, program_1.calculatePrice)(virtualSol, virtualTokens);
|
|
92
|
-
const priceInSol = (price * constants_1.TOKEN_MULTIPLIER) / constants_1.LAMPORTS_PER_SOL;
|
|
93
|
-
const circulating = constants_1.TOTAL_SUPPLY - realTokens - voteVault;
|
|
94
|
-
const marketCapSol = (priceInSol * Number(circulating)) / constants_1.TOKEN_MULTIPLIER;
|
|
95
|
-
return {
|
|
96
|
-
mint: raw.mint,
|
|
97
|
-
name: (0, program_1.decodeString)(bc.name),
|
|
98
|
-
symbol: (0, program_1.decodeString)(bc.symbol),
|
|
99
|
-
status: getTokenStatus(bc),
|
|
100
|
-
price_sol: priceInSol,
|
|
101
|
-
market_cap_sol: marketCapSol,
|
|
102
|
-
progress_percent: (0, program_1.calculateBondingProgress)(realSol),
|
|
103
|
-
holders: null,
|
|
104
|
-
created_at: 0,
|
|
105
|
-
};
|
|
106
|
-
};
|
|
107
|
-
const filterAndSort = (tokens, params) => {
|
|
108
|
-
let filtered = [...tokens];
|
|
109
|
-
if (params.status && params.status !== 'all') {
|
|
110
|
-
filtered = filtered.filter((t) => getTokenStatus(t.bondingCurve) === params.status);
|
|
111
|
-
}
|
|
112
|
-
switch (params.sort) {
|
|
113
|
-
case 'marketcap':
|
|
114
|
-
case 'volume':
|
|
115
|
-
filtered.sort((a, b) => {
|
|
116
|
-
const aR = BigInt(a.bondingCurve.real_sol_reserves.toString());
|
|
117
|
-
const bR = BigInt(b.bondingCurve.real_sol_reserves.toString());
|
|
118
|
-
return bR > aR ? 1 : bR < aR ? -1 : 0;
|
|
119
|
-
});
|
|
120
|
-
break;
|
|
121
|
-
case 'newest':
|
|
122
|
-
default:
|
|
123
|
-
filtered.sort((a, b) => {
|
|
124
|
-
const aA = BigInt(a.bondingCurve.last_activity_slot.toString());
|
|
125
|
-
const bA = BigInt(b.bondingCurve.last_activity_slot.toString());
|
|
126
|
-
return bA > aA ? 1 : bA < aA ? -1 : 0;
|
|
127
|
-
});
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
const offset = params.offset || 0;
|
|
131
|
-
const limit = Math.min(params.limit || 50, 100);
|
|
132
|
-
return filtered.slice(offset, offset + limit);
|
|
133
|
-
};
|
|
134
|
-
const buildTokenDetail = (mint, bc, treasury, metadata, holdersCount, solPriceUsd, saidVerification, warnings) => {
|
|
135
|
-
const virtualSol = BigInt(bc.virtual_sol_reserves.toString());
|
|
136
|
-
const virtualTokens = BigInt(bc.virtual_token_reserves.toString());
|
|
137
|
-
const realSol = BigInt(bc.real_sol_reserves.toString());
|
|
138
|
-
const realTokens = BigInt(bc.real_token_reserves.toString());
|
|
139
|
-
const voteVault = BigInt(bc.vote_vault_balance.toString());
|
|
140
|
-
const burned = BigInt(bc.permanently_burned_tokens?.toString() || '0');
|
|
141
|
-
const price = (0, program_1.calculatePrice)(virtualSol, virtualTokens);
|
|
142
|
-
const priceInSol = (price * constants_1.TOKEN_MULTIPLIER) / constants_1.LAMPORTS_PER_SOL;
|
|
143
|
-
const circulating = constants_1.TOTAL_SUPPLY - realTokens - voteVault;
|
|
144
|
-
const marketCapSol = (priceInSol * Number(circulating)) / constants_1.TOKEN_MULTIPLIER;
|
|
145
|
-
const treasurySol = treasury ? Number(treasury.sol_balance.toString()) / constants_1.LAMPORTS_PER_SOL : 0;
|
|
146
|
-
const treasuryTokens = treasury ? Number(treasury.tokens_held.toString()) / constants_1.TOKEN_MULTIPLIER : 0;
|
|
147
|
-
const boughtBack = treasury ? Number(treasury.total_bought_back.toString()) / constants_1.TOKEN_MULTIPLIER : 0;
|
|
148
|
-
const buybackCount = treasury ? Number(treasury.buyback_count.toString()) : 0;
|
|
149
|
-
const stars = treasury ? Number(treasury.total_stars.toString()) : 0;
|
|
150
|
-
return {
|
|
151
|
-
mint,
|
|
152
|
-
name: (0, program_1.decodeString)(bc.name),
|
|
153
|
-
symbol: (0, program_1.decodeString)(bc.symbol),
|
|
154
|
-
description: metadata?.description,
|
|
155
|
-
image: metadata?.image,
|
|
156
|
-
status: getTokenStatus(bc),
|
|
157
|
-
price_sol: priceInSol,
|
|
158
|
-
price_usd: solPriceUsd ? priceInSol * solPriceUsd : undefined,
|
|
159
|
-
market_cap_sol: marketCapSol,
|
|
160
|
-
market_cap_usd: solPriceUsd ? marketCapSol * solPriceUsd : undefined,
|
|
161
|
-
progress_percent: (0, program_1.calculateBondingProgress)(realSol),
|
|
162
|
-
sol_raised: Number(realSol) / constants_1.LAMPORTS_PER_SOL,
|
|
163
|
-
sol_target: 200,
|
|
164
|
-
total_supply: Number(constants_1.TOTAL_SUPPLY) / constants_1.TOKEN_MULTIPLIER,
|
|
165
|
-
circulating_supply: Number(circulating) / constants_1.TOKEN_MULTIPLIER,
|
|
166
|
-
tokens_in_curve: Number(realTokens) / constants_1.TOKEN_MULTIPLIER,
|
|
167
|
-
tokens_in_vote_vault: Number(voteVault) / constants_1.TOKEN_MULTIPLIER,
|
|
168
|
-
tokens_burned: Number(burned) / constants_1.TOKEN_MULTIPLIER,
|
|
169
|
-
treasury_sol_balance: treasurySol,
|
|
170
|
-
treasury_token_balance: treasuryTokens,
|
|
171
|
-
total_bought_back: boughtBack,
|
|
172
|
-
buyback_count: buybackCount,
|
|
173
|
-
votes_return: Number(bc.votes_return.toString()),
|
|
174
|
-
votes_burn: Number(bc.votes_burn.toString()),
|
|
175
|
-
creator: bc.creator.toString(),
|
|
176
|
-
holders: holdersCount ?? null,
|
|
177
|
-
stars,
|
|
178
|
-
created_at: 0,
|
|
179
|
-
last_activity_at: Number(bc.last_activity_slot.toString()),
|
|
180
|
-
twitter: metadata?.twitter,
|
|
181
|
-
telegram: metadata?.telegram,
|
|
182
|
-
website: metadata?.website,
|
|
183
|
-
creator_verified: saidVerification?.verified,
|
|
184
|
-
creator_trust_tier: saidVerification?.trustTier,
|
|
185
|
-
creator_said_name: saidVerification?.name,
|
|
186
|
-
creator_badge_url: saidVerification?.verified
|
|
187
|
-
? `https://api.saidprotocol.com/api/badge/${bc.creator.toString()}.svg`
|
|
188
|
-
: undefined,
|
|
189
|
-
...(warnings && warnings.length > 0 ? { warnings } : {}),
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
// Internal: fetch single token on-chain data
|
|
193
|
-
const fetchTokenRaw = async (connection, mint) => {
|
|
194
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
195
|
-
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
196
|
-
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
197
|
-
const [bcAccount, treasuryAccount] = await Promise.all([
|
|
198
|
-
connection.getAccountInfo(bondingCurvePda),
|
|
199
|
-
connection.getAccountInfo(treasuryPda),
|
|
200
|
-
]);
|
|
201
|
-
if (!bcAccount)
|
|
202
|
-
return null;
|
|
203
|
-
const bondingCurve = coder.accounts.decode('BondingCurve', bcAccount.data);
|
|
204
|
-
let treasury = null;
|
|
205
|
-
if (treasuryAccount) {
|
|
206
|
-
treasury = coder.accounts.decode('Treasury', treasuryAccount.data);
|
|
207
|
-
}
|
|
208
|
-
return { bondingCurve, treasury };
|
|
209
|
-
};
|
|
210
|
-
exports.fetchTokenRaw = fetchTokenRaw;
|
|
211
|
-
// ============================================================================
|
|
212
|
-
// Public API
|
|
213
|
-
// ============================================================================
|
|
214
|
-
/**
|
|
215
|
-
* List tokens with optional filtering and sorting.
|
|
216
|
-
*/
|
|
217
|
-
const getTokens = async (connection, params = {}) => {
|
|
218
|
-
const allTokens = await fetchAllRawTokens(connection);
|
|
219
|
-
const filtered = filterAndSort(allTokens, params);
|
|
220
|
-
const summaries = filtered.map(toTokenSummary);
|
|
221
|
-
return {
|
|
222
|
-
tokens: summaries,
|
|
223
|
-
total: allTokens.length,
|
|
224
|
-
limit: params.limit || 50,
|
|
225
|
-
offset: params.offset || 0,
|
|
226
|
-
};
|
|
227
|
-
};
|
|
228
|
-
exports.getTokens = getTokens;
|
|
229
|
-
/**
|
|
230
|
-
* Get detailed info for a single token.
|
|
231
|
-
*/
|
|
232
|
-
const getToken = async (connection, mintStr) => {
|
|
233
|
-
const mint = new web3_js_1.PublicKey(mintStr);
|
|
234
|
-
const tokenData = await fetchTokenRaw(connection, mint);
|
|
235
|
-
if (!tokenData) {
|
|
236
|
-
throw new Error(`Token not found: ${mintStr}`);
|
|
237
|
-
}
|
|
238
|
-
const { bondingCurve, treasury } = tokenData;
|
|
239
|
-
const warnings = [];
|
|
240
|
-
// Fetch metadata from URI
|
|
241
|
-
let metadata;
|
|
242
|
-
const uri = (0, program_1.decodeString)(bondingCurve.uri);
|
|
243
|
-
if (uri) {
|
|
244
|
-
try {
|
|
245
|
-
const res = await (0, gateway_1.fetchWithFallback)(uri);
|
|
246
|
-
const data = (await res.json());
|
|
247
|
-
metadata = {
|
|
248
|
-
description: data.description,
|
|
249
|
-
image: data.image && (0, gateway_1.isIrysUrl)(data.image) ? (0, gateway_1.irysToUploader)(data.image) : data.image,
|
|
250
|
-
twitter: data.twitter,
|
|
251
|
-
telegram: data.telegram,
|
|
252
|
-
website: data.website,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
catch (e) {
|
|
256
|
-
warnings.push(`Metadata fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
// Fetch holders count
|
|
260
|
-
let holdersCount = null;
|
|
261
|
-
try {
|
|
262
|
-
const holders = await connection.getTokenLargestAccounts(mint, 'confirmed');
|
|
263
|
-
holdersCount = holders.value.filter((a) => a.uiAmount && a.uiAmount > 0).length;
|
|
264
|
-
}
|
|
265
|
-
catch (e) {
|
|
266
|
-
warnings.push(`Holders fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
267
|
-
}
|
|
268
|
-
// Fetch SOL price
|
|
269
|
-
let solPriceUsd;
|
|
270
|
-
try {
|
|
271
|
-
const res = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd');
|
|
272
|
-
const data = (await res.json());
|
|
273
|
-
solPriceUsd = data?.solana?.usd;
|
|
274
|
-
}
|
|
275
|
-
catch (e) {
|
|
276
|
-
warnings.push(`SOL price fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
277
|
-
}
|
|
278
|
-
return buildTokenDetail(mintStr, bondingCurve, treasury, metadata, holdersCount, solPriceUsd, undefined, warnings);
|
|
279
|
-
};
|
|
280
|
-
exports.getToken = getToken;
|
|
281
|
-
/**
|
|
282
|
-
* Get top holders for a token.
|
|
283
|
-
*/
|
|
284
|
-
const getHolders = async (connection, mintStr, limit = 20) => {
|
|
285
|
-
const mint = new web3_js_1.PublicKey(mintStr);
|
|
286
|
-
const safeLimit = Math.min(limit, 100);
|
|
287
|
-
// Build excluded addresses (pools/vaults)
|
|
288
|
-
const excluded = new Set();
|
|
289
|
-
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
290
|
-
const bondingCurveVault = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, bondingCurvePda, true, constants_1.TOKEN_2022_PROGRAM_ID);
|
|
291
|
-
excluded.add(bondingCurveVault.toString());
|
|
292
|
-
const [treasuryPda] = (0, program_1.getTokenTreasuryPda)(mint);
|
|
293
|
-
const treasuryVault = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, treasuryPda, true, constants_1.TOKEN_2022_PROGRAM_ID);
|
|
294
|
-
excluded.add(treasuryVault.toString());
|
|
295
|
-
try {
|
|
296
|
-
const raydiumAccounts = (0, program_1.getRaydiumMigrationAccounts)(mint);
|
|
297
|
-
excluded.add(raydiumAccounts.token0Vault.toString());
|
|
298
|
-
excluded.add(raydiumAccounts.token1Vault.toString());
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
// Ignore
|
|
302
|
-
}
|
|
303
|
-
const response = await connection.getTokenLargestAccounts(mint, 'confirmed');
|
|
304
|
-
const totalSupply = BigInt(1000000000) * BigInt(10 ** constants_1.TOKEN_DECIMALS);
|
|
305
|
-
const filteredAccounts = response.value
|
|
306
|
-
.filter((account) => account.uiAmount && account.uiAmount > 0)
|
|
307
|
-
.filter((account) => !excluded.has(account.address.toString()))
|
|
308
|
-
.slice(0, safeLimit);
|
|
309
|
-
const accountInfos = await connection.getMultipleParsedAccounts(filteredAccounts.map((a) => a.address));
|
|
310
|
-
const holders = filteredAccounts.map((account, i) => {
|
|
311
|
-
const parsed = accountInfos.value[i]?.data;
|
|
312
|
-
const owner = parsed && 'parsed' in parsed ? parsed.parsed?.info?.owner : null;
|
|
313
|
-
return {
|
|
314
|
-
address: owner || account.address.toString(),
|
|
315
|
-
balance: Number(account.amount) / 10 ** constants_1.TOKEN_DECIMALS,
|
|
316
|
-
percentage: (Number(account.amount) / Number(totalSupply)) * 100,
|
|
317
|
-
};
|
|
318
|
-
});
|
|
319
|
-
return {
|
|
320
|
-
holders,
|
|
321
|
-
total_holders: response.value.filter((a) => a.uiAmount && a.uiAmount > 0 && !excluded.has(a.address.toString())).length,
|
|
322
|
-
};
|
|
323
|
-
};
|
|
324
|
-
exports.getHolders = getHolders;
|
|
325
|
-
/**
|
|
326
|
-
* Get messages (memos) for a token.
|
|
327
|
-
*/
|
|
328
|
-
const getMessages = async (connection, mintStr, limit = 50) => {
|
|
329
|
-
const mint = new web3_js_1.PublicKey(mintStr);
|
|
330
|
-
const safeLimit = Math.min(limit, 100);
|
|
331
|
-
const [bondingCurvePda] = (0, program_1.getBondingCurvePda)(mint);
|
|
332
|
-
// Fetch only as many signatures as needed (keep small to avoid 429s)
|
|
333
|
-
const sigLimit = Math.min(safeLimit, 50);
|
|
334
|
-
const signatures = await connection.getSignaturesForAddress(bondingCurvePda, { limit: sigLimit }, 'confirmed');
|
|
335
|
-
if (signatures.length === 0)
|
|
336
|
-
return { messages: [], total: 0 };
|
|
337
|
-
const messages = [];
|
|
338
|
-
// Batch fetch transactions (1 RPC call per batch instead of 1 per tx)
|
|
339
|
-
const BATCH_SIZE = 100;
|
|
340
|
-
for (let i = 0; i < signatures.length && messages.length < safeLimit; i += BATCH_SIZE) {
|
|
341
|
-
const batch = signatures.slice(i, i + BATCH_SIZE);
|
|
342
|
-
const sigStrings = batch.map((s) => s.signature);
|
|
343
|
-
let txs;
|
|
344
|
-
try {
|
|
345
|
-
txs = await connection.getParsedTransactions(sigStrings, {
|
|
346
|
-
maxSupportedTransactionVersion: 0,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
catch {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
for (let j = 0; j < txs.length && messages.length < safeLimit; j++) {
|
|
353
|
-
const tx = txs[j];
|
|
354
|
-
if (!tx?.meta || tx.meta.err)
|
|
355
|
-
continue;
|
|
356
|
-
for (const ix of tx.transaction.message.instructions) {
|
|
357
|
-
const programId = 'programId' in ix ? ix.programId.toString() : '';
|
|
358
|
-
const programName = 'program' in ix ? ix.program : '';
|
|
359
|
-
const isMemo = programId === constants_1.MEMO_PROGRAM_ID.toString() ||
|
|
360
|
-
programId === 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' ||
|
|
361
|
-
programName === 'spl-memo';
|
|
362
|
-
if (isMemo) {
|
|
363
|
-
let memoText = '';
|
|
364
|
-
if ('parsed' in ix) {
|
|
365
|
-
memoText = typeof ix.parsed === 'string' ? ix.parsed : JSON.stringify(ix.parsed);
|
|
366
|
-
}
|
|
367
|
-
else if ('data' in ix && typeof ix.data === 'string') {
|
|
368
|
-
try {
|
|
369
|
-
const bs58 = await Promise.resolve().then(() => __importStar(require('bs58')));
|
|
370
|
-
const decoded = bs58.default.decode(ix.data);
|
|
371
|
-
memoText = new TextDecoder().decode(decoded);
|
|
372
|
-
}
|
|
373
|
-
catch {
|
|
374
|
-
memoText = ix.data;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
if (memoText && memoText.trim()) {
|
|
378
|
-
const sender = tx.transaction.message.accountKeys[0]?.pubkey?.toString() || 'Unknown';
|
|
379
|
-
messages.push({
|
|
380
|
-
signature: batch[j].signature,
|
|
381
|
-
memo: memoText.trim(),
|
|
382
|
-
sender,
|
|
383
|
-
timestamp: batch[j].blockTime || 0,
|
|
384
|
-
});
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return { messages, total: messages.length };
|
|
392
|
-
};
|
|
393
|
-
exports.getMessages = getMessages;
|
|
394
|
-
// ============================================================================
|
|
395
|
-
// Lending (V2.4)
|
|
396
|
-
// ============================================================================
|
|
397
|
-
// Lending constants (matching the Rust program)
|
|
398
|
-
const INTEREST_RATE_BPS = 200; // 2% per epoch
|
|
399
|
-
const MAX_LTV_BPS = 5000; // 50%
|
|
400
|
-
const LIQUIDATION_THRESHOLD_BPS = 6500; // 65%
|
|
401
|
-
const LIQUIDATION_BONUS_BPS = 1000; // 10%
|
|
402
|
-
/**
|
|
403
|
-
* Get lending info for a migrated token.
|
|
404
|
-
*
|
|
405
|
-
* Returns interest rates, LTV limits, and active loan statistics.
|
|
406
|
-
* Lending is available on all migrated tokens with treasury SOL.
|
|
407
|
-
*/
|
|
408
|
-
const getLendingInfo = async (connection, mintStr) => {
|
|
409
|
-
const mint = new web3_js_1.PublicKey(mintStr);
|
|
410
|
-
const tokenData = await fetchTokenRaw(connection, mint);
|
|
411
|
-
if (!tokenData)
|
|
412
|
-
throw new Error(`Token not found: ${mintStr}`);
|
|
413
|
-
const { bondingCurve, treasury } = tokenData;
|
|
414
|
-
if (!bondingCurve.migrated)
|
|
415
|
-
throw new Error('Token not yet migrated, lending not available');
|
|
416
|
-
const treasurySol = treasury ? Number(treasury.sol_balance.toString()) : 0;
|
|
417
|
-
// Scan for active loan positions via collateral vault balance
|
|
418
|
-
const [collateralVaultPda] = (0, program_1.getCollateralVaultPda)(mint);
|
|
419
|
-
const vaultInfo = await connection.getAccountInfo(collateralVaultPda);
|
|
420
|
-
// Count active loans by scanning LoanPosition accounts
|
|
421
|
-
let activeLoans = 0;
|
|
422
|
-
let totalSolLent = 0;
|
|
423
|
-
const warnings = [];
|
|
424
|
-
try {
|
|
425
|
-
// Derive discriminator from IDL rather than hardcoding
|
|
426
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
427
|
-
const loanDiscriminator = coder.accounts.accountDiscriminator('LoanPosition');
|
|
428
|
-
const accounts = await connection.getProgramAccounts(constants_1.PROGRAM_ID, {
|
|
429
|
-
filters: [
|
|
430
|
-
{ memcmp: { offset: 0, bytes: loanDiscriminator.toString('base64') } },
|
|
431
|
-
{ memcmp: { offset: 8 + 32, bytes: mint.toBase58() } }, // mint at offset 40
|
|
432
|
-
],
|
|
433
|
-
dataSlice: { offset: 8 + 32 + 32, length: 16 }, // collateral_amount + borrowed_amount
|
|
434
|
-
});
|
|
435
|
-
for (const acc of accounts) {
|
|
436
|
-
try {
|
|
437
|
-
// Read borrowed_amount (u64 at offset 8 within the slice)
|
|
438
|
-
const borrowed = acc.account.data.readBigUInt64LE(8);
|
|
439
|
-
if (borrowed > BigInt(0)) {
|
|
440
|
-
activeLoans = (activeLoans ?? 0) + 1;
|
|
441
|
-
totalSolLent = (totalSolLent ?? 0) + Number(borrowed);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
catch {
|
|
445
|
-
// Skip malformed accounts
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
catch (e) {
|
|
450
|
-
activeLoans = null;
|
|
451
|
-
totalSolLent = null;
|
|
452
|
-
warnings.push(`Loan enumeration failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
453
|
-
}
|
|
454
|
-
return {
|
|
455
|
-
interest_rate_bps: INTEREST_RATE_BPS,
|
|
456
|
-
max_ltv_bps: MAX_LTV_BPS,
|
|
457
|
-
liquidation_threshold_bps: LIQUIDATION_THRESHOLD_BPS,
|
|
458
|
-
liquidation_bonus_bps: LIQUIDATION_BONUS_BPS,
|
|
459
|
-
total_sol_lent: totalSolLent,
|
|
460
|
-
active_loans: activeLoans,
|
|
461
|
-
treasury_sol_available: treasurySol,
|
|
462
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
463
|
-
};
|
|
464
|
-
};
|
|
465
|
-
exports.getLendingInfo = getLendingInfo;
|
|
466
|
-
/**
|
|
467
|
-
* Get loan position for a wallet on a specific token.
|
|
468
|
-
*
|
|
469
|
-
* Returns collateral locked, SOL owed, health status, etc.
|
|
470
|
-
* Returns health="none" if no active loan exists.
|
|
471
|
-
*/
|
|
472
|
-
const getLoanPosition = async (connection, mintStr, walletStr) => {
|
|
473
|
-
const mint = new web3_js_1.PublicKey(mintStr);
|
|
474
|
-
const wallet = new web3_js_1.PublicKey(walletStr);
|
|
475
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
476
|
-
const [loanPositionPda] = (0, program_1.getLoanPositionPda)(mint, wallet);
|
|
477
|
-
const accountInfo = await connection.getAccountInfo(loanPositionPda);
|
|
478
|
-
if (!accountInfo) {
|
|
479
|
-
return {
|
|
480
|
-
collateral_amount: 0,
|
|
481
|
-
borrowed_amount: 0,
|
|
482
|
-
accrued_interest: 0,
|
|
483
|
-
total_owed: 0,
|
|
484
|
-
collateral_value_sol: 0,
|
|
485
|
-
current_ltv_bps: 0,
|
|
486
|
-
health: 'none',
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
const loan = coder.accounts.decode('LoanPosition', accountInfo.data);
|
|
490
|
-
const collateral = Number(loan.collateral_amount.toString());
|
|
491
|
-
const borrowed = Number(loan.borrowed_amount.toString());
|
|
492
|
-
const interest = Number(loan.accrued_interest.toString());
|
|
493
|
-
const totalOwed = borrowed + interest;
|
|
494
|
-
// Get collateral value from Raydium pool price
|
|
495
|
-
let collateralValueSol = 0;
|
|
496
|
-
const warnings = [];
|
|
497
|
-
try {
|
|
498
|
-
const raydium = (0, program_1.getRaydiumMigrationAccounts)(mint);
|
|
499
|
-
const [vault0Info, vault1Info] = await Promise.all([
|
|
500
|
-
connection.getTokenAccountBalance(raydium.token0Vault),
|
|
501
|
-
connection.getTokenAccountBalance(raydium.token1Vault),
|
|
502
|
-
]);
|
|
503
|
-
const vault0Amount = Number(vault0Info.value.amount);
|
|
504
|
-
const vault1Amount = Number(vault1Info.value.amount);
|
|
505
|
-
// Determine which vault is SOL and which is token
|
|
506
|
-
if (raydium.isWsolToken0) {
|
|
507
|
-
// token0 = WSOL, token1 = token
|
|
508
|
-
const solReserves = vault0Amount;
|
|
509
|
-
const tokenReserves = vault1Amount;
|
|
510
|
-
if (tokenReserves > 0) {
|
|
511
|
-
collateralValueSol = (collateral * solReserves) / tokenReserves;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
else {
|
|
515
|
-
// token0 = token, token1 = WSOL
|
|
516
|
-
const solReserves = vault1Amount;
|
|
517
|
-
const tokenReserves = vault0Amount;
|
|
518
|
-
if (tokenReserves > 0) {
|
|
519
|
-
collateralValueSol = (collateral * solReserves) / tokenReserves;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
catch (e) {
|
|
524
|
-
collateralValueSol = null;
|
|
525
|
-
warnings.push(`Collateral valuation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
526
|
-
}
|
|
527
|
-
let currentLtvBps;
|
|
528
|
-
if (collateralValueSol === null) {
|
|
529
|
-
currentLtvBps = null;
|
|
530
|
-
}
|
|
531
|
-
else if (collateralValueSol > 0) {
|
|
532
|
-
currentLtvBps = Math.floor((totalOwed / collateralValueSol) * 10000);
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
currentLtvBps = totalOwed > 0 ? 10000 : 0;
|
|
536
|
-
}
|
|
537
|
-
let health;
|
|
538
|
-
if (borrowed === 0 && interest === 0) {
|
|
539
|
-
health = 'none';
|
|
540
|
-
}
|
|
541
|
-
else if (currentLtvBps === null) {
|
|
542
|
-
health = 'healthy';
|
|
543
|
-
}
|
|
544
|
-
else if (currentLtvBps >= LIQUIDATION_THRESHOLD_BPS) {
|
|
545
|
-
health = 'liquidatable';
|
|
546
|
-
}
|
|
547
|
-
else if (currentLtvBps >= MAX_LTV_BPS) {
|
|
548
|
-
health = 'at_risk';
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
health = 'healthy';
|
|
552
|
-
}
|
|
553
|
-
return {
|
|
554
|
-
collateral_amount: collateral,
|
|
555
|
-
borrowed_amount: borrowed,
|
|
556
|
-
accrued_interest: interest,
|
|
557
|
-
total_owed: totalOwed,
|
|
558
|
-
collateral_value_sol: collateralValueSol,
|
|
559
|
-
current_ltv_bps: currentLtvBps,
|
|
560
|
-
health,
|
|
561
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
562
|
-
};
|
|
563
|
-
};
|
|
564
|
-
exports.getLoanPosition = getLoanPosition;
|
|
565
|
-
// ============================================================================
|
|
566
|
-
// Vault Queries (V2.0)
|
|
567
|
-
// ============================================================================
|
|
568
|
-
/**
|
|
569
|
-
* Get vault state by the vault creator's public key.
|
|
570
|
-
*
|
|
571
|
-
* Returns vault balance, authority, linked wallet count, etc.
|
|
572
|
-
* Returns null if no vault exists for this creator.
|
|
573
|
-
*/
|
|
574
|
-
const getVault = async (connection, creatorStr) => {
|
|
575
|
-
const creator = new web3_js_1.PublicKey(creatorStr);
|
|
576
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
577
|
-
const [vaultPda] = (0, program_1.getTorchVaultPda)(creator);
|
|
578
|
-
const accountInfo = await connection.getAccountInfo(vaultPda);
|
|
579
|
-
if (!accountInfo)
|
|
580
|
-
return null;
|
|
581
|
-
const vault = coder.accounts.decode('TorchVault', accountInfo.data);
|
|
582
|
-
return {
|
|
583
|
-
address: vaultPda.toString(),
|
|
584
|
-
creator: vault.creator.toString(),
|
|
585
|
-
authority: vault.authority.toString(),
|
|
586
|
-
sol_balance: Number(vault.sol_balance.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
587
|
-
total_deposited: Number(vault.total_deposited.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
588
|
-
total_withdrawn: Number(vault.total_withdrawn.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
589
|
-
total_spent: Number(vault.total_spent.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
590
|
-
total_received: Number(vault.total_received.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
591
|
-
linked_wallets: vault.linked_wallets,
|
|
592
|
-
created_at: Number(vault.created_at.toString()),
|
|
593
|
-
};
|
|
594
|
-
};
|
|
595
|
-
exports.getVault = getVault;
|
|
596
|
-
/**
|
|
597
|
-
* Get vault state by looking up a linked wallet's VaultWalletLink.
|
|
598
|
-
*
|
|
599
|
-
* Useful when you have an agent wallet and need to find its vault.
|
|
600
|
-
* Returns null if the wallet is not linked to any vault.
|
|
601
|
-
*/
|
|
602
|
-
const getVaultForWallet = async (connection, walletStr) => {
|
|
603
|
-
const wallet = new web3_js_1.PublicKey(walletStr);
|
|
604
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
605
|
-
const [walletLinkPda] = (0, program_1.getVaultWalletLinkPda)(wallet);
|
|
606
|
-
const linkInfo = await connection.getAccountInfo(walletLinkPda);
|
|
607
|
-
if (!linkInfo)
|
|
608
|
-
return null;
|
|
609
|
-
const link = coder.accounts.decode('VaultWalletLink', linkInfo.data);
|
|
610
|
-
// Now fetch the vault using the vault PDA stored in the link
|
|
611
|
-
const vaultInfo = await connection.getAccountInfo(link.vault);
|
|
612
|
-
if (!vaultInfo)
|
|
613
|
-
return null;
|
|
614
|
-
const vault = coder.accounts.decode('TorchVault', vaultInfo.data);
|
|
615
|
-
return {
|
|
616
|
-
address: link.vault.toString(),
|
|
617
|
-
creator: vault.creator.toString(),
|
|
618
|
-
authority: vault.authority.toString(),
|
|
619
|
-
sol_balance: Number(vault.sol_balance.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
620
|
-
total_deposited: Number(vault.total_deposited.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
621
|
-
total_withdrawn: Number(vault.total_withdrawn.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
622
|
-
total_spent: Number(vault.total_spent.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
623
|
-
total_received: Number(vault.total_received.toString()) / constants_1.LAMPORTS_PER_SOL,
|
|
624
|
-
linked_wallets: vault.linked_wallets,
|
|
625
|
-
created_at: Number(vault.created_at.toString()),
|
|
626
|
-
};
|
|
627
|
-
};
|
|
628
|
-
exports.getVaultForWallet = getVaultForWallet;
|
|
629
|
-
/**
|
|
630
|
-
* Get wallet link state for a specific wallet.
|
|
631
|
-
*
|
|
632
|
-
* Returns the link info (which vault it's linked to, when) or null if not linked.
|
|
633
|
-
*/
|
|
634
|
-
const getVaultWalletLink = async (connection, walletStr) => {
|
|
635
|
-
const wallet = new web3_js_1.PublicKey(walletStr);
|
|
636
|
-
const coder = new anchor_1.BorshCoder(torch_market_json_1.default);
|
|
637
|
-
const [walletLinkPda] = (0, program_1.getVaultWalletLinkPda)(wallet);
|
|
638
|
-
const accountInfo = await connection.getAccountInfo(walletLinkPda);
|
|
639
|
-
if (!accountInfo)
|
|
640
|
-
return null;
|
|
641
|
-
const link = coder.accounts.decode('VaultWalletLink', accountInfo.data);
|
|
642
|
-
return {
|
|
643
|
-
address: walletLinkPda.toString(),
|
|
644
|
-
vault: link.vault.toString(),
|
|
645
|
-
wallet: link.wallet.toString(),
|
|
646
|
-
linked_at: Number(link.linked_at.toString()),
|
|
647
|
-
};
|
|
648
|
-
};
|
|
649
|
-
exports.getVaultWalletLink = getVaultWalletLink;
|
|
650
|
-
//# sourceMappingURL=tokens.js.map
|