@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.
Files changed (60) hide show
  1. package/build/ChainApi.d.ts +23 -1
  2. package/build/chains/algorand.d.ts +277 -0
  3. package/build/chains/algorand.js +618 -0
  4. package/build/chains/aptos.d.ts +230 -0
  5. package/build/chains/aptos.js +612 -0
  6. package/build/chains/cardano.d.ts +242 -0
  7. package/build/chains/cardano.js +407 -0
  8. package/build/chains/cosmos.d.ts +252 -0
  9. package/build/chains/cosmos.js +618 -0
  10. package/build/chains/icp.d.ts +235 -0
  11. package/build/chains/icp.js +1040 -0
  12. package/build/chains/index.d.ts +23 -0
  13. package/build/chains/index.js +59 -0
  14. package/build/chains/near.d.ts +192 -0
  15. package/build/chains/near.js +446 -0
  16. package/build/chains/rpc.d.ts +125 -0
  17. package/build/chains/rpc.js +326 -0
  18. package/build/chains/starknet.d.ts +205 -0
  19. package/build/chains/starknet.js +884 -0
  20. package/build/chains/stellar.d.ts +410 -0
  21. package/build/chains/stellar.js +1082 -0
  22. package/build/chains/substrate.d.ts +333 -0
  23. package/build/chains/substrate.js +1042 -0
  24. package/build/chains/sui.d.ts +365 -0
  25. package/build/chains/sui.js +1135 -0
  26. package/build/chains/svm.d.ts +340 -0
  27. package/build/chains/svm.js +760 -0
  28. package/build/chains/tezos.d.ts +225 -0
  29. package/build/chains/tezos.js +412 -0
  30. package/build/chains/ton.d.ts +353 -0
  31. package/build/chains/ton.js +788 -0
  32. package/build/chains/tron.d.ts +45 -0
  33. package/build/chains/tron.js +110 -0
  34. package/build/chains/utxo.d.ts +202 -0
  35. package/build/chains/utxo.js +592 -0
  36. package/build/chains/xrpl.d.ts +179 -0
  37. package/build/chains/xrpl.js +467 -0
  38. package/build/index.d.ts +2 -1
  39. package/build/index.js +3 -1
  40. package/build/providers.json +1 -1
  41. package/build/util/chainUtils/data.json +11 -0
  42. package/build/util/chainUtils/index.d.ts +1 -0
  43. package/build/util/chainUtils/index.js +23 -0
  44. package/build/util/common.js +2 -3
  45. package/build/util/cosmos.d.ts +1 -11
  46. package/build/util/cosmos.js +6 -66
  47. package/build/util/env.js +1 -0
  48. package/build/util/indexer.compatibility.d.ts +6 -0
  49. package/build/util/indexer.compatibility.js +64 -0
  50. package/build/util/indexer.d.ts +24 -2
  51. package/build/util/indexer.js +215 -282
  52. package/build/util/indexerSql.d.ts +8 -1
  53. package/build/util/indexerSql.js +53 -22
  54. package/build/util/logs.decode.shared.js +5 -4
  55. package/build/util/transactions.d.ts +23 -1
  56. package/build/util/updateChainLabels.d.ts +1 -0
  57. package/build/util/updateChainLabels.js +5 -0
  58. package/build/util/updateProviderList.js +38 -17
  59. package/package.json +20 -7
  60. package/README.md +0 -41
