@defillama/sdk 5.0.235 → 5.0.236
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/build/ChainApi.d.ts +23 -1
- package/build/chains/algorand.d.ts +277 -0
- package/build/chains/algorand.js +618 -0
- package/build/chains/aptos.d.ts +230 -0
- package/build/chains/aptos.js +612 -0
- package/build/chains/cardano.d.ts +242 -0
- package/build/chains/cardano.js +407 -0
- package/build/chains/cosmos.d.ts +252 -0
- package/build/chains/cosmos.js +618 -0
- package/build/chains/icp.d.ts +235 -0
- package/build/chains/icp.js +1040 -0
- package/build/chains/index.d.ts +23 -0
- package/build/chains/index.js +59 -0
- package/build/chains/near.d.ts +192 -0
- package/build/chains/near.js +446 -0
- package/build/chains/rpc.d.ts +125 -0
- package/build/chains/rpc.js +326 -0
- package/build/chains/starknet.d.ts +205 -0
- package/build/chains/starknet.js +884 -0
- package/build/chains/stellar.d.ts +410 -0
- package/build/chains/stellar.js +1082 -0
- package/build/chains/substrate.d.ts +333 -0
- package/build/chains/substrate.js +1042 -0
- package/build/chains/sui.d.ts +365 -0
- package/build/chains/sui.js +1135 -0
- package/build/chains/svm.d.ts +340 -0
- package/build/chains/svm.js +760 -0
- package/build/chains/tezos.d.ts +225 -0
- package/build/chains/tezos.js +412 -0
- package/build/chains/ton.d.ts +353 -0
- package/build/chains/ton.js +788 -0
- package/build/chains/tron.d.ts +45 -0
- package/build/chains/tron.js +110 -0
- package/build/chains/utxo.d.ts +202 -0
- package/build/chains/utxo.js +592 -0
- package/build/chains/xrpl.d.ts +179 -0
- package/build/chains/xrpl.js +467 -0
- package/build/index.d.ts +2 -1
- package/build/index.js +3 -1
- package/build/providers.json +1 -1
- package/build/util/chainUtils/data.json +11 -0
- package/build/util/chainUtils/index.d.ts +1 -0
- package/build/util/chainUtils/index.js +23 -0
- package/build/util/common.js +2 -3
- package/build/util/cosmos.d.ts +1 -11
- package/build/util/cosmos.js +6 -66
- package/build/util/env.js +1 -0
- package/build/util/indexer.compatibility.d.ts +6 -0
- package/build/util/indexer.compatibility.js +64 -0
- package/build/util/indexer.d.ts +24 -2
- package/build/util/indexer.js +215 -282
- package/build/util/indexerSql.d.ts +8 -1
- package/build/util/indexerSql.js +53 -22
- package/build/util/logs.decode.shared.js +5 -4
- package/build/util/transactions.d.ts +23 -1
- package/build/util/updateChainLabels.d.ts +1 -0
- package/build/util/updateChainLabels.js +5 -0
- package/build/util/updateProviderList.js +38 -17
- package/package.json +20 -7
- package/README.md +0 -41
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LAMPORTS_PER_SOL = exports.TOKEN_ACCOUNT_SIZE = exports.MINT_ACCOUNT_SIZE = exports.SYSVAR_RENT_ID = exports.SYSTEM_PROGRAM_ID = exports.STAKE_PROGRAM_ID = exports.ASSOCIATED_TOKEN_PROGRAM_ID = exports.TOKEN_2022_PROGRAM_ID = exports.TOKEN_PROGRAM_ID = exports.svmChains = exports.DEFAULT_ENDPOINTS = void 0;
|
|
4
|
+
exports.isSvmChain = isSvmChain;
|
|
5
|
+
exports.getEndpoints = getEndpoints;
|
|
6
|
+
exports.getEndpoint = getEndpoint;
|
|
7
|
+
exports.base58Encode = base58Encode;
|
|
8
|
+
exports.base58Decode = base58Decode;
|
|
9
|
+
exports.isValidPublicKey = isValidPublicKey;
|
|
10
|
+
exports.readBigUInt64LE = readBigUInt64LE;
|
|
11
|
+
exports.i80f48ToNumber = i80f48ToNumber;
|
|
12
|
+
exports.readI80F48 = readI80F48;
|
|
13
|
+
exports.decodeMintAccount = decodeMintAccount;
|
|
14
|
+
exports.decodeTokenAccount = decodeTokenAccount;
|
|
15
|
+
exports.readScaledUiMultiplier = readScaledUiMultiplier;
|
|
16
|
+
exports.decodeStakePool = decodeStakePool;
|
|
17
|
+
exports.extractPubkey = extractPubkey;
|
|
18
|
+
exports.isOnCurve = isOnCurve;
|
|
19
|
+
exports.usesWeb3js = usesWeb3js;
|
|
20
|
+
exports.createProgramAddress = createProgramAddress;
|
|
21
|
+
exports.findProgramAddress = findProgramAddress;
|
|
22
|
+
exports.getAssociatedTokenAddress = getAssociatedTokenAddress;
|
|
23
|
+
exports.call = call;
|
|
24
|
+
exports.getSlot = getSlot;
|
|
25
|
+
exports.getBlockTime = getBlockTime;
|
|
26
|
+
exports.getBlockHeight = getBlockHeight;
|
|
27
|
+
exports.getLatestBlock = getLatestBlock;
|
|
28
|
+
exports.getEpochInfo = getEpochInfo;
|
|
29
|
+
exports.getBalance = getBalance;
|
|
30
|
+
exports.getBalances = getBalances;
|
|
31
|
+
exports.getAccountInfo = getAccountInfo;
|
|
32
|
+
exports.getAccountBuffer = getAccountBuffer;
|
|
33
|
+
exports.getAccounts = getAccounts;
|
|
34
|
+
exports.getAccountBuffers = getAccountBuffers;
|
|
35
|
+
exports.getProgramAccounts = getProgramAccounts;
|
|
36
|
+
exports.getTokenSupply = getTokenSupply;
|
|
37
|
+
exports.getTokenSupplies = getTokenSupplies;
|
|
38
|
+
exports.getTokenAccountBalances = getTokenAccountBalances;
|
|
39
|
+
exports.getTokenAccountsByOwner = getTokenAccountsByOwner;
|
|
40
|
+
exports.getTokenBalance = getTokenBalance;
|
|
41
|
+
exports.getSignaturesForAddress = getSignaturesForAddress;
|
|
42
|
+
exports.getTransaction = getTransaction;
|
|
43
|
+
exports.getStakedSol = getStakedSol;
|
|
44
|
+
exports.getSolBalanceFromStakePool = getSolBalanceFromStakePool;
|
|
45
|
+
/**
|
|
46
|
+
* Solana-VM family client (solana, eclipse, soon, soon_base, soon_bsc, fogo,
|
|
47
|
+
* cookiechain, renec) over raw JSON-RPC. No hard `@solana/web3.js` dependency: base58,
|
|
48
|
+
* SPL token layouts, PDA derivation (with a real ed25519 on-curve check) and the
|
|
49
|
+
* Token-2022 ScaledUiAmount extension are implemented here. When `@solana/web3.js`
|
|
50
|
+
* is installed in the host repo (optional peer dependency) PDA derivation is
|
|
51
|
+
* delegated to it, see `usesWeb3js`.
|
|
52
|
+
*
|
|
53
|
+
* Replaces the RPC / codec parts of:
|
|
54
|
+
* - DefiLlama-Adapters projects/helper/solana.js (getConnection, getProvider,
|
|
55
|
+
* getAssociatedTokenAddress, getTokenSupplies, getTokenAccountBalances,
|
|
56
|
+
* getMultipleAccounts, getEndpoint, readBigUInt64LE, getStakedSol,
|
|
57
|
+
* getSolBalanceFromStakePool, runInChunks, i80f48ToNumber)
|
|
58
|
+
* - DefiLlama-Adapters projects/helper/svmChainConfig.js (chain -> env key map)
|
|
59
|
+
* - DefiLlama-Adapters projects/helper/env.js (SVM `<CHAIN>_RPC` defaults)
|
|
60
|
+
* - DefiLlama-Adapters projects/helper/utils/solana/layout.js (mint / tokenAccount decoders)
|
|
61
|
+
* - defillama-server coins/src/adapters/solana/utils.ts (getTokenSupplies,
|
|
62
|
+
* getTokenAccountBalances, getMultipleAccounts, getMultipleAccountBuffers)
|
|
63
|
+
* - defillama-server defi/l2/utils.ts (readScaledUiMultiplier, getSolanaTokenSupply)
|
|
64
|
+
* - peggedassets-server src/adapters/peggedAssets/helper/solana.js (getTokenSupply, getTokenBalance)
|
|
65
|
+
* - dimension-adapters helpers/solana.ts (getTokenSupply, getTokenBalance)
|
|
66
|
+
* - dimension-adapters fees/marginfi/index.ts (readI80F48, getProgramAccounts with retry)
|
|
67
|
+
* - dimension-adapters fees/exponent/index.ts (extractPubkey)
|
|
68
|
+
* - dimension-adapters fees/neutral-trade/index.ts (b58decode, b58encode, derivePDA)
|
|
69
|
+
* - dimension-adapters fees/solstice-usx/index.ts (getSignaturesForAddress)
|
|
70
|
+
* - dimension-adapters fees/save-staked-sol/index.ts (getAccountInfo, stake pool lamports)
|
|
71
|
+
*
|
|
72
|
+
* TVL coupling (Balances / ChainApi / coingecko maps / sumTokens) is intentionally
|
|
73
|
+
* left out; every function returns raw chain values.
|
|
74
|
+
*
|
|
75
|
+
* Usage: `sdk.chains.svm.getTokenSupply({ chain: 'solana', token })`
|
|
76
|
+
*/
|
|
77
|
+
const crypto_1 = require("crypto");
|
|
78
|
+
const env_1 = require("../util/env");
|
|
79
|
+
const debugLog_1 = require("../util/debugLog");
|
|
80
|
+
const rpc_1 = require("./rpc");
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// config
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/** Built-in endpoints, overridable with `<CHAIN>_RPC` (also `SDK_` / `LLAMA_SDK_` prefixed). */
|
|
85
|
+
exports.DEFAULT_ENDPOINTS = {
|
|
86
|
+
solana: "https://api.mainnet-beta.solana.com",
|
|
87
|
+
soon: "https://rpc.mainnet.soo.network/rpc",
|
|
88
|
+
soon_base: "https://rpc.soonbase.soo.network/rpc",
|
|
89
|
+
soon_bsc: "https://rpc.svmbnbmainnet.soo.network/rpc",
|
|
90
|
+
eclipse: "https://mainnetbeta-rpc.eclipse.xyz",
|
|
91
|
+
renec: "https://api-mainnet-beta.renec.foundation:8899/",
|
|
92
|
+
fogo: "https://mainnet.fogo.io",
|
|
93
|
+
cookiechain: "https://rpc.cookiescan.io",
|
|
94
|
+
};
|
|
95
|
+
exports.svmChains = Object.keys(exports.DEFAULT_ENDPOINTS);
|
|
96
|
+
const svmChainSet = new Set(exports.svmChains);
|
|
97
|
+
function isSvmChain(chain) {
|
|
98
|
+
return svmChainSet.has(chain);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Endpoint list for a chain: `<CHAIN>_RPC` env wins, then `DEFAULT_ENDPOINTS`.
|
|
102
|
+
* For solana with `isClient`, `SOLANA_RPC_CLIENT` is checked first.
|
|
103
|
+
*/
|
|
104
|
+
function getEndpoints({ chain = 'solana', isClient = false } = {}) {
|
|
105
|
+
if (isClient && chain === 'solana') {
|
|
106
|
+
const client = (0, env_1.getEnvValue)('SOLANA_RPC_CLIENT');
|
|
107
|
+
if (client)
|
|
108
|
+
return client.split(',').map(i => i.trim()).filter(Boolean);
|
|
109
|
+
}
|
|
110
|
+
return (0, rpc_1.getEndpoints)(chain, exports.DEFAULT_ENDPOINTS[chain]);
|
|
111
|
+
}
|
|
112
|
+
function getEndpoint(options = {}) {
|
|
113
|
+
return getEndpoints(options)[0];
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// constants
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
exports.TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
|
|
119
|
+
exports.TOKEN_2022_PROGRAM_ID = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
|
|
120
|
+
exports.ASSOCIATED_TOKEN_PROGRAM_ID = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
|
|
121
|
+
exports.STAKE_PROGRAM_ID = 'Stake11111111111111111111111111111111111111';
|
|
122
|
+
exports.SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
123
|
+
exports.SYSVAR_RENT_ID = 'SysvarRent111111111111111111111111111111111';
|
|
124
|
+
exports.MINT_ACCOUNT_SIZE = 82;
|
|
125
|
+
exports.TOKEN_ACCOUNT_SIZE = 165;
|
|
126
|
+
exports.LAMPORTS_PER_SOL = 1000000000;
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// base58
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
const B58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
131
|
+
const B58_MAP = {};
|
|
132
|
+
for (let i = 0; i < B58_ALPHABET.length; i++)
|
|
133
|
+
B58_MAP[B58_ALPHABET[i]] = i;
|
|
134
|
+
/** Bitcoin-style base58 (leading zero bytes become leading '1's). */
|
|
135
|
+
function base58Encode(bytes) {
|
|
136
|
+
if (!bytes.length)
|
|
137
|
+
return '';
|
|
138
|
+
let zeros = 0;
|
|
139
|
+
while (zeros < bytes.length && bytes[zeros] === 0)
|
|
140
|
+
zeros++;
|
|
141
|
+
// upper bound on output length: log(256)/log(58) ~ 1.37
|
|
142
|
+
const size = Math.ceil(((bytes.length - zeros) * 138) / 100) + 1;
|
|
143
|
+
const digits = new Uint8Array(size);
|
|
144
|
+
let length = 0;
|
|
145
|
+
for (let i = zeros; i < bytes.length; i++) {
|
|
146
|
+
let carry = bytes[i];
|
|
147
|
+
let j = 0;
|
|
148
|
+
for (let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++) {
|
|
149
|
+
carry += 256 * digits[k];
|
|
150
|
+
digits[k] = carry % 58;
|
|
151
|
+
carry = Math.floor(carry / 58);
|
|
152
|
+
}
|
|
153
|
+
length = j;
|
|
154
|
+
}
|
|
155
|
+
let start = size - length;
|
|
156
|
+
while (start < size && digits[start] === 0)
|
|
157
|
+
start++;
|
|
158
|
+
let out = '1'.repeat(zeros);
|
|
159
|
+
for (let i = start; i < size; i++)
|
|
160
|
+
out += B58_ALPHABET[digits[i]];
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
function base58Decode(str) {
|
|
164
|
+
if (typeof str !== 'string')
|
|
165
|
+
throw new Error('base58Decode: expected string');
|
|
166
|
+
if (!str.length)
|
|
167
|
+
return new Uint8Array(0);
|
|
168
|
+
let zeros = 0;
|
|
169
|
+
while (zeros < str.length && str[zeros] === '1')
|
|
170
|
+
zeros++;
|
|
171
|
+
const size = Math.ceil(((str.length - zeros) * 733) / 1000) + 1; // log(58)/log(256) ~ 0.733
|
|
172
|
+
const bytes = new Uint8Array(size);
|
|
173
|
+
let length = 0;
|
|
174
|
+
for (let i = zeros; i < str.length; i++) {
|
|
175
|
+
let carry = B58_MAP[str[i]];
|
|
176
|
+
if (carry === undefined)
|
|
177
|
+
throw new Error(`base58Decode: invalid character "${str[i]}"`);
|
|
178
|
+
let j = 0;
|
|
179
|
+
for (let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++) {
|
|
180
|
+
carry += 58 * bytes[k];
|
|
181
|
+
bytes[k] = carry & 0xff;
|
|
182
|
+
carry >>= 8;
|
|
183
|
+
}
|
|
184
|
+
length = j;
|
|
185
|
+
}
|
|
186
|
+
let start = size - length;
|
|
187
|
+
while (start < size && bytes[start] === 0)
|
|
188
|
+
start++;
|
|
189
|
+
const out = new Uint8Array(zeros + (size - start));
|
|
190
|
+
out.set(bytes.subarray(start), zeros);
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function isValidPublicKey(str) {
|
|
194
|
+
if (typeof str !== 'string' || str.length < 32 || str.length > 44)
|
|
195
|
+
return false;
|
|
196
|
+
try {
|
|
197
|
+
return base58Decode(str).length === 32;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function toPubkeyBytes(key, label = 'public key') {
|
|
204
|
+
const bytes = typeof key === 'string' ? base58Decode(key) : key;
|
|
205
|
+
if (bytes.length !== 32)
|
|
206
|
+
throw new Error(`Invalid ${label}: ${typeof key === 'string' ? key : `${bytes.length} bytes`}`);
|
|
207
|
+
return Buffer.from(bytes);
|
|
208
|
+
}
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// numeric codecs
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
/** Little-endian u64 -> bigint, works on any Uint8Array (not only Buffer). */
|
|
213
|
+
function readBigUInt64LE(buffer, offset = 0) {
|
|
214
|
+
const first = buffer[offset];
|
|
215
|
+
const last = buffer[offset + 7];
|
|
216
|
+
if (first === undefined || last === undefined)
|
|
217
|
+
throw new Error(`readBigUInt64LE: out of range (offset ${offset}, length ${buffer.length})`);
|
|
218
|
+
const lo = first + buffer[offset + 1] * 2 ** 8 + buffer[offset + 2] * 2 ** 16 + buffer[offset + 3] * 2 ** 24;
|
|
219
|
+
const hi = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + last * 2 ** 24;
|
|
220
|
+
return BigInt(lo) + (BigInt(hi) << BigInt(32));
|
|
221
|
+
}
|
|
222
|
+
function readU32LE(buffer, offset) {
|
|
223
|
+
return (buffer[offset] + buffer[offset + 1] * 2 ** 8 + buffer[offset + 2] * 2 ** 16 + buffer[offset + 3] * 2 ** 24) >>> 0;
|
|
224
|
+
}
|
|
225
|
+
const TWO_POW_48 = BigInt(2) ** BigInt(48);
|
|
226
|
+
const TWO_POW_127 = BigInt(2) ** BigInt(127);
|
|
227
|
+
const TWO_POW_128 = BigInt(2) ** BigInt(128);
|
|
228
|
+
const MASK_48 = TWO_POW_48 - BigInt(1);
|
|
229
|
+
/**
|
|
230
|
+
* Fixed point I80F48 (signed i128, 48 fractional bits) -> number.
|
|
231
|
+
* Accepts a bigint / decimal string, `{ val }` / `{ value }` wrappers (anchor BN
|
|
232
|
+
* style) or the 16 little-endian bytes of the raw i128.
|
|
233
|
+
*/
|
|
234
|
+
function i80f48ToNumber(i80f48) {
|
|
235
|
+
var _a;
|
|
236
|
+
let raw = i80f48;
|
|
237
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw) && !(raw instanceof Uint8Array))
|
|
238
|
+
raw = (_a = raw.val) !== null && _a !== void 0 ? _a : raw.value;
|
|
239
|
+
let value;
|
|
240
|
+
if (Array.isArray(raw) || raw instanceof Uint8Array) {
|
|
241
|
+
if (raw.length !== 16)
|
|
242
|
+
throw new Error(`i80f48ToNumber: expected 16 bytes, got ${raw.length}`);
|
|
243
|
+
value = BigInt(0);
|
|
244
|
+
for (let i = 15; i >= 0; i--)
|
|
245
|
+
value = (value << BigInt(8)) + BigInt(raw[i]);
|
|
246
|
+
if (value >= TWO_POW_127)
|
|
247
|
+
value -= TWO_POW_128;
|
|
248
|
+
}
|
|
249
|
+
else if (typeof raw === 'bigint') {
|
|
250
|
+
value = raw;
|
|
251
|
+
}
|
|
252
|
+
else if (typeof raw === 'string' || typeof raw === 'number') {
|
|
253
|
+
value = BigInt(raw);
|
|
254
|
+
}
|
|
255
|
+
else if (raw && typeof raw.toString === 'function') {
|
|
256
|
+
value = BigInt(raw.toString());
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
throw new Error('i80f48ToNumber: unsupported input');
|
|
260
|
+
}
|
|
261
|
+
const integerPart = value >> BigInt(48);
|
|
262
|
+
const fractionalPart = value & MASK_48;
|
|
263
|
+
return Number(integerPart) + Number(fractionalPart) / Number(TWO_POW_48);
|
|
264
|
+
}
|
|
265
|
+
/** Little-endian I80F48 at `offset` (marginfi WrappedI80F48). */
|
|
266
|
+
function readI80F48(buf, offset) {
|
|
267
|
+
return i80f48ToNumber(buf.subarray(offset, offset + 16));
|
|
268
|
+
}
|
|
269
|
+
/** Decode an SPL / Token-2022 mint account (first 82 bytes; extensions are ignored). */
|
|
270
|
+
function decodeMintAccount(buf) {
|
|
271
|
+
var _a;
|
|
272
|
+
if (!buf || buf.length < exports.MINT_ACCOUNT_SIZE)
|
|
273
|
+
throw new Error(`decodeMintAccount: need ${exports.MINT_ACCOUNT_SIZE} bytes, got ${(_a = buf === null || buf === void 0 ? void 0 : buf.length) !== null && _a !== void 0 ? _a : 0}`);
|
|
274
|
+
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
275
|
+
return {
|
|
276
|
+
mintAuthority: readU32LE(b, 0) ? base58Encode(b.subarray(4, 36)) : null,
|
|
277
|
+
supply: readBigUInt64LE(b, 36).toString(),
|
|
278
|
+
decimals: b[44],
|
|
279
|
+
isInitialized: b[45] !== 0,
|
|
280
|
+
freezeAuthority: readU32LE(b, 46) ? base58Encode(b.subarray(50, 82)) : null,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/** Decode an SPL / Token-2022 token account (first 165 bytes; extensions are ignored). */
|
|
284
|
+
function decodeTokenAccount(buf) {
|
|
285
|
+
var _a;
|
|
286
|
+
if (!buf || buf.length < exports.TOKEN_ACCOUNT_SIZE)
|
|
287
|
+
throw new Error(`decodeTokenAccount: need ${exports.TOKEN_ACCOUNT_SIZE} bytes, got ${(_a = buf === null || buf === void 0 ? void 0 : buf.length) !== null && _a !== void 0 ? _a : 0}`);
|
|
288
|
+
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
289
|
+
return {
|
|
290
|
+
mint: base58Encode(b.subarray(0, 32)),
|
|
291
|
+
owner: base58Encode(b.subarray(32, 64)),
|
|
292
|
+
amount: readBigUInt64LE(b, 64).toString(),
|
|
293
|
+
delegate: readU32LE(b, 72) ? base58Encode(b.subarray(76, 108)) : null,
|
|
294
|
+
state: b[108],
|
|
295
|
+
isNative: readU32LE(b, 109) ? readBigUInt64LE(b, 113).toString() : null,
|
|
296
|
+
delegatedAmount: readBigUInt64LE(b, 121).toString(),
|
|
297
|
+
closeAuthority: readU32LE(b, 129) ? base58Encode(b.subarray(133, 165)) : null,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
// Token-2022 ExtensionType::ScaledUiAmountConfig (=25): displayed balance = raw x multiplier.
|
|
301
|
+
// A mint is padded to the 165-byte Account length; account_type sits at 165 and the TLV
|
|
302
|
+
// extension list begins at 166.
|
|
303
|
+
const T22_TLV_START = 166;
|
|
304
|
+
const EXT_SCALED_UI_AMOUNT = 25;
|
|
305
|
+
/**
|
|
306
|
+
* Currently-effective UI multiplier of a Token-2022 mint with the ScaledUiAmount
|
|
307
|
+
* extension, or 1 for a classic SPL mint / a mint without this extension.
|
|
308
|
+
*/
|
|
309
|
+
function readScaledUiMultiplier(data, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
310
|
+
if (!data || data.length <= exports.TOKEN_ACCOUNT_SIZE)
|
|
311
|
+
return 1;
|
|
312
|
+
let off = T22_TLV_START;
|
|
313
|
+
while (off + 4 <= data.length) {
|
|
314
|
+
const extType = data.readUInt16LE(off);
|
|
315
|
+
const len = data.readUInt16LE(off + 2);
|
|
316
|
+
const dataStart = off + 4;
|
|
317
|
+
if (extType === EXT_SCALED_UI_AMOUNT && dataStart + 56 <= data.length) {
|
|
318
|
+
// authority(32) multiplier:f64(8) newMultiplierEffectiveTimestamp:i64(8) newMultiplier:f64(8)
|
|
319
|
+
const p = dataStart + 32;
|
|
320
|
+
const multiplier = data.readDoubleLE(p);
|
|
321
|
+
const effectiveTs = Number(data.readBigInt64LE(p + 8));
|
|
322
|
+
const newMultiplier = data.readDoubleLE(p + 16);
|
|
323
|
+
const m = nowSeconds >= effectiveTs ? newMultiplier : multiplier;
|
|
324
|
+
return Number.isFinite(m) && m > 0 ? m : 1; // never zero/NaN out a real supply
|
|
325
|
+
}
|
|
326
|
+
if (extType === 0 && len === 0)
|
|
327
|
+
break; // uninitialized padding
|
|
328
|
+
off = dataStart + len;
|
|
329
|
+
}
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
/** SPL stake pool: `total_lamports` u64 at offset 258, `pool_token_supply` u64 at 266. */
|
|
333
|
+
function decodeStakePool(buf) {
|
|
334
|
+
var _a;
|
|
335
|
+
if (!buf || buf.length < 274)
|
|
336
|
+
throw new Error(`decodeStakePool: need 274 bytes, got ${(_a = buf === null || buf === void 0 ? void 0 : buf.length) !== null && _a !== void 0 ? _a : 0}`);
|
|
337
|
+
return {
|
|
338
|
+
totalLamports: readBigUInt64LE(buf, 258).toString(),
|
|
339
|
+
poolTokenSupply: readBigUInt64LE(buf, 266).toString(),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/** Read the 32-byte pubkey at `offset` of a base64 encoded account payload. */
|
|
343
|
+
function extractPubkey(base64Data, offset) {
|
|
344
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
345
|
+
if (buffer.length < offset + 32)
|
|
346
|
+
throw new Error(`extractPubkey: offset ${offset} out of range (length ${buffer.length})`);
|
|
347
|
+
return base58Encode(buffer.subarray(offset, offset + 32));
|
|
348
|
+
}
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
// ed25519 / PDA derivation
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
const P = (BigInt(1) << BigInt(255)) - BigInt(19);
|
|
353
|
+
const ONE = BigInt(1);
|
|
354
|
+
const ZERO = BigInt(0);
|
|
355
|
+
function mod(a, m = P) {
|
|
356
|
+
const r = a % m;
|
|
357
|
+
return r < ZERO ? r + m : r;
|
|
358
|
+
}
|
|
359
|
+
function modPow(base, exp, m = P) {
|
|
360
|
+
let result = ONE;
|
|
361
|
+
base = mod(base, m);
|
|
362
|
+
while (exp > ZERO) {
|
|
363
|
+
if (exp & ONE)
|
|
364
|
+
result = (result * base) % m;
|
|
365
|
+
base = (base * base) % m;
|
|
366
|
+
exp >>= ONE;
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
function modInv(a, m = P) {
|
|
371
|
+
// p is prime: a^(p-2)
|
|
372
|
+
return modPow(a, m - BigInt(2), m);
|
|
373
|
+
}
|
|
374
|
+
// d = -121665 / 121666 mod p
|
|
375
|
+
const ED25519_D = mod(-BigInt(121665) * modInv(BigInt(121666)));
|
|
376
|
+
const LEGENDRE_EXP = (P - ONE) / BigInt(2);
|
|
377
|
+
/**
|
|
378
|
+
* True when the 32 bytes decompress to a point on the ed25519 curve
|
|
379
|
+
* (curve25519-dalek `CompressedEdwardsY::decompress` semantics). A PDA must be
|
|
380
|
+
* off-curve so it can never have a private key.
|
|
381
|
+
*/
|
|
382
|
+
function isOnCurve(pubkey) {
|
|
383
|
+
const bytes = toPubkeyBytes(pubkey);
|
|
384
|
+
let y = ZERO;
|
|
385
|
+
for (let i = 31; i >= 0; i--)
|
|
386
|
+
y = (y << BigInt(8)) + BigInt(bytes[i]);
|
|
387
|
+
y &= (ONE << BigInt(255)) - ONE; // drop the sign bit
|
|
388
|
+
y = mod(y);
|
|
389
|
+
const y2 = (y * y) % P;
|
|
390
|
+
const u = mod(y2 - ONE); // y^2 - 1
|
|
391
|
+
const v = mod(ED25519_D * y2 + ONE); // d*y^2 + 1
|
|
392
|
+
// x^2 = u / v ; sqrt_ratio_i: valid when u == 0, invalid when v == 0 (u != 0),
|
|
393
|
+
// otherwise valid iff u/v is a quadratic residue
|
|
394
|
+
if (u === ZERO)
|
|
395
|
+
return true;
|
|
396
|
+
if (v === ZERO)
|
|
397
|
+
return false;
|
|
398
|
+
const x2 = (u * modInv(v)) % P;
|
|
399
|
+
return modPow(x2, LEGENDRE_EXP) === ONE;
|
|
400
|
+
}
|
|
401
|
+
const PDA_MARKER = Buffer.from('ProgramDerivedAddress');
|
|
402
|
+
const MAX_SEED_LENGTH = 32;
|
|
403
|
+
const MAX_SEEDS = 16;
|
|
404
|
+
/**
|
|
405
|
+
* `@solana/web3.js` is an optional peer dependency: when the host repo has it
|
|
406
|
+
* installed (DefiLlama-Adapters, server/coins) PDA derivation is delegated to
|
|
407
|
+
* `PublicKey.createProgramAddressSync` / `findProgramAddressSync`, and the
|
|
408
|
+
* hand-rolled ed25519 check above is only the fallback for repos without it.
|
|
409
|
+
* Set `SVM_DISABLE_WEB3JS=true` to force the built-in implementation.
|
|
410
|
+
*/
|
|
411
|
+
let web3js;
|
|
412
|
+
function loadWeb3js() {
|
|
413
|
+
var _a;
|
|
414
|
+
if (web3js !== undefined)
|
|
415
|
+
return web3js;
|
|
416
|
+
if ((0, env_1.getEnvValue)('SVM_DISABLE_WEB3JS') === 'true')
|
|
417
|
+
return (web3js = null);
|
|
418
|
+
try {
|
|
419
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
420
|
+
web3js = require('@solana/web3.js');
|
|
421
|
+
if (typeof ((_a = web3js === null || web3js === void 0 ? void 0 : web3js.PublicKey) === null || _a === void 0 ? void 0 : _a.findProgramAddressSync) !== 'function')
|
|
422
|
+
web3js = null;
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
web3js = null;
|
|
426
|
+
}
|
|
427
|
+
return web3js;
|
|
428
|
+
}
|
|
429
|
+
/** True when PDA derivation is delegated to an installed `@solana/web3.js`. */
|
|
430
|
+
function usesWeb3js() {
|
|
431
|
+
return !!loadWeb3js();
|
|
432
|
+
}
|
|
433
|
+
function toSeedBuffer(seed) {
|
|
434
|
+
const buf = typeof seed === 'string' ? Buffer.from(seed, 'utf8') : Buffer.from(seed);
|
|
435
|
+
if (buf.length > MAX_SEED_LENGTH)
|
|
436
|
+
throw new Error(`Max seed length exceeded (${buf.length} > ${MAX_SEED_LENGTH})`);
|
|
437
|
+
return buf;
|
|
438
|
+
}
|
|
439
|
+
/** sha256(seeds || programId || "ProgramDerivedAddress"); throws when the result is on-curve. */
|
|
440
|
+
function createProgramAddress(seeds, programId, bump) {
|
|
441
|
+
if (seeds.length > MAX_SEEDS)
|
|
442
|
+
throw new Error(`Max seeds exceeded (${seeds.length} > ${MAX_SEEDS})`);
|
|
443
|
+
const web3 = loadWeb3js();
|
|
444
|
+
if (web3) {
|
|
445
|
+
const allSeeds = seeds.map(toSeedBuffer);
|
|
446
|
+
if (bump !== undefined)
|
|
447
|
+
allSeeds.push(Buffer.from([bump]));
|
|
448
|
+
return web3.PublicKey.createProgramAddressSync(allSeeds, new web3.PublicKey(programId)).toBase58();
|
|
449
|
+
}
|
|
450
|
+
const hash = (0, crypto_1.createHash)('sha256');
|
|
451
|
+
for (const seed of seeds)
|
|
452
|
+
hash.update(toSeedBuffer(seed));
|
|
453
|
+
if (bump !== undefined) {
|
|
454
|
+
if (!Number.isInteger(bump) || bump < 0 || bump > 255)
|
|
455
|
+
throw new Error(`Invalid bump seed: ${bump}`);
|
|
456
|
+
hash.update(Buffer.from([bump]));
|
|
457
|
+
}
|
|
458
|
+
hash.update(toPubkeyBytes(programId, 'program id'));
|
|
459
|
+
hash.update(PDA_MARKER);
|
|
460
|
+
const digest = hash.digest();
|
|
461
|
+
if (isOnCurve(digest))
|
|
462
|
+
throw new Error('Invalid seeds, address must fall off the curve');
|
|
463
|
+
return base58Encode(digest);
|
|
464
|
+
}
|
|
465
|
+
/** Find the first off-curve address for `seeds`, walking bump 255 -> 0. */
|
|
466
|
+
function findProgramAddress(seeds, programId) {
|
|
467
|
+
const web3 = loadWeb3js();
|
|
468
|
+
if (web3) {
|
|
469
|
+
const [address, bump] = web3.PublicKey.findProgramAddressSync(seeds.map(toSeedBuffer), new web3.PublicKey(programId));
|
|
470
|
+
return [address.toBase58(), bump];
|
|
471
|
+
}
|
|
472
|
+
for (let bump = 255; bump >= 0; bump--) {
|
|
473
|
+
try {
|
|
474
|
+
return [createProgramAddress(seeds, programId, bump), bump];
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
if (!String(e === null || e === void 0 ? void 0 : e.message).includes('off the curve'))
|
|
478
|
+
throw e;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
throw new Error('Unable to find a viable program address nonce');
|
|
482
|
+
}
|
|
483
|
+
function getAssociatedTokenAddress({ mint, owner, programId = exports.TOKEN_PROGRAM_ID, associatedTokenProgramId = exports.ASSOCIATED_TOKEN_PROGRAM_ID }) {
|
|
484
|
+
const [address] = findProgramAddress([
|
|
485
|
+
toPubkeyBytes(owner, 'owner'),
|
|
486
|
+
toPubkeyBytes(programId, 'program id'),
|
|
487
|
+
toPubkeyBytes(mint, 'mint'),
|
|
488
|
+
], associatedTokenProgramId);
|
|
489
|
+
return address;
|
|
490
|
+
}
|
|
491
|
+
const DEFAULT_CONCURRENCY = 10;
|
|
492
|
+
function limiterFor(chain) {
|
|
493
|
+
return (0, rpc_1.getLimiter)(chain.toUpperCase(), DEFAULT_CONCURRENCY);
|
|
494
|
+
}
|
|
495
|
+
/** Raw JSON-RPC call against the chain's endpoints (rate limited per chain, retried). */
|
|
496
|
+
async function call({ chain = 'solana', method, params = [], commitment, timeout, retries }) {
|
|
497
|
+
const endpoints = getEndpoints({ chain, isClient: true });
|
|
498
|
+
if (commitment)
|
|
499
|
+
params = withCommitment(params, commitment);
|
|
500
|
+
return limiterFor(chain)(() => (0, rpc_1.jsonRpc)(method, params, { chain, endpoints, timeout, retries }));
|
|
501
|
+
}
|
|
502
|
+
function withCommitment(params, commitment) {
|
|
503
|
+
const last = params[params.length - 1];
|
|
504
|
+
if (last && typeof last === 'object' && !Array.isArray(last))
|
|
505
|
+
return [...params.slice(0, -1), { ...last, commitment }];
|
|
506
|
+
return [...params, { commitment }];
|
|
507
|
+
}
|
|
508
|
+
async function getSlot({ chain = 'solana', commitment } = {}) {
|
|
509
|
+
return call({ chain, method: 'getSlot', params: [], commitment });
|
|
510
|
+
}
|
|
511
|
+
async function getBlockTime({ chain = 'solana', slot }) {
|
|
512
|
+
const res = await call({ chain, method: 'getBlockTime', params: [slot] });
|
|
513
|
+
return res === null || res === undefined ? null : Number(res);
|
|
514
|
+
}
|
|
515
|
+
async function getBlockHeight({ chain = 'solana', commitment } = {}) {
|
|
516
|
+
return call({ chain, method: 'getBlockHeight', params: [], commitment });
|
|
517
|
+
}
|
|
518
|
+
/** Latest slot and its block time (walks back over skipped slots without a time). */
|
|
519
|
+
async function getLatestBlock({ chain = 'solana', commitment } = {}) {
|
|
520
|
+
let slot = await getSlot({ chain, commitment });
|
|
521
|
+
for (let i = 0; i < 10; i++) {
|
|
522
|
+
const timestamp = await getBlockTime({ chain, slot }).catch((e) => {
|
|
523
|
+
(0, debugLog_1.debugLog)(`[chains.svm] ${chain} getBlockTime(${slot}) failed: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
524
|
+
return null;
|
|
525
|
+
});
|
|
526
|
+
if (timestamp)
|
|
527
|
+
return { number: slot, timestamp };
|
|
528
|
+
slot--;
|
|
529
|
+
}
|
|
530
|
+
throw new Error(`[chains.svm] ${chain}: unable to resolve block time for the latest slots`);
|
|
531
|
+
}
|
|
532
|
+
async function getEpochInfo({ chain = 'solana', commitment } = {}) {
|
|
533
|
+
return call({ chain, method: 'getEpochInfo', params: [], commitment });
|
|
534
|
+
}
|
|
535
|
+
/** Lamport balance of a single account. */
|
|
536
|
+
async function getBalance({ chain = 'solana', account, commitment }) {
|
|
537
|
+
var _a;
|
|
538
|
+
const res = await call({ chain, method: 'getBalance', params: [account], commitment });
|
|
539
|
+
return Number((_a = res === null || res === void 0 ? void 0 : res.value) !== null && _a !== void 0 ? _a : 0);
|
|
540
|
+
}
|
|
541
|
+
/** Lamport balances of many accounts (batched `getMultipleAccounts`, 0 for missing accounts). */
|
|
542
|
+
async function getBalances({ chain = 'solana', accounts, commitment }) {
|
|
543
|
+
const infos = await getAccounts({ chain, accounts, commitment, dataSlice: { offset: 0, length: 0 } });
|
|
544
|
+
return infos.map(i => i ? Number(i.lamports) : 0);
|
|
545
|
+
}
|
|
546
|
+
async function getAccountInfo({ chain = 'solana', account, encoding = 'base64', commitment, dataSlice }) {
|
|
547
|
+
var _a;
|
|
548
|
+
const config = { encoding };
|
|
549
|
+
if (dataSlice)
|
|
550
|
+
config.dataSlice = dataSlice;
|
|
551
|
+
const res = await call({ chain, method: 'getAccountInfo', params: [account, config], commitment });
|
|
552
|
+
return (_a = res === null || res === void 0 ? void 0 : res.value) !== null && _a !== void 0 ? _a : null;
|
|
553
|
+
}
|
|
554
|
+
/** Base64 payload of one account as a Buffer, or null when it does not exist. */
|
|
555
|
+
async function getAccountBuffer({ chain = 'solana', account, commitment }) {
|
|
556
|
+
const info = await getAccountInfo({ chain, account, encoding: 'base64', commitment });
|
|
557
|
+
return accountDataToBuffer(info);
|
|
558
|
+
}
|
|
559
|
+
/** Batched `getMultipleAccounts`; result is in input order with `null` for missing accounts. */
|
|
560
|
+
async function getAccounts({ chain = 'solana', accounts, encoding = 'base64', commitment, dataSlice, chunkSize = 99, concurrency = 5, sleepTime = 0 }) {
|
|
561
|
+
if (!accounts.length)
|
|
562
|
+
return [];
|
|
563
|
+
const config = { encoding };
|
|
564
|
+
if (dataSlice)
|
|
565
|
+
config.dataSlice = dataSlice;
|
|
566
|
+
(0, debugLog_1.debugLog)(`[chains.svm] ${chain} getAccounts: ${accounts.length} accounts in chunks of ${chunkSize}`);
|
|
567
|
+
return (0, rpc_1.runInChunks)(accounts, async (chunk) => {
|
|
568
|
+
var _a;
|
|
569
|
+
const res = await call({ chain, method: 'getMultipleAccounts', params: [chunk, config], commitment });
|
|
570
|
+
const value = (_a = res === null || res === void 0 ? void 0 : res.value) !== null && _a !== void 0 ? _a : [];
|
|
571
|
+
if (value.length !== chunk.length)
|
|
572
|
+
throw new Error(`[chains.svm] ${chain} getMultipleAccounts returned ${value.length} entries for ${chunk.length} accounts`);
|
|
573
|
+
return value;
|
|
574
|
+
}, { chunkSize, concurrency, sleepTime });
|
|
575
|
+
}
|
|
576
|
+
function accountDataToBuffer(info) {
|
|
577
|
+
if (!info)
|
|
578
|
+
return null;
|
|
579
|
+
const data = info.data;
|
|
580
|
+
if (Array.isArray(data))
|
|
581
|
+
return data[1] === 'base58' ? Buffer.from(base58Decode(data[0])) : Buffer.from(data[0], 'base64');
|
|
582
|
+
if (typeof data === 'string')
|
|
583
|
+
return Buffer.from(data, 'base64');
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
/** Raw account payloads as Buffers, in input order (`null` for missing accounts). */
|
|
587
|
+
async function getAccountBuffers(options) {
|
|
588
|
+
const infos = await getAccounts({ ...options, encoding: 'base64' });
|
|
589
|
+
return infos.map(accountDataToBuffer);
|
|
590
|
+
}
|
|
591
|
+
async function getProgramAccounts({ chain = 'solana', programId, filters, encoding = 'base64', dataSlice, commitment, withContext, retries = 5, timeout = 120000 }) {
|
|
592
|
+
const config = { encoding };
|
|
593
|
+
if (filters === null || filters === void 0 ? void 0 : filters.length)
|
|
594
|
+
config.filters = filters;
|
|
595
|
+
if (dataSlice)
|
|
596
|
+
config.dataSlice = dataSlice;
|
|
597
|
+
if (withContext)
|
|
598
|
+
config.withContext = true;
|
|
599
|
+
const res = await call({ chain, method: 'getProgramAccounts', params: [programId, config], commitment, retries, timeout });
|
|
600
|
+
if (withContext)
|
|
601
|
+
return res;
|
|
602
|
+
if (!Array.isArray(res))
|
|
603
|
+
throw new Error(`[chains.svm] ${chain} getProgramAccounts(${programId}) returned no array`);
|
|
604
|
+
return res;
|
|
605
|
+
}
|
|
606
|
+
async function getTokenSupply({ chain = 'solana', token, commitment }) {
|
|
607
|
+
var _a, _b;
|
|
608
|
+
const res = await call({ chain, method: 'getTokenSupply', params: [token], commitment });
|
|
609
|
+
const value = res === null || res === void 0 ? void 0 : res.value;
|
|
610
|
+
if (!value)
|
|
611
|
+
throw new Error(`[chains.svm] ${chain} getTokenSupply(${token}): empty result`);
|
|
612
|
+
return { amount: String(value.amount), decimals: Number(value.decimals), uiAmount: Number((_b = (_a = value.uiAmount) !== null && _a !== void 0 ? _a : value.uiAmountString) !== null && _b !== void 0 ? _b : 0), uiAmountString: value.uiAmountString };
|
|
613
|
+
}
|
|
614
|
+
function supplyFromMint(mint) {
|
|
615
|
+
const uiAmount = Number(mint.supply) / 10 ** mint.decimals;
|
|
616
|
+
return { amount: mint.supply, decimals: mint.decimals, uiAmount, uiAmountString: String(uiAmount) };
|
|
617
|
+
}
|
|
618
|
+
async function getTokenSupplies({ chain = 'solana', tokens, commitment, chunkSize, concurrency, sleepTime, allowError = false }) {
|
|
619
|
+
if (!tokens.length)
|
|
620
|
+
return [];
|
|
621
|
+
const buffers = await getAccountBuffers({ chain, accounts: tokens, commitment, chunkSize, concurrency, sleepTime });
|
|
622
|
+
return Promise.all(buffers.map(async (buf, i) => {
|
|
623
|
+
if (buf) {
|
|
624
|
+
try {
|
|
625
|
+
return supplyFromMint(decodeMintAccount(buf));
|
|
626
|
+
}
|
|
627
|
+
catch (e) {
|
|
628
|
+
(0, debugLog_1.debugLog)(`[chains.svm] ${chain} mint ${tokens[i]} failed to decode (${e === null || e === void 0 ? void 0 : e.message}), falling back to getTokenSupply`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
(0, debugLog_1.debugLog)(`[chains.svm] ${chain} mint ${tokens[i]}: account not found, falling back to getTokenSupply`);
|
|
633
|
+
}
|
|
634
|
+
try {
|
|
635
|
+
return await getTokenSupply({ chain, token: tokens[i], commitment });
|
|
636
|
+
}
|
|
637
|
+
catch (e) {
|
|
638
|
+
if (allowError)
|
|
639
|
+
return null;
|
|
640
|
+
throw e;
|
|
641
|
+
}
|
|
642
|
+
}));
|
|
643
|
+
}
|
|
644
|
+
async function getTokenAccountBalances({ chain = 'solana', tokenAccounts, individual = false, allowError = false, withDecimals = false, commitment, chunkSize, concurrency, sleepTime }) {
|
|
645
|
+
const buffers = tokenAccounts.length ? await getAccountBuffers({ chain, accounts: tokenAccounts, commitment, chunkSize, concurrency, sleepTime }) : [];
|
|
646
|
+
const individualBalances = [];
|
|
647
|
+
const balances = {};
|
|
648
|
+
buffers.forEach((buf, i) => {
|
|
649
|
+
var _a;
|
|
650
|
+
const account = tokenAccounts[i];
|
|
651
|
+
let decoded;
|
|
652
|
+
try {
|
|
653
|
+
if (!buf)
|
|
654
|
+
throw new Error('account not found');
|
|
655
|
+
decoded = decodeTokenAccount(buf);
|
|
656
|
+
}
|
|
657
|
+
catch (e) {
|
|
658
|
+
(0, debugLog_1.debugLog)(`[chains.svm] ${chain} token account ${account}: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
659
|
+
if (!allowError)
|
|
660
|
+
throw new Error(`[chains.svm] ${chain} invalid token account ${account}: ${e === null || e === void 0 ? void 0 : e.message}`);
|
|
661
|
+
if (individual)
|
|
662
|
+
individualBalances.push({ mint: 'error', amount: '0', owner: '' });
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (individual)
|
|
666
|
+
individualBalances.push({ mint: decoded.mint, amount: decoded.amount, owner: decoded.owner });
|
|
667
|
+
else
|
|
668
|
+
balances[decoded.mint] = ((_a = balances[decoded.mint]) !== null && _a !== void 0 ? _a : BigInt(0)) + BigInt(decoded.amount);
|
|
669
|
+
});
|
|
670
|
+
if (!individual) {
|
|
671
|
+
const res = {};
|
|
672
|
+
for (const mint of Object.keys(balances))
|
|
673
|
+
res[mint] = balances[mint].toString();
|
|
674
|
+
return res;
|
|
675
|
+
}
|
|
676
|
+
if (withDecimals) {
|
|
677
|
+
const mints = [...new Set(individualBalances.map(i => i.mint).filter(i => i !== 'error'))];
|
|
678
|
+
const supplies = await getTokenSupplies({ chain, tokens: mints, commitment, allowError: true });
|
|
679
|
+
const decimals = {};
|
|
680
|
+
mints.forEach((mint, i) => { if (supplies[i])
|
|
681
|
+
decimals[mint] = supplies[i].decimals; });
|
|
682
|
+
individualBalances.forEach(i => { if (decimals[i.mint] !== undefined)
|
|
683
|
+
i.decimals = decimals[i.mint]; });
|
|
684
|
+
}
|
|
685
|
+
return individualBalances;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Token accounts of `owner` (jsonParsed). With neither `mint` nor `programId`,
|
|
689
|
+
* both the Token and Token-2022 programs are queried.
|
|
690
|
+
*/
|
|
691
|
+
async function getTokenAccountsByOwner({ chain = 'solana', owner, mint, programId, commitment }) {
|
|
692
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
693
|
+
const filters = [];
|
|
694
|
+
if (mint)
|
|
695
|
+
filters.push({ mint });
|
|
696
|
+
else if (programId)
|
|
697
|
+
filters.push({ programId });
|
|
698
|
+
else
|
|
699
|
+
filters.push({ programId: exports.TOKEN_PROGRAM_ID }, { programId: exports.TOKEN_2022_PROGRAM_ID });
|
|
700
|
+
const results = await Promise.all(filters.map(filter => call({ chain, method: 'getTokenAccountsByOwner', params: [owner, filter, { encoding: 'jsonParsed' }], commitment })));
|
|
701
|
+
const out = [];
|
|
702
|
+
for (const res of results) {
|
|
703
|
+
for (const entry of (_a = res === null || res === void 0 ? void 0 : res.value) !== null && _a !== void 0 ? _a : []) {
|
|
704
|
+
const info = (_d = (_c = (_b = entry === null || entry === void 0 ? void 0 : entry.account) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.parsed) === null || _d === void 0 ? void 0 : _d.info;
|
|
705
|
+
if (!info)
|
|
706
|
+
continue;
|
|
707
|
+
out.push({
|
|
708
|
+
pubkey: entry.pubkey,
|
|
709
|
+
mint: info.mint,
|
|
710
|
+
owner: info.owner,
|
|
711
|
+
amount: String((_f = (_e = info.tokenAmount) === null || _e === void 0 ? void 0 : _e.amount) !== null && _f !== void 0 ? _f : '0'),
|
|
712
|
+
decimals: Number((_h = (_g = info.tokenAmount) === null || _g === void 0 ? void 0 : _g.decimals) !== null && _h !== void 0 ? _h : 0),
|
|
713
|
+
uiAmount: Number((_k = (_j = info.tokenAmount) === null || _j === void 0 ? void 0 : _j.uiAmount) !== null && _k !== void 0 ? _k : 0),
|
|
714
|
+
programId: entry.account.owner,
|
|
715
|
+
state: info.state,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return out;
|
|
720
|
+
}
|
|
721
|
+
/** Raw token amount (string) of `mint` held by `owner`, summed over all of the owner's token accounts. */
|
|
722
|
+
async function getTokenBalance({ chain = 'solana', owner, mint, commitment }) {
|
|
723
|
+
const accounts = await getTokenAccountsByOwner({ chain, owner, mint, commitment });
|
|
724
|
+
return accounts.reduce((sum, i) => sum + BigInt(i.amount), BigInt(0)).toString();
|
|
725
|
+
}
|
|
726
|
+
async function getSignaturesForAddress({ chain = 'solana', address, before, until, limit = 1000, commitment }) {
|
|
727
|
+
const config = { limit };
|
|
728
|
+
if (before)
|
|
729
|
+
config.before = before;
|
|
730
|
+
if (until)
|
|
731
|
+
config.until = until;
|
|
732
|
+
const res = await call({ chain, method: 'getSignaturesForAddress', params: [address, config], commitment });
|
|
733
|
+
return Array.isArray(res) ? res : [];
|
|
734
|
+
}
|
|
735
|
+
async function getTransaction({ chain = 'solana', signature, encoding = 'json', maxSupportedTransactionVersion = 0, commitment }) {
|
|
736
|
+
return call({ chain, method: 'getTransaction', params: [signature, { encoding, maxSupportedTransactionVersion }], commitment });
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Lamports in all stake accounts whose stake authority (default, as in the
|
|
740
|
+
* adapters helper) or withdraw authority is `address`.
|
|
741
|
+
*/
|
|
742
|
+
async function getStakedSol({ chain = 'solana', address, authority = 'staker', commitment }) {
|
|
743
|
+
// StakeStateV2: u32 enum tag, then Meta { rent_exempt_reserve: u64, authorized: { staker: Pubkey, withdrawer: Pubkey }, .. }
|
|
744
|
+
const offset = authority === 'withdrawer' ? 4 + 8 + 32 : 4 + 8;
|
|
745
|
+
const stakeAccounts = await getProgramAccounts({
|
|
746
|
+
chain,
|
|
747
|
+
programId: exports.STAKE_PROGRAM_ID,
|
|
748
|
+
filters: [{ memcmp: { offset, bytes: address } }],
|
|
749
|
+
dataSlice: { offset: 0, length: 1 }, // only lamports are needed
|
|
750
|
+
commitment,
|
|
751
|
+
});
|
|
752
|
+
return stakeAccounts.reduce((total, { account }) => total + Number(account.lamports), 0);
|
|
753
|
+
}
|
|
754
|
+
/** `total_lamports` of an SPL stake pool account (lamports, as number). */
|
|
755
|
+
async function getSolBalanceFromStakePool({ chain = 'solana', address, commitment }) {
|
|
756
|
+
const buf = await getAccountBuffer({ chain, account: address, commitment });
|
|
757
|
+
if (!buf)
|
|
758
|
+
throw new Error(`[chains.svm] ${chain} stake pool ${address} not found`);
|
|
759
|
+
return Number(decodeStakePool(buf).totalLamports);
|
|
760
|
+
}
|