@@ -0,0 +1,788 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BitReader = exports.Address = exports.MASTERCHAIN_SHARD = exports.DEFAULT_ENDPOINTS = void 0;
4
+ exports.getToncenterEndpoints = getToncenterEndpoints;
5
+ exports.getToncenterEndpoint = getToncenterEndpoint;
6
+ exports.getTonapiEndpoints = getTonapiEndpoints;
7
+ exports.getTonapiEndpoint = getTonapiEndpoint;
8
+ exports.toncenterGet = toncenterGet;
9
+ exports.toncenterPost = toncenterPost;
10
+ exports.getAccountState = getAccountState;
11
+ exports.getTonBalance = getTonBalance;
12
+ exports.getJettonMaster = getJettonMaster;
13
+ exports.getJettonSupply = getJettonSupply;
14
+ exports.getJettonWallets = getJettonWallets;
15
+ exports.getJettonWalletBalance = getJettonWalletBalance;
16
+ exports.runGetMethod = runGetMethod;
17
+ exports.decodeStackItem = decodeStackItem;
18
+ exports.call = call;
19
+ exports.getTransactions = getTransactions;
20
+ exports.getMessages = getMessages;
21
+ exports.getMasterchainInfo = getMasterchainInfo;
22
+ exports.lookupBlock = lookupBlock;
23
+ exports.getBlockAtTimestamp = getBlockAtTimestamp;
24
+ exports.getLatestBlock = getLatestBlock;
25
+ exports.tonapiGet = tonapiGet;
26
+ exports.tonapiPost = tonapiPost;
27
+ exports.getJettonBalances = getJettonBalances;
28
+ exports.jettonBalancesByAddress = jettonBalancesByAddress;
29
+ exports.getJettonInfo = getJettonInfo;
30
+ exports.getJettonsInfo = getJettonsInfo;
31
+ exports.getTokenRates = getTokenRates;
32
+ exports.crc16 = crc16;
33
+ exports.parseAddress = parseAddress;
34
+ exports.normalizeAddress = normalizeAddress;
35
+ exports.toRawAddress = toRawAddress;
36
+ exports.isAddress = isAddress;
37
+ exports.addressToInt = addressToInt;
38
+ exports.convertIntToAddress = convertIntToAddress;
39
+ exports.compareAddress = compareAddress;
40
+ exports.decodeBase64 = decodeBase64;
41
+ exports.parseBoc = parseBoc;
42
+ exports.serializeAddress = serializeAddress;
43
+ exports.readAddressFromSlice = readAddressFromSlice;
44
+ exports.tryReadAddressFromSlice = tryReadAddressFromSlice;
45
+ /**
46
+ * TON client: toncenter (v2 + v3), tonapi, and a dependency-free address / BoC codec.
47
+ *
48
+ * Replaces the TON glue scattered across the DefiLlama repos:
49
+ * - DefiLlama-Adapters `projects/helper/chain/ton.js` (getTonBalance, getJettonBalances,
50
+ * getTokenRates, getJettonsInfo, call/runGetMethod, processTVMSliceReadAddress, parseBoc,
51
+ * BitReader, serializeAddress, computeCRC16) — the TVL parts (sumTokens, addJettonBalances,
52
+ * addTonBalances) are intentionally not ported.
53
+ * - DefiLlama-Adapters `projects/helper/chain/utils/ton-address.js` (addressToInt,
54
+ * convertIntToAddress, compareAddress)
55
+ * - DefiLlama-Adapters `projects/helper/utils/ton.js` and defillama-server
56
+ * `coins/src/utils/ton.ts` (ton-core `Address` + crc16)
57
+ * - defillama-server `coins/src/adapters/other/ton.ts` (runGetMethod stack decoding) and
58
+ * `coins/src/scripts/coingeckoUtils.ts` (jetton symbol / decimals lookup)
59
+ * - peggedassets-server `helper/getSupply.ts` tonTokenSupply (v3 jetton/masters)
60
+ * - dimension-adapters `fees/sTONks`, `fees/hipo` (v3 /transactions and /messages window
61
+ * pagination) and `helpers/getBlock.ts` getTonBlock (v2 lookupBlock)
62
+ *
63
+ * Endpoints: `TON_RPC` (toncenter base, default https://toncenter.com) and `TON_API_RPC`
64
+ * (tonapi base, default https://tonapi.io). Keys: `TONCENTER_API_KEY` (sent as `X-API-Key`)
65
+ * and `TON_API_KEY` (tonapi bearer token). Unauthenticated toncenter / tonapi allow about one
66
+ * request per second, so every call goes through a per-service limiter that spaces requests
67
+ * (`TON_RPC_MIN_INTERVAL_MS` / `TONAPI_RPC_MIN_INTERVAL_MS` override the spacing,
68
+ * `TON_RPC_CONCURRENCY` / `TONAPI_RPC_CONCURRENCY` the concurrency).
69
+ */
70
+ const env_1 = require("../util/env");
71
+ const debugLog_1 = require("../util/debugLog");
72
+ const rpc_1 = require("./rpc");
73
+ // ---------------------------------------------------------------------------
74
+ // config
75
+ // ---------------------------------------------------------------------------
76
+ exports.DEFAULT_ENDPOINTS = {
77
+ toncenter: 'https://toncenter.com',
78
+ tonapi: 'https://tonapi.io',
79
+ };
80
+ /** All configured toncenter base urls (`TON_RPC`, comma separated) */
81
+ function getToncenterEndpoints() {
82
+ return (0, rpc_1.getEndpoints)('ton', exports.DEFAULT_ENDPOINTS.toncenter);
83
+ }
84
+ function getToncenterEndpoint() {
85
+ return getToncenterEndpoints()[0];
86
+ }
87
+ /** All configured tonapi base urls (`TON_API_RPC`, comma separated) */
88
+ function getTonapiEndpoints() {
89
+ return (0, rpc_1.getEndpoints)('ton', exports.DEFAULT_ENDPOINTS.tonapi, { envKey: 'TON_API_RPC' });
90
+ }
91
+ function getTonapiEndpoint() {
92
+ return getTonapiEndpoints()[0];
93
+ }
94
+ function getToncenterApiKey() {
95
+ return (0, env_1.getEnvValue)('TONCENTER_API_KEY');
96
+ }
97
+ function getTonapiKey() {
98
+ return (0, env_1.getEnvValue)('TON_API_KEY');
99
+ }
100
+ function toncenterHeaders() {
101
+ const key = getToncenterApiKey();
102
+ return key ? { 'X-API-Key': key } : {};
103
+ }
104
+ function tonapiHeaders() {
105
+ const key = getTonapiKey();
106
+ if (!key)
107
+ return {};
108
+ // the adapters repo stores the key with the scheme already included ("Bearer xxx"), accept both forms
109
+ return { Authorization: /^bearer /i.test(key) ? key : `Bearer ${key}` };
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // rate limiting
113
+ // ---------------------------------------------------------------------------
114
+ const lastRequestAt = {};
115
+ /**
116
+ * Serialise requests per service and keep at least `minIntervalMs` between them. Without an
117
+ * API key both toncenter and tonapi allow roughly one request per second; with a key the
118
+ * spacing is dropped and the limiter allows a few parallel requests.
119
+ */
120
+ async function paced(service, fn) {
121
+ const hasKey = service === 'TON' ? !!getToncenterApiKey() : !!getTonapiKey();
122
+ const envInterval = (0, env_1.getEnvValue)(`${service}_RPC_MIN_INTERVAL_MS`);
123
+ const minIntervalMs = envInterval !== undefined ? +envInterval : hasKey ? 0 : 1100;
124
+ const limiter = (0, rpc_1.getLimiter)(service, hasKey ? 5 : 1);
125
+ return limiter(async () => {
126
+ var _a;
127
+ const wait = ((_a = lastRequestAt[service]) !== null && _a !== void 0 ? _a : 0) + minIntervalMs - Date.now();
128
+ if (wait > 0)
129
+ await (0, rpc_1.sleep)(wait);
130
+ try {
131
+ return await fn();
132
+ }
133
+ finally {
134
+ lastRequestAt[service] = Date.now();
135
+ }
136
+ });
137
+ }
138
+ // 429s from toncenter need a longer pause than the transport default
139
+ const TONCENTER_RETRY = { retries: 4, delay: 1500, maxDelay: 8000 };
140
+ const TONAPI_RETRY = { retries: 3, delay: 1500, maxDelay: 8000 };
141
+ function toncenterPath(path, version) {
142
+ return `/api/v${version}/${path.replace(/^\/+/, '')}`;
143
+ }
144
+ function dropUndefined(params = {}) {
145
+ const res = {};
146
+ Object.keys(params).forEach(k => { if (params[k] !== undefined && params[k] !== null)
147
+ res[k] = params[k]; });
148
+ return res;
149
+ }
150
+ /** toncenter v2 wraps everything in `{ ok, result, error }`; unwrap and surface errors */
151
+ function unwrapV2(res, label) {
152
+ var _a, _b;
153
+ if (res && typeof res === 'object' && 'ok' in res) {
154
+ if (!res.ok)
155
+ throw new Error(`toncenter ${label} failed: ${(_b = (_a = res.error) !== null && _a !== void 0 ? _a : res.result) !== null && _b !== void 0 ? _b : 'unknown error'}${res.code !== undefined ? ` (code ${res.code})` : ''}`);
156
+ return res.result;
157
+ }
158
+ return res;
159
+ }
160
+ /** GET `/api/v{version}/{path}` on toncenter. Returns the raw response body (v2 bodies are `{ok, result}`). */
161
+ async function toncenterGet({ path, params, version = 3 }) {
162
+ return paced('TON', () => (0, rpc_1.httpGet)(getToncenterEndpoints(), {
163
+ path: toncenterPath(path, version),
164
+ params: dropUndefined(params),
165
+ headers: toncenterHeaders(),
166
+ ...TONCENTER_RETRY,
167
+ }));
168
+ }
169
+ /** POST to `/api/v{version}/{path}` on toncenter. Returns the raw response body. */
170
+ async function toncenterPost({ path, body = {}, version = 2 }) {
171
+ return paced('TON', () => (0, rpc_1.httpPost)(getToncenterEndpoints(), body, {
172
+ path: toncenterPath(path, version),
173
+ headers: toncenterHeaders(),
174
+ ...TONCENTER_RETRY,
175
+ }));
176
+ }
177
+ // ---------------------------------------------------------------------------
178
+ // toncenter: accounts & jettons
179
+ // ---------------------------------------------------------------------------
180
+ /** Full v3 account state: `{ balance, status, code, data, last_transaction_lt, last_transaction_hash, frozen_hash }` */
181
+ async function getAccountState({ address }) {
182
+ return toncenterGet({ path: 'account', params: { address } });
183
+ }
184
+ /** TON balance in nanoton, as a decimal string */
185
+ async function getTonBalance({ address }) {
186
+ const res = await getAccountState({ address });
187
+ if ((res === null || res === void 0 ? void 0 : res.balance) === undefined || (res === null || res === void 0 ? void 0 : res.balance) === null)
188
+ throw new Error(`toncenter account: no balance for ${address}`);
189
+ return String(res.balance);
190
+ }
191
+ /** v3 `jetton/masters` entry for one jetton. Throws when the address is not a jetton master. */
192
+ async function getJettonMaster({ address }) {
193
+ var _a;
194
+ const res = await toncenterGet({ path: 'jetton/masters', params: { address, limit: 1, offset: 0 } });
195
+ const master = (_a = res === null || res === void 0 ? void 0 : res.jetton_masters) === null || _a === void 0 ? void 0 : _a[0];
196
+ if (!master)
197
+ throw new Error(`toncenter jetton/masters: ${address} is not a known jetton master`);
198
+ return master;
199
+ }
200
+ function parseDecimals(value) {
201
+ if (value === undefined || value === null || value === '')
202
+ return undefined;
203
+ const n = Number(value);
204
+ return Number.isFinite(n) ? n : undefined;
205
+ }
206
+ /** `{ supply, decimals }` of a jetton from its v3 master record. `decimals` is undefined when the content is off-chain. */
207
+ async function getJettonSupply({ address }) {
208
+ var _a;
209
+ const master = await getJettonMaster({ address });
210
+ return { supply: String(master.total_supply), decimals: parseDecimals((_a = master.jetton_content) === null || _a === void 0 ? void 0 : _a.decimals) };
211
+ }
212
+ /** v3 `jetton/wallets` filtered by owner and/or jetton, all pages by default */
213
+ async function getJettonWallets({ owner, jetton, limit = 256, offset = 0, paginate = true, excludeZeroBalance }) {
214
+ var _a;
215
+ if (!owner && !jetton)
216
+ throw new Error('getJettonWallets: owner or jetton is required');
217
+ const owner_address = Array.isArray(owner) ? owner.join(',') : owner;
218
+ const all = [];
219
+ for (let page = 0;; page++) {
220
+ const res = await toncenterGet({
221
+ path: 'jetton/wallets',
222
+ params: { owner_address, jetton_address: jetton, limit, offset: offset + page * limit, exclude_zero_balance: excludeZeroBalance },
223
+ });
224
+ const wallets = (_a = res === null || res === void 0 ? void 0 : res.jetton_wallets) !== null && _a !== void 0 ? _a : [];
225
+ all.push(...wallets);
226
+ if (!paginate || wallets.length < limit)
227
+ break;
228
+ (0, debugLog_1.debugLog)(`[chains.ton] jetton/wallets page ${page + 1} (${all.length} so far)`);
229
+ }
230
+ return all;
231
+ }
232
+ /** Balance (smallest units, decimal string) of `owner` in `jetton`; '0' when no wallet exists */
233
+ async function getJettonWalletBalance({ owner, jetton }) {
234
+ const wallets = await getJettonWallets({ owner, jetton, limit: 16, paginate: false });
235
+ let total = BigInt(0);
236
+ wallets.forEach(w => { var _a; total += BigInt((_a = w.balance) !== null && _a !== void 0 ? _a : 0); });
237
+ return total.toString();
238
+ }
239
+ /** v2 `runGetMethod`; returns the unwrapped result `{ exit_code, gas_used, stack }` */
240
+ async function runGetMethod({ address, method, stack = [] }) {
241
+ const res = await toncenterPost({ path: 'runGetMethod', body: { address, method, stack }, version: 2 });
242
+ return unwrapV2(res, `runGetMethod ${method}`);
243
+ }
244
+ function parseStackNumber(value) {
245
+ const s = String(value).trim();
246
+ const big = s.startsWith('-') ? -BigInt(s.slice(1)) : BigInt(s);
247
+ const n = Number(big);
248
+ return Number.isSafeInteger(n) ? n : big;
249
+ }
250
+ function stackBytes(item) {
251
+ const payload = item[1];
252
+ if (typeof payload === 'string')
253
+ return payload;
254
+ return payload === null || payload === void 0 ? void 0 : payload.bytes;
255
+ }
256
+ /** Decode one runGetMethod stack entry: num -> number (bigint when unsafe), cell/slice holding an address -> friendly address, anything else is returned as-is */
257
+ function decodeStackItem(item) {
258
+ const [type] = item;
259
+ if (type === 'num')
260
+ return parseStackNumber(item[1]);
261
+ if (type === 'cell' || type === 'slice' || type === 'tvm.Cell' || type === 'tvm.Slice') {
262
+ const bytes = stackBytes(item);
263
+ if (bytes) {
264
+ const address = tryReadAddressFromSlice(bytes);
265
+ if (address)
266
+ return address;
267
+ }
268
+ }
269
+ return item;
270
+ }
271
+ /**
272
+ * Call a get method and decode the stack. `num` entries become numbers (bigint when they do
273
+ * not fit a safe integer, where the adapters helper silently lost precision), cells / slices
274
+ * that hold a `MsgAddressInt` become friendly addresses, everything else stays raw.
275
+ */
276
+ async function call({ target, abi, method, params = [], rawStack = false }) {
277
+ const name = method !== null && method !== void 0 ? method : abi;
278
+ if (!name)
279
+ throw new Error('call: method (or abi) is required');
280
+ const { exit_code, stack } = await runGetMethod({ address: target, method: name, stack: params });
281
+ if (exit_code !== 0)
282
+ throw new Error(`Expected a zero exit code, but got ${exit_code} (${name} on ${target})`);
283
+ if (rawStack)
284
+ return stack;
285
+ return stack.map(decodeStackItem);
286
+ }
287
+ function txTime(tx) {
288
+ var _a;
289
+ const t = (_a = tx === null || tx === void 0 ? void 0 : tx.now) !== null && _a !== void 0 ? _a : tx === null || tx === void 0 ? void 0 : tx.utime;
290
+ return t === undefined || t === null ? undefined : Number(t);
291
+ }
292
+ function inWindow(time, start, end) {
293
+ if (time === undefined)
294
+ return true;
295
+ if (start !== undefined && time < start)
296
+ return false;
297
+ if (end !== undefined && time >= end)
298
+ return false;
299
+ return true;
300
+ }
301
+ /**
302
+ * Every v3 transaction of `account` in `[startTimestamp, endTimestamp)`, paging through
303
+ * `/transactions` (toncenter's `end_utime` is inclusive, so the last second is dropped to
304
+ * keep consecutive windows disjoint).
305
+ */
306
+ async function getTransactions({ account, startTimestamp, endTimestamp, limit = 128, sort = 'desc', dedupe = true, maxPages, params }) {
307
+ var _a;
308
+ const all = [];
309
+ const seen = new Set();
310
+ const accountParam = Array.isArray(account) ? account.join(',') : account;
311
+ for (let page = 0;; page++) {
312
+ const res = await toncenterGet({
313
+ path: 'transactions',
314
+ params: { account: accountParam, start_utime: startTimestamp, end_utime: endTimestamp, limit, offset: page * limit, sort, ...params },
315
+ });
316
+ // a body without a transactions array is a failure wearing a 200
317
+ if (!Array.isArray(res === null || res === void 0 ? void 0 : res.transactions))
318
+ throw new Error(`Expected a transactions array from toncenter for ${accountParam}`);
319
+ const txs = res.transactions;
320
+ for (const tx of txs) {
321
+ if (dedupe) {
322
+ const key = (_a = tx.hash) !== null && _a !== void 0 ? _a : `${tx.lt}:${txTime(tx)}`;
323
+ if (seen.has(key))
324
+ continue;
325
+ seen.add(key);
326
+ }
327
+ if (!inWindow(txTime(tx), startTimestamp, endTimestamp))
328
+ continue;
329
+ all.push(tx);
330
+ }
331
+ if (txs.length < limit)
332
+ break;
333
+ if (maxPages !== undefined && page + 1 >= maxPages)
334
+ break;
335
+ (0, debugLog_1.debugLog)(`[chains.ton] transactions page ${page + 1} for ${accountParam} (${all.length} so far)`);
336
+ }
337
+ return all;
338
+ }
339
+ /** Every v3 message matching the filters in `[startTimestamp, endTimestamp)`, paging through `/messages` */
340
+ async function getMessages({ source, destination, direction, opcode, bodyHash, startTimestamp, endTimestamp, limit = 128, sort = 'desc', dedupe = true, maxPages, params }) {
341
+ var _a, _b;
342
+ if (!source && !destination && !bodyHash && !opcode)
343
+ throw new Error('getMessages: source, destination, opcode or bodyHash is required');
344
+ const all = [];
345
+ const seen = new Set();
346
+ const label = (_b = (_a = source !== null && source !== void 0 ? source : destination) !== null && _a !== void 0 ? _a : bodyHash) !== null && _b !== void 0 ? _b : opcode;
347
+ for (let page = 0;; page++) {
348
+ const res = await toncenterGet({
349
+ path: 'messages',
350
+ params: { source, destination, direction, opcode, body_hash: bodyHash, start_utime: startTimestamp, end_utime: endTimestamp, limit, offset: page * limit, sort, ...params },
351
+ });
352
+ if (!Array.isArray(res === null || res === void 0 ? void 0 : res.messages))
353
+ throw new Error(`Expected a messages array from toncenter for ${label}`);
354
+ const messages = res.messages;
355
+ for (const message of messages) {
356
+ if (dedupe && message.hash) {
357
+ if (seen.has(message.hash))
358
+ continue;
359
+ seen.add(message.hash);
360
+ }
361
+ const created = message.created_at === undefined || message.created_at === null ? undefined : Number(message.created_at);
362
+ if (!inWindow(created, startTimestamp, endTimestamp))
363
+ continue;
364
+ all.push(message);
365
+ }
366
+ if (messages.length < limit)
367
+ break;
368
+ if (maxPages !== undefined && page + 1 >= maxPages)
369
+ break;
370
+ (0, debugLog_1.debugLog)(`[chains.ton] messages page ${page + 1} for ${label} (${all.length} so far)`);
371
+ }
372
+ return all;
373
+ }
374
+ // ---------------------------------------------------------------------------
375
+ // toncenter: blocks
376
+ // ---------------------------------------------------------------------------
377
+ exports.MASTERCHAIN_SHARD = '-9223372036854775808';
378
+ /** v3 `masterchainInfo`: `{ first: block, last: block }` where `last.seqno` is the current masterchain height */
379
+ async function getMasterchainInfo() {
380
+ return toncenterGet({ path: 'masterchainInfo' });
381
+ }
382
+ /** v2 `lookupBlock` by seqno, unixtime or lt. Returns `{ workchain, shard, seqno, root_hash, file_hash }` */
383
+ async function lookupBlock({ workchain = -1, shard = exports.MASTERCHAIN_SHARD, seqno, utime, lt } = {}) {
384
+ if (seqno === undefined && utime === undefined && lt === undefined)
385
+ throw new Error('lookupBlock: seqno, utime or lt is required');
386
+ const res = await toncenterGet({ path: 'lookupBlock', params: { workchain, shard, seqno, unixtime: utime, lt }, version: 2 });
387
+ return unwrapV2(res, 'lookupBlock');
388
+ }
389
+ /** Masterchain block at `timestamp`: `{ number: seqno, timestamp, block }` */
390
+ async function getBlockAtTimestamp({ timestamp }) {
391
+ const block = await lookupBlock({ utime: timestamp });
392
+ return { number: Number(block.seqno), timestamp, block };
393
+ }
394
+ /** Latest masterchain block: `{ number: seqno, timestamp: gen_utime, block }` */
395
+ async function getLatestBlock() {
396
+ const { last } = await getMasterchainInfo();
397
+ if (!(last === null || last === void 0 ? void 0 : last.seqno))
398
+ throw new Error('toncenter masterchainInfo: no last block');
399
+ return { number: Number(last.seqno), timestamp: Number(last.gen_utime), block: last };
400
+ }
401
+ // ---------------------------------------------------------------------------
402
+ // tonapi
403
+ // ---------------------------------------------------------------------------
404
+ function tonapiPath(path) {
405
+ return `/v2/${path.replace(/^\/+/, '')}`;
406
+ }
407
+ /** GET `/v2/{path}` on tonapi */
408
+ async function tonapiGet({ path, params }) {
409
+ return paced('TONAPI', () => (0, rpc_1.httpGet)(getTonapiEndpoints(), {
410
+ path: tonapiPath(path),
411
+ params: dropUndefined(params),
412
+ headers: tonapiHeaders(),
413
+ ...TONAPI_RETRY,
414
+ }));
415
+ }
416
+ /** POST to `/v2/{path}` on tonapi */
417
+ async function tonapiPost({ path, body = {} }) {
418
+ return paced('TONAPI', () => (0, rpc_1.httpPost)(getTonapiEndpoints(), body, {
419
+ path: tonapiPath(path),
420
+ headers: tonapiHeaders(),
421
+ ...TONAPI_RETRY,
422
+ }));
423
+ }
424
+ /**
425
+ * Jetton balances of an account from tonapi `accounts/{address}/jettons`, with USD prices.
426
+ * Returns the `balances` array (the adapters helper re-keyed it by jetton address; use
427
+ * `jettonBalancesByAddress` for that shape). Addresses are in raw `0:...` form.
428
+ */
429
+ async function getJettonBalances({ address, currencies = 'usd' }) {
430
+ const res = await tonapiGet({ path: `accounts/${encodeURIComponent(address)}/jettons`, params: { currencies } });
431
+ if (!Array.isArray(res === null || res === void 0 ? void 0 : res.balances))
432
+ throw new Error(`tonapi accounts/${address}/jettons: no balances array`);
433
+ return res.balances;
434
+ }
435
+ /** `{ [rawJettonAddress]: { balance, price, decimals } }`, the shape the adapters helper returned */
436
+ function jettonBalancesByAddress(balances, currency = 'USD') {
437
+ const res = {};
438
+ balances.forEach(b => {
439
+ var _a, _b;
440
+ res[b.jetton.address] = { balance: b.balance, price: (_b = (_a = b.price) === null || _a === void 0 ? void 0 : _a.prices) === null || _b === void 0 ? void 0 : _b[currency], decimals: b.jetton.decimals };
441
+ });
442
+ return res;
443
+ }
444
+ /** tonapi `jettons/{address}`: `{ mintable, total_supply, metadata: { symbol, decimals, name, ... }, holders_count, ... }` */
445
+ async function getJettonInfo({ address }) {
446
+ return tonapiGet({ path: `jettons/${encodeURIComponent(address)}` });
447
+ }
448
+ /** tonapi `jettons/_bulk`, 100 addresses per request */
449
+ async function getJettonsInfo({ addresses }) {
450
+ var _a;
451
+ const result = [];
452
+ for (const chunk of (0, rpc_1.sliceIntoChunks)(addresses, 100)) {
453
+ const res = await tonapiPost({ path: 'jettons/_bulk', body: { account_ids: chunk } });
454
+ result.push(...((_a = res === null || res === void 0 ? void 0 : res.jettons) !== null && _a !== void 0 ? _a : []));
455
+ }
456
+ return result;
457
+ }
458
+ /** USD prices from tonapi `rates`, keyed by the token string that was passed in ('ton' or a jetton address) */
459
+ async function getTokenRates({ tokens, currency = 'usd' }) {
460
+ var _a;
461
+ if (!tokens.length)
462
+ return {};
463
+ const res = await tonapiGet({ path: 'rates', params: { tokens: tokens.join(','), currencies: currency } });
464
+ const rates = (_a = res === null || res === void 0 ? void 0 : res.rates) !== null && _a !== void 0 ? _a : {};
465
+ const prices = {};
466
+ const key = currency.toUpperCase();
467
+ tokens.forEach(token => {
468
+ var _a, _b;
469
+ const price = (_b = (_a = rates[token]) === null || _a === void 0 ? void 0 : _a.prices) === null || _b === void 0 ? void 0 : _b[key];
470
+ if (price !== undefined && price !== null)
471
+ prices[token] = price;
472
+ });
473
+ return prices;
474
+ }
475
+ // ---------------------------------------------------------------------------
476
+ // codec: crc16 & Address
477
+ // ---------------------------------------------------------------------------
478
+ const BOUNCEABLE_TAG = 0x11;
479
+ const NON_BOUNCEABLE_TAG = 0x51;
480
+ const TEST_FLAG = 0x80;
481
+ /** CRC-16/XMODEM (poly 0x1021, init 0), the checksum used by friendly TON addresses */
482
+ function crc16(data) {
483
+ let crc = 0;
484
+ for (let i = 0; i < data.length; i++) {
485
+ crc ^= data[i] << 8;
486
+ for (let j = 0; j < 8; j++) {
487
+ crc = crc & 0x8000 ? ((crc << 1) ^ 0x1021) & 0xffff : (crc << 1) & 0xffff;
488
+ }
489
+ }
490
+ return crc;
491
+ }
492
+ function base64ToBuffer(source) {
493
+ return Buffer.from(source.replace(/-/g, '+').replace(/_/g, '/'), 'base64');
494
+ }
495
+ function bufferToBase64(buffer, urlSafe) {
496
+ const b64 = buffer.toString('base64');
497
+ return urlSafe ? b64.replace(/\+/g, '-').replace(/\//g, '_') : b64;
498
+ }
499
+ function parseFriendlyBuffer(data, source) {
500
+ // 1 byte tag + 1 byte workchain + 32 bytes hash + 2 bytes crc
501
+ if (data.length !== 36)
502
+ throw new Error(`Unknown address type: byte length is not equal to 36 (${source})`);
503
+ const addr = data.subarray(0, 34);
504
+ if (crc16(addr) !== data.readUInt16BE(34))
505
+ throw new Error(`Invalid checksum: ${source}`);
506
+ let tag = addr[0];
507
+ let isTestOnly = false;
508
+ if (tag & TEST_FLAG) {
509
+ isTestOnly = true;
510
+ tag ^= TEST_FLAG;
511
+ }
512
+ if (tag !== BOUNCEABLE_TAG && tag !== NON_BOUNCEABLE_TAG)
513
+ throw new Error(`Unknown address tag ${tag} (${source})`);
514
+ const workchain = addr.readInt8(1);
515
+ return { isTestOnly, isBounceable: tag === BOUNCEABLE_TAG, workchain, hashPart: Buffer.from(addr.subarray(2, 34)) };
516
+ }
517
+ /**
518
+ * TON address (workchain + 256 bit hash), ported from ton-core. Parses friendly
519
+ * (`EQ...` / `UQ...`, base64 or base64url) and raw (`0:abcd...`) forms.
520
+ */
521
+ class Address {
522
+ constructor(workChain, hash) {
523
+ if (hash.length !== 32)
524
+ throw new Error(`Invalid address hash length: ${hash.length}`);
525
+ this.workChain = workChain;
526
+ this.hash = hash;
527
+ Object.freeze(this);
528
+ }
529
+ static isAddress(src) {
530
+ return src instanceof Address;
531
+ }
532
+ /** shape check only (48 base64 chars); use `isAddress` to also verify the checksum */
533
+ static isFriendly(source) {
534
+ return typeof source === 'string' && /^[A-Za-z0-9+/_-]{48}$/.test(source);
535
+ }
536
+ static isRaw(source) {
537
+ if (typeof source !== 'string')
538
+ return false;
539
+ const parts = source.split(':');
540
+ if (parts.length !== 2)
541
+ return false;
542
+ const [wc, hash] = parts;
543
+ if (!/^-?\d+$/.test(wc))
544
+ return false;
545
+ return /^[a-fA-F0-9]{64}$/.test(hash);
546
+ }
547
+ static parse(source) {
548
+ if (Address.isAddress(source))
549
+ return source;
550
+ if (Address.isFriendly(source))
551
+ return Address.parseFriendly(source).address;
552
+ if (Address.isRaw(source))
553
+ return Address.parseRaw(source);
554
+ throw new Error(`Unknown address type: ${source}`);
555
+ }
556
+ static parseRaw(source) {
557
+ if (!Address.isRaw(source))
558
+ throw new Error(`Invalid raw address: ${source}`);
559
+ const [wc, hash] = source.split(':');
560
+ return new Address(parseInt(wc, 10), Buffer.from(hash, 'hex'));
561
+ }
562
+ static parseFriendly(source) {
563
+ let data;
564
+ if (Buffer.isBuffer(source))
565
+ data = source;
566
+ else {
567
+ if (!Address.isFriendly(source))
568
+ throw new Error(`Unknown address type: ${source}`);
569
+ data = base64ToBuffer(source);
570
+ }
571
+ const r = parseFriendlyBuffer(data, Buffer.isBuffer(source) ? source.toString('base64') : source);
572
+ return { isBounceable: r.isBounceable, isTestOnly: r.isTestOnly, address: new Address(r.workchain, r.hashPart) };
573
+ }
574
+ /** friendly form of any address string (or Address), bounceable + url-safe by default */
575
+ static normalize(source, options) {
576
+ return Address.parse(source).toString(options);
577
+ }
578
+ toRawString() {
579
+ return `${this.workChain}:${this.hash.toString('hex')}`;
580
+ }
581
+ /** 36 bytes: hash followed by the workchain repeated 4 times (ton-core `toRaw`) */
582
+ toRaw() {
583
+ const out = Buffer.alloc(36);
584
+ out.set(this.hash);
585
+ const wc = this.workChain & 0xff;
586
+ out.set([wc, wc, wc, wc], 32);
587
+ return out;
588
+ }
589
+ toStringBuffer({ bounceable = true, testOnly = false } = {}) {
590
+ let tag = bounceable ? BOUNCEABLE_TAG : NON_BOUNCEABLE_TAG;
591
+ if (testOnly)
592
+ tag |= TEST_FLAG;
593
+ const out = Buffer.alloc(36);
594
+ out[0] = tag;
595
+ out.writeInt8(this.workChain, 1);
596
+ out.set(this.hash, 2);
597
+ out.writeUInt16BE(crc16(out.subarray(0, 34)), 34);
598
+ return out;
599
+ }
600
+ toString(options = {}) {
601
+ const { urlSafe = true } = options;
602
+ return bufferToBase64(this.toStringBuffer(options), urlSafe);
603
+ }
604
+ equals(src) {
605
+ return src.workChain === this.workChain && src.hash.equals(this.hash);
606
+ }
607
+ }
608
+ exports.Address = Address;
609
+ /** `Address.parse` */
610
+ function parseAddress(source) {
611
+ return Address.parse(source);
612
+ }
613
+ /** Friendly form of any address string, bounceable + url-safe by default */
614
+ function normalizeAddress(source, { bounceable = true, urlSafe = true, testOnly = false } = {}) {
615
+ return Address.parse(source).toString({ bounceable, urlSafe, testOnly });
616
+ }
617
+ /** `wc:hex` form of any address string */
618
+ function toRawAddress(source) {
619
+ return Address.parse(source).toRawString();
620
+ }
621
+ /** true when `source` parses as a friendly (checksum verified) or raw address */
622
+ function isAddress(source) {
623
+ if (Address.isAddress(source))
624
+ return true;
625
+ if (typeof source !== 'string')
626
+ return false;
627
+ try {
628
+ Address.parse(source);
629
+ return true;
630
+ }
631
+ catch {
632
+ return false;
633
+ }
634
+ }
635
+ /** 256 bit account hash as a bigint (the workchain is dropped) */
636
+ function addressToInt(address) {
637
+ return BigInt(`0x${Address.parse(address).hash.toString('hex')}`);
638
+ }
639
+ /** Inverse of `addressToInt`, workchain 0 by default */
640
+ function convertIntToAddress(value, workChain = 0) {
641
+ const hex = BigInt(value).toString(16).padStart(64, '0');
642
+ if (hex.length !== 64)
643
+ throw new Error(`convertIntToAddress: value does not fit 256 bits`);
644
+ return Address.parseRaw(`${workChain}:${hex}`);
645
+ }
646
+ /** true when both parse and point at the same workchain + hash, false for anything unparsable */
647
+ function compareAddress(a, b) {
648
+ if (!a || !b)
649
+ return false;
650
+ try {
651
+ return Address.parse(a).equals(Address.parse(b));
652
+ }
653
+ catch {
654
+ return false;
655
+ }
656
+ }
657
+ // ---------------------------------------------------------------------------
658
+ // codec: BoC & bit reader
659
+ // ---------------------------------------------------------------------------
660
+ function decodeBase64(source) {
661
+ return base64ToBuffer(source);
662
+ }
663
+ /** MSB-first bit reader over a buffer */
664
+ class BitReader {
665
+ constructor(buffer, startByte = 0, startBit = 0) {
666
+ this.buffer = buffer;
667
+ this.byteOffset = startByte;
668
+ this.bitOffset = startBit;
669
+ }
670
+ get remainingBits() {
671
+ return (this.buffer.length - this.byteOffset) * 8 - this.bitOffset;
672
+ }
673
+ readBit() {
674
+ if (this.byteOffset >= this.buffer.length)
675
+ throw new Error('Buffer overflow while reading bits');
676
+ const bit = (this.buffer[this.byteOffset] >> (7 - this.bitOffset)) & 1;
677
+ this.bitOffset++;
678
+ if (this.bitOffset === 8) {
679
+ this.bitOffset = 0;
680
+ this.byteOffset++;
681
+ }
682
+ return bit;
683
+ }
684
+ /** unsigned integer of `n` bits (n <= 53) */
685
+ readBits(n) {
686
+ if (n > 53)
687
+ throw new Error('readBits: use readBigUint for more than 53 bits');
688
+ let value = 0;
689
+ for (let i = 0; i < n; i++)
690
+ value = value * 2 + this.readBit();
691
+ return value;
692
+ }
693
+ readUint(n) {
694
+ return this.readBits(n);
695
+ }
696
+ /** two's complement signed integer of `n` bits */
697
+ readInt(n) {
698
+ const value = this.readBits(n);
699
+ return value >= 2 ** (n - 1) ? value - 2 ** n : value;
700
+ }
701
+ readBigUint(n) {
702
+ let value = BigInt(0);
703
+ for (let i = 0; i < n; i++)
704
+ value = (value << BigInt(1)) | BigInt(this.readBit());
705
+ return value;
706
+ }
707
+ readBytes(n) {
708
+ const out = Buffer.alloc(n);
709
+ for (let i = 0; i < n; i++)
710
+ out[i] = this.readBits(8);
711
+ return out;
712
+ }
713
+ skip(n) {
714
+ for (let i = 0; i < n; i++)
715
+ this.readBit();
716
+ }
717
+ }
718
+ exports.BitReader = BitReader;
719
+ /** Parse a serialized bag-of-cells header (`b5ee9c72` magic) and return the raw cell data */
720
+ function parseBoc(buffer) {
721
+ if (buffer.length < 4)
722
+ throw new Error('Buffer is too short to contain magic bytes');
723
+ if (buffer.readUInt32BE(0) !== 0xb5ee9c72)
724
+ throw new Error('Invalid magic');
725
+ let offset = 4;
726
+ const byte = buffer[offset++];
727
+ const hasIdx = !!((byte >> 7) & 1);
728
+ const hasCrc32c = !!((byte >> 6) & 1);
729
+ const hasCacheBits = !!((byte >> 5) & 1);
730
+ const flags = (byte >> 3) & 0b11;
731
+ const size = byte & 0b111;
732
+ const offBytes = buffer.readUInt8(offset++);
733
+ const cells = buffer.readUIntBE(offset, size);
734
+ offset += size;
735
+ const roots = buffer.readUIntBE(offset, size);
736
+ offset += size;
737
+ const absent = buffer.readUIntBE(offset, size);
738
+ offset += size;
739
+ const totalCellSize = buffer.readUIntBE(offset, offBytes);
740
+ offset += offBytes;
741
+ const root = [];
742
+ for (let i = 0; i < roots; i++) {
743
+ root.push(buffer.readUIntBE(offset, size));
744
+ offset += size;
745
+ }
746
+ let index = null;
747
+ if (hasIdx) {
748
+ index = buffer.subarray(offset, offset + cells * offBytes);
749
+ offset += cells * offBytes;
750
+ }
751
+ const cellData = buffer.subarray(offset, offset + totalCellSize);
752
+ offset += totalCellSize;
753
+ return { hasIdx, hasCrc32c, hasCacheBits, flags, size, offBytes, cells, roots, absent, totalCellSize, root, index, cellData, offset };
754
+ }
755
+ /** Friendly address for a workchain + 32 byte hash (bounceable, url-safe by default) */
756
+ function serializeAddress(wc, hash, options = {}) {
757
+ return new Address(wc, hash).toString(options);
758
+ }
759
+ /**
760
+ * Read the `MsgAddressInt` (addr_std) stored at the start of the first cell of a base64
761
+ * BoC, as returned in a runGetMethod `cell` / `slice` stack entry. Returns the friendly
762
+ * bounceable address. Throws when the cell does not start with an addr_std.
763
+ */
764
+ function readAddressFromSlice(base64Boc) {
765
+ const { cellData } = parseBoc(decodeBase64(base64Boc));
766
+ if (cellData.length < 2 + 33)
767
+ throw new Error('Cell too short to hold an address');
768
+ // cell layout: d1 (refs descriptor), d2 (data length descriptor), data bits
769
+ const reader = new BitReader(cellData, 2, 0);
770
+ const tag = reader.readBits(2);
771
+ if (tag !== 0b10)
772
+ throw new Error(`Not an addr_std (tag ${tag})`);
773
+ const anycast = reader.readBit();
774
+ if (anycast !== 0)
775
+ throw new Error('Anycast addresses are not supported');
776
+ const wc = reader.readInt(8);
777
+ const hash = reader.readBytes(32);
778
+ return serializeAddress(wc, hash);
779
+ }
780
+ /** `readAddressFromSlice` that returns null instead of throwing */
781
+ function tryReadAddressFromSlice(base64Boc) {
782
+ try {
783
+ return readAddressFromSlice(base64Boc);
784
+ }
785
+ catch {
786
+ return null;
787
+ }
788
+ }