@defillama/sdk 5.0.235 → 5.0.238

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 (69) hide show
  1. package/build/ChainApi.d.ts +23 -1
  2. package/build/ChainApi.js +13 -0
  3. package/build/abi/tron.js +7 -1
  4. package/build/chains/algorand.d.ts +277 -0
  5. package/build/chains/algorand.js +618 -0
  6. package/build/chains/aptos.d.ts +230 -0
  7. package/build/chains/aptos.js +612 -0
  8. package/build/chains/cardano.d.ts +242 -0
  9. package/build/chains/cardano.js +407 -0
  10. package/build/chains/cosmos.d.ts +252 -0
  11. package/build/chains/cosmos.js +618 -0
  12. package/build/chains/icp.d.ts +235 -0
  13. package/build/chains/icp.js +1040 -0
  14. package/build/chains/index.d.ts +23 -0
  15. package/build/chains/index.js +59 -0
  16. package/build/chains/near.d.ts +192 -0
  17. package/build/chains/near.js +446 -0
  18. package/build/chains/rpc.d.ts +125 -0
  19. package/build/chains/rpc.js +326 -0
  20. package/build/chains/starknet.d.ts +205 -0
  21. package/build/chains/starknet.js +884 -0
  22. package/build/chains/stellar.d.ts +410 -0
  23. package/build/chains/stellar.js +1082 -0
  24. package/build/chains/substrate.d.ts +333 -0
  25. package/build/chains/substrate.js +1042 -0
  26. package/build/chains/sui.d.ts +365 -0
  27. package/build/chains/sui.js +1135 -0
  28. package/build/chains/svm.d.ts +340 -0
  29. package/build/chains/svm.js +760 -0
  30. package/build/chains/tezos.d.ts +225 -0
  31. package/build/chains/tezos.js +412 -0
  32. package/build/chains/ton.d.ts +353 -0
  33. package/build/chains/ton.js +788 -0
  34. package/build/chains/tron.d.ts +45 -0
  35. package/build/chains/tron.js +110 -0
  36. package/build/chains/utxo.d.ts +202 -0
  37. package/build/chains/utxo.js +592 -0
  38. package/build/chains/xrpl.d.ts +179 -0
  39. package/build/chains/xrpl.js +467 -0
  40. package/build/index.d.ts +2 -1
  41. package/build/index.js +3 -1
  42. package/build/providers.json +1 -1
  43. package/build/util/LlamaProvider.js +2 -0
  44. package/build/util/blocks.js +292 -84
  45. package/build/util/chainUtils/data.json +80 -0
  46. package/build/util/chainUtils/index.d.ts +1 -0
  47. package/build/util/chainUtils/index.js +23 -0
  48. package/build/util/coins.js +14 -3
  49. package/build/util/common.js +6 -4
  50. package/build/util/computeTVL.js +1 -1
  51. package/build/util/cosmos.d.ts +1 -11
  52. package/build/util/cosmos.js +6 -66
  53. package/build/util/env.d.ts +1 -0
  54. package/build/util/env.js +18 -0
  55. package/build/util/indexer.compatibility.d.ts +6 -0
  56. package/build/util/indexer.compatibility.js +64 -0
  57. package/build/util/indexer.d.ts +24 -2
  58. package/build/util/indexer.js +333 -294
  59. package/build/util/indexerSql.d.ts +8 -1
  60. package/build/util/indexerSql.js +53 -22
  61. package/build/util/logs.d.ts +3 -0
  62. package/build/util/logs.decode.shared.js +5 -4
  63. package/build/util/logs.js +57 -26
  64. package/build/util/transactions.d.ts +23 -1
  65. package/build/util/updateChainLabels.d.ts +1 -0
  66. package/build/util/updateChainLabels.js +5 -0
  67. package/build/util/updateProviderList.js +40 -18
  68. package/package.json +20 -7
  69. package/README.md +0 -41
@@ -0,0 +1,612 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.APT_DECIMALS = exports.APT_COIN_TYPE = exports.aptosChains = exports.ARCHIVAL_ENDPOINTS = exports.DEFAULT_ENDPOINTS = void 0;
7
+ exports.isAptosChain = isAptosChain;
8
+ exports.getEndpointList = getEndpointList;
9
+ exports.getEndpoint = getEndpoint;
10
+ exports.getArchivalEndpointList = getArchivalEndpointList;
11
+ exports.get = get;
12
+ exports.post = post;
13
+ exports.getLedgerInfo = getLedgerInfo;
14
+ exports.getAccount = getAccount;
15
+ exports.getResources = getResources;
16
+ exports.getResource = getResource;
17
+ exports.getAccountModules = getAccountModules;
18
+ exports.getAccountModule = getAccountModule;
19
+ exports.getTableItem = getTableItem;
20
+ exports.view = view;
21
+ exports.getEvents = getEvents;
22
+ exports.getEventsByCreationNumber = getEventsByCreationNumber;
23
+ exports.getBlockByHeight = getBlockByHeight;
24
+ exports.getBlockByVersion = getBlockByVersion;
25
+ exports.getLatestBlock = getLatestBlock;
26
+ exports.getBlockAtTimestamp = getBlockAtTimestamp;
27
+ exports.getVersionAtTimestamp = getVersionAtTimestamp;
28
+ exports.getBalance = getBalance;
29
+ exports.getCoinInfo = getCoinInfo;
30
+ exports.getCoinSupply = getCoinSupply;
31
+ exports.hexToString = hexToString;
32
+ exports.octasToApt = octasToApt;
33
+ exports.isFungibleAssetAddress = isFungibleAssetAddress;
34
+ exports.normalizeAddress = normalizeAddress;
35
+ exports.parseTypeTag = parseTypeTag;
36
+ /**
37
+ * Aptos-style Move REST client for `aptos` (default) and `move` (Movement).
38
+ *
39
+ * Replaces the scattered per-repo helpers:
40
+ * - DefiLlama-Adapters `projects/helper/chain/aptos.js` (endpointMap, aQuery, getResources with
41
+ * `x-aptos-cursor` pagination, getResource, getBalance, getTableData, function_view, hexToString,
42
+ * timestampToVersion)
43
+ * - DefiLlama-Adapters `projects/helper/env.js` (APTOS_RPC / MOVE_RPC defaults)
44
+ * - defillama-server `defi/l2/utils.ts` (aptosFetch with 404-as-answer, getAptosSupplies resolution chain)
45
+ * - defillama-server `coins/src/scripts/coingeckoUtils.ts` (CoinInfo metadata lookup)
46
+ * - peggedassets-server `src/adapters/peggedAssets/helper/aptos.ts` (getTokenSupply FA branches, function_view)
47
+ * - dimension-adapters `helpers/aptos.ts` (getResources, view, getCoinSupply, octasToApt, getVersionFromTimestamp)
48
+ *
49
+ * All endpoints resolve through `rpc.getEndpoints`, so `APTOS_RPC` / `MOVE_RPC` (comma separated
50
+ * for fallbacks) override the defaults. An optional `<CHAIN>_API_KEY` (e.g. `APTOS_API_KEY`) is sent
51
+ * as a bearer token. Requests the primary answers with 410 `block_pruned` / `version_pruned` are
52
+ * retried on the archival endpoint (`<CHAIN>_ARCHIVAL_RPC`, default `archive.mainnet.aptoslabs.com`).
53
+ * Timestamp -> version lookups use a REST binary search over block heights instead of the
54
+ * aptoslabs indexer GraphQL API, so they work on any full node and on Movement.
55
+ *
56
+ * Usage: `sdk.chains.aptos.getResource({ account: '0x1', type: '0x1::coin::CoinInfo<0x1::aptos_coin::AptosCoin>' })`
57
+ */
58
+ const axios_1 = __importDefault(require("axios"));
59
+ const env_1 = require("../util/env");
60
+ const debugLog_1 = require("../util/debugLog");
61
+ const rpc_1 = require("./rpc");
62
+ // ---------------------------------------------------------------------------
63
+ // config
64
+ // ---------------------------------------------------------------------------
65
+ exports.DEFAULT_ENDPOINTS = {
66
+ aptos: 'https://fullnode.mainnet.aptoslabs.com',
67
+ move: 'https://mainnet.movementnetwork.xyz',
68
+ };
69
+ /**
70
+ * Archival nodes used when the primary answers 410 `block_pruned` / `version_pruned`
71
+ * (the public aptos fullnode keeps only a few weeks of blocks). Override with `<CHAIN>_ARCHIVAL_RPC`.
72
+ */
73
+ exports.ARCHIVAL_ENDPOINTS = {
74
+ aptos: 'https://archive.mainnet.aptoslabs.com',
75
+ };
76
+ exports.aptosChains = Object.keys(exports.DEFAULT_ENDPOINTS);
77
+ exports.APT_COIN_TYPE = '0x1::aptos_coin::AptosCoin';
78
+ exports.APT_DECIMALS = 8;
79
+ const DEFAULT_TIMEOUT = 30000;
80
+ const RESOURCE_PAGE_SIZE = 9999;
81
+ const MODULE_PAGE_SIZE = 1000;
82
+ const EVENT_PAGE_SIZE = 100;
83
+ function isAptosChain(chain) {
84
+ return !!chain && exports.aptosChains.includes(chain);
85
+ }
86
+ function resolveChain(chain) {
87
+ return chain !== null && chain !== void 0 ? chain : 'aptos';
88
+ }
89
+ /** All configured endpoints for the chain (env override first, then defaults). */
90
+ function getEndpointList({ chain } = {}) {
91
+ chain = resolveChain(chain);
92
+ return (0, rpc_1.getEndpoints)(chain, exports.DEFAULT_ENDPOINTS[chain]);
93
+ }
94
+ /** First configured endpoint for the chain. */
95
+ function getEndpoint({ chain } = {}) {
96
+ chain = resolveChain(chain);
97
+ return (0, rpc_1.getEndpoint)(chain, exports.DEFAULT_ENDPOINTS[chain]);
98
+ }
99
+ /** Archival endpoints for the chain (`<CHAIN>_ARCHIVAL_RPC` env, then built-in); empty when none. */
100
+ function getArchivalEndpointList({ chain } = {}) {
101
+ chain = resolveChain(chain);
102
+ try {
103
+ return (0, rpc_1.getEndpoints)(chain, exports.ARCHIVAL_ENDPOINTS[chain], { envKey: `${chain.toUpperCase()}_ARCHIVAL_RPC` });
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ function getHeaders(chain) {
110
+ const headers = { 'Content-Type': 'application/json' };
111
+ const apiKey = (0, env_1.getEnvValue)(`${chain.toUpperCase()}_API_KEY`);
112
+ if (apiKey)
113
+ headers['Authorization'] = `Bearer ${apiKey}`;
114
+ return headers;
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // transport
118
+ // ---------------------------------------------------------------------------
119
+ const NOT_FOUND = Symbol('aptos:not-found');
120
+ function isNotFound(e) {
121
+ var _a;
122
+ return ((_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status) === 404;
123
+ }
124
+ /** 410 `block_pruned` / `version_pruned`: the data exists but this node no longer has it. */
125
+ function isPruned(e) {
126
+ var _a, _b, _c, _d;
127
+ if (((_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status) === 410 || (e === null || e === void 0 ? void 0 : e.status) === 410)
128
+ return true;
129
+ const code = String((_d = (_c = (_b = e === null || e === void 0 ? void 0 : e.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.error_code) !== null && _d !== void 0 ? _d : '');
130
+ return code.includes('pruned');
131
+ }
132
+ /**
133
+ * Low level request: rotates through the configured endpoints, retries transient
134
+ * failures, sends the optional bearer key and (when `allowNotFound`) turns a 404 into
135
+ * `null` before the retry layer sees it (a 404 is a real answer for missing resources).
136
+ * A 410 "pruned" answer from the primary is retried once against the archival endpoints.
137
+ */
138
+ async function request(options) {
139
+ const chain = resolveChain(options.chain);
140
+ const archivalEndpoints = getArchivalEndpointList({ chain });
141
+ if (options.archival) {
142
+ if (!archivalEndpoints.length)
143
+ throw new Error(`[${chain}] no archival endpoint configured (env ${chain.toUpperCase()}_ARCHIVAL_RPC)`);
144
+ return requestOn(archivalEndpoints, options);
145
+ }
146
+ try {
147
+ return await requestOn(getEndpointList({ chain }), options);
148
+ }
149
+ catch (e) {
150
+ if (!archivalEndpoints.length || !isPruned(e))
151
+ throw e;
152
+ (0, debugLog_1.debugLog)(`[chains.aptos] ${chain} ${options.method.toUpperCase()} ${options.path} pruned on primary, retrying on archival`);
153
+ return requestOn(archivalEndpoints, options);
154
+ }
155
+ }
156
+ async function requestOn(endpoints, options) {
157
+ var _a;
158
+ const chain = resolveChain(options.chain);
159
+ const limiter = (0, rpc_1.getLimiter)(chain.toUpperCase(), 10);
160
+ const { method, path, params, body, allowNotFound, withMetadata, timeout = DEFAULT_TIMEOUT } = options;
161
+ const headers = getHeaders(chain);
162
+ const config = { timeout, headers, params };
163
+ const res = await (0, rpc_1.withRetry)(async (attempt) => {
164
+ const base = endpoints[attempt % endpoints.length];
165
+ const url = (0, rpc_1.joinUrl)(base, path);
166
+ return limiter(async () => {
167
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
168
+ try {
169
+ const response = method === 'post'
170
+ ? await axios_1.default.post(url, body, config)
171
+ : await axios_1.default.get(url, config);
172
+ return withMetadata ? response : response.data;
173
+ }
174
+ catch (e) {
175
+ if (allowNotFound && isNotFound(e)) {
176
+ (0, debugLog_1.debugLog)(`[chains.aptos] ${chain} ${method.toUpperCase()} ${path} -> 404 (treated as not found)`);
177
+ return NOT_FOUND;
178
+ }
179
+ // keep the error message helpful when the retry layer reformats it
180
+ const status = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status;
181
+ const detail = (_g = (_d = (_c = (_b = e === null || e === void 0 ? void 0 : e.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : (_f = (_e = e === null || e === void 0 ? void 0 : e.response) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.error_code) !== null && _g !== void 0 ? _g : e === null || e === void 0 ? void 0 : e.message;
182
+ const err = new Error(`[${chain}] ${method.toUpperCase()} ${path} failed${status ? ` [${status}]` : ''}: ${String(detail !== null && detail !== void 0 ? detail : '').slice(0, 300)} (${(0, rpc_1.shortUrl)(url)})`);
183
+ err.response = e === null || e === void 0 ? void 0 : e.response;
184
+ err.status = status;
185
+ err.errorCode = (_j = (_h = e === null || e === void 0 ? void 0 : e.response) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j.error_code;
186
+ err._isCustomError = true; // already formatted: keeps status/errorCode intact through withRetry/formError
187
+ throw err;
188
+ }
189
+ });
190
+ }, {
191
+ label: `${chain} ${method.toUpperCase()} ${path.split('?')[0].slice(0, 80)}`,
192
+ retries: (_a = options.retries) !== null && _a !== void 0 ? _a : Math.max(3, endpoints.length),
193
+ shouldRetry: rpc_1.isRetryableError,
194
+ });
195
+ return res === NOT_FOUND ? null : res;
196
+ }
197
+ function withLedgerVersion(params, ledgerVersion) {
198
+ if (ledgerVersion === undefined || ledgerVersion === null)
199
+ return params;
200
+ return { ...(params !== null && params !== void 0 ? params : {}), ledger_version: String(ledgerVersion) };
201
+ }
202
+ function encodeSegment(value) {
203
+ return encodeURIComponent(value);
204
+ }
205
+ /** GET `path` (relative to the node root, e.g. `/v1/accounts/0x1`). 404 -> `null` when `allowNotFound`. */
206
+ async function get({ chain, path, params, allowNotFound, timeout, retries, archival }) {
207
+ return request({ chain, method: 'get', path, params, allowNotFound, timeout, retries, archival });
208
+ }
209
+ /** POST `body` to `path` (relative to the node root, e.g. `/v1/view`). */
210
+ async function post({ chain, path, body, params, allowNotFound, timeout, retries, archival }) {
211
+ return request({ chain, method: 'post', path, body, params, allowNotFound, timeout, retries, archival });
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // ledger / accounts / resources
215
+ // ---------------------------------------------------------------------------
216
+ function microsToSeconds(value) {
217
+ return Math.floor(Number(value) / 1e6);
218
+ }
219
+ async function getLedgerInfo({ chain, archival } = {}) {
220
+ const raw = await get({ chain, path: '/v1', archival });
221
+ return {
222
+ chainId: Number(raw.chain_id),
223
+ ledgerVersion: Number(raw.ledger_version),
224
+ ledgerTimestamp: microsToSeconds(raw.ledger_timestamp),
225
+ blockHeight: Number(raw.block_height),
226
+ oldestLedgerVersion: Number(raw.oldest_ledger_version),
227
+ oldestBlockHeight: Number(raw.oldest_block_height),
228
+ epoch: Number(raw.epoch),
229
+ raw,
230
+ };
231
+ }
232
+ /** `{ sequence_number, authentication_key }` or `null` when the account does not exist. */
233
+ async function getAccount({ chain, address, ledgerVersion }) {
234
+ return get({ chain, path: `/v1/accounts/${address}`, params: withLedgerVersion(undefined, ledgerVersion), allowNotFound: true });
235
+ }
236
+ function extractPage(data) {
237
+ if (Array.isArray(data))
238
+ return data;
239
+ if (Array.isArray(data === null || data === void 0 ? void 0 : data.resources))
240
+ return data.resources;
241
+ if (Array.isArray(data === null || data === void 0 ? void 0 : data.data))
242
+ return data.data;
243
+ if (Array.isArray(data === null || data === void 0 ? void 0 : data.items))
244
+ return data.items;
245
+ return [];
246
+ }
247
+ async function getPaginated({ chain, path, pageSize, ledgerVersion, label }) {
248
+ var _a;
249
+ const items = [];
250
+ let cursor;
251
+ let pageLength = 0;
252
+ do {
253
+ const params = { limit: pageSize };
254
+ if (cursor)
255
+ params.start = cursor;
256
+ const res = await request({ chain, method: 'get', path, params: withLedgerVersion(params, ledgerVersion), withMetadata: true });
257
+ const page = extractPage(res === null || res === void 0 ? void 0 : res.data);
258
+ items.push(...page);
259
+ pageLength = page.length;
260
+ cursor = (_a = res === null || res === void 0 ? void 0 : res.headers) === null || _a === void 0 ? void 0 : _a['x-aptos-cursor'];
261
+ (0, debugLog_1.debugLog)(`[chains.aptos] ${resolveChain(chain)} ${label}: fetched ${pageLength} (total ${items.length})${cursor ? ', more pages' : ''}`);
262
+ } while (pageLength === pageSize && cursor);
263
+ return items;
264
+ }
265
+ /** Every resource of `account` (`{ type, data }[]`), following `x-aptos-cursor` pagination. */
266
+ async function getResources({ chain, account, ledgerVersion }) {
267
+ return getPaginated({ chain, path: `/v1/accounts/${account}/resources`, pageSize: RESOURCE_PAGE_SIZE, ledgerVersion, label: `resources ${account}` });
268
+ }
269
+ /** `data` of one resource, or `null` when the account does not hold it. */
270
+ async function getResource({ chain, account, type, ledgerVersion }) {
271
+ var _a;
272
+ const res = await get({ chain, path: `/v1/accounts/${account}/resource/${encodeSegment(type)}`, params: withLedgerVersion(undefined, ledgerVersion), allowNotFound: true });
273
+ if (res === null || res === undefined)
274
+ return null;
275
+ return (_a = res.data) !== null && _a !== void 0 ? _a : null;
276
+ }
277
+ /** Every module published by `account`, following `x-aptos-cursor` pagination. */
278
+ async function getAccountModules({ chain, account, ledgerVersion }) {
279
+ return getPaginated({ chain, path: `/v1/accounts/${account}/modules`, pageSize: MODULE_PAGE_SIZE, ledgerVersion, label: `modules ${account}` });
280
+ }
281
+ /** Single module ABI/bytecode, or `null` when missing. */
282
+ async function getAccountModule({ chain, account, name, ledgerVersion }) {
283
+ return get({ chain, path: `/v1/accounts/${account}/module/${encodeSegment(name)}`, params: withLedgerVersion(undefined, ledgerVersion), allowNotFound: true });
284
+ }
285
+ /**
286
+ * Read one table item. Returns the decoded value, or `null` when `allowNotFound` is set and the
287
+ * key is missing (the node answers 404 for absent keys).
288
+ */
289
+ async function getTableItem({ chain, handle, keyType, valueType, key, ledgerVersion, allowNotFound }) {
290
+ return post({ chain, path: `/v1/tables/${handle}/item`, body: { key_type: keyType, value_type: valueType, key }, params: withLedgerVersion(undefined, ledgerVersion), allowNotFound });
291
+ }
292
+ /** Run a `#[view]` function. Returns the result array as the node encodes it (u64/u128 as strings). */
293
+ async function view({ chain, function: fn, typeArguments = [], args = [], ledgerVersion }) {
294
+ const res = await post({ chain, path: '/v1/view', body: { function: fn, type_arguments: typeArguments, arguments: args }, params: withLedgerVersion(undefined, ledgerVersion) });
295
+ return (Array.isArray(res) ? res : [res]);
296
+ }
297
+ // ---------------------------------------------------------------------------
298
+ // events
299
+ // ---------------------------------------------------------------------------
300
+ /** Events of an event handle field, e.g. `{ eventHandle: '0x1::coin::CoinStore<...>', field: 'deposit_events' }`. */
301
+ async function getEvents({ chain, account, eventHandle, field, start, limit = EVENT_PAGE_SIZE }) {
302
+ const params = { limit };
303
+ if (start !== undefined)
304
+ params.start = String(start);
305
+ const res = await get({ chain, path: `/v1/accounts/${account}/events/${encodeSegment(eventHandle)}/${encodeSegment(field)}`, params });
306
+ return extractPage(res);
307
+ }
308
+ async function getEventsByCreationNumber({ chain, account, creationNumber, start, limit = EVENT_PAGE_SIZE }) {
309
+ const params = { limit };
310
+ if (start !== undefined)
311
+ params.start = String(start);
312
+ const res = await get({ chain, path: `/v1/accounts/${account}/events/${creationNumber}`, params });
313
+ return extractPage(res);
314
+ }
315
+ // ---------------------------------------------------------------------------
316
+ // blocks / time travel
317
+ // ---------------------------------------------------------------------------
318
+ function toBlock(raw) {
319
+ return {
320
+ number: Number(raw.block_height),
321
+ timestamp: microsToSeconds(raw.block_timestamp),
322
+ hash: raw.block_hash,
323
+ firstVersion: Number(raw.first_version),
324
+ lastVersion: Number(raw.last_version),
325
+ transactions: raw.transactions,
326
+ raw,
327
+ };
328
+ }
329
+ /**
330
+ * Block by height; `null` when it does not exist (yet). Heights pruned from the primary are
331
+ * fetched from the archival endpoint when one is configured, otherwise the 410 error propagates.
332
+ */
333
+ async function getBlockByHeight({ chain, height, withTransactions = false, archival }) {
334
+ const raw = await get({ chain, path: `/v1/blocks/by_height/${height}`, params: { with_transactions: withTransactions }, allowNotFound: true, archival });
335
+ return raw ? toBlock(raw) : null;
336
+ }
337
+ /** Block containing ledger version `version`; `null` when unknown. Pruned versions fall back to archival like `getBlockByHeight`. */
338
+ async function getBlockByVersion({ chain, version, withTransactions = false, archival }) {
339
+ const raw = await get({ chain, path: `/v1/blocks/by_version/${version}`, params: { with_transactions: withTransactions }, allowNotFound: true, archival });
340
+ return raw ? toBlock(raw) : null;
341
+ }
342
+ /** Latest block height + timestamp (seconds) from ledger info; `version` is the latest ledger version. */
343
+ async function getLatestBlock({ chain } = {}) {
344
+ const info = await getLedgerInfo({ chain });
345
+ return { number: info.blockHeight, timestamp: info.ledgerTimestamp, version: info.ledgerVersion };
346
+ }
347
+ /**
348
+ * Binary search over block heights (REST only) for the last block whose timestamp is at or
349
+ * before `timestamp` (unix seconds). Starts on the primary node; when the target predates the
350
+ * primary's oldest block the search moves to the archival endpoint (the public aptos fullnode
351
+ * keeps only a few weeks). Throws when `timestamp` predates all available history.
352
+ */
353
+ async function getBlockAtTimestamp({ chain, timestamp, minBlock }) {
354
+ chain = resolveChain(chain);
355
+ if (timestamp > 1e12)
356
+ timestamp = Math.floor(timestamp / 1000); // tolerate milliseconds
357
+ const hasArchival = getArchivalEndpointList({ chain }).length > 0;
358
+ let archival = false;
359
+ let info = await getLedgerInfo({ chain });
360
+ let lo = Math.max(minBlock !== null && minBlock !== void 0 ? minBlock : 0, info.oldestBlockHeight);
361
+ let hi = info.blockHeight;
362
+ const cache = new Map();
363
+ const fetchBlock = async (height) => {
364
+ if (!cache.has(height))
365
+ cache.set(height, await getBlockByHeight({ chain, height, archival }));
366
+ return cache.get(height);
367
+ };
368
+ const latest = await fetchBlock(hi);
369
+ if (latest && latest.timestamp <= timestamp)
370
+ return latest;
371
+ let first = await fetchBlock(lo);
372
+ if ((!first || first.timestamp > timestamp) && hasArchival) {
373
+ (0, debugLog_1.debugLog)(`[chains.aptos] ${chain} timestamp ${timestamp} predates primary history (oldest block ${lo}), searching archival`);
374
+ archival = true;
375
+ info = await getLedgerInfo({ chain, archival: true });
376
+ lo = Math.max(minBlock !== null && minBlock !== void 0 ? minBlock : 0, info.oldestBlockHeight);
377
+ first = await fetchBlock(lo);
378
+ }
379
+ if (!first)
380
+ throw new Error(`[${chain}] block ${lo} is not available on this node`);
381
+ if (first.timestamp > timestamp)
382
+ throw new Error(`[${chain}] timestamp ${timestamp} predates available history (oldest block ${lo} at ${first.timestamp})`);
383
+ // invariant: block(lo).timestamp <= timestamp < block(hi).timestamp
384
+ let steps = 0;
385
+ while (hi - lo > 1) {
386
+ const mid = Math.floor((lo + hi) / 2);
387
+ const block = await fetchBlock(mid);
388
+ steps++;
389
+ if (!block || block.timestamp <= timestamp)
390
+ lo = mid;
391
+ else
392
+ hi = mid;
393
+ }
394
+ (0, debugLog_1.debugLog)(`[chains.aptos] ${chain} getBlockAtTimestamp(${timestamp}) -> block ${lo} in ${steps} steps`);
395
+ const result = await fetchBlock(lo);
396
+ if (!result)
397
+ throw new Error(`[${chain}] block ${lo} is not available on this node`);
398
+ return result;
399
+ }
400
+ /**
401
+ * Last ledger version at or before `timestamp` (unix seconds). Every transaction in a block
402
+ * shares the block timestamp, so this is the `last_version` of the block found by `getBlockAtTimestamp`.
403
+ */
404
+ async function getVersionAtTimestamp({ chain, timestamp, minBlock }) {
405
+ if (timestamp > 1e12)
406
+ timestamp = Math.floor(timestamp / 1000);
407
+ const info = await getLedgerInfo({ chain });
408
+ if (info.ledgerTimestamp <= timestamp)
409
+ return info.ledgerVersion;
410
+ const block = await getBlockAtTimestamp({ chain, timestamp, minBlock });
411
+ return block.lastVersion;
412
+ }
413
+ // ---------------------------------------------------------------------------
414
+ // token helpers
415
+ // ---------------------------------------------------------------------------
416
+ function optionValue(value) {
417
+ // Move `Option<T>` is encoded as `{ vec: [T] }`
418
+ if (value && typeof value === 'object' && Array.isArray(value.vec))
419
+ return value.vec.length ? value.vec[0] : undefined;
420
+ return value;
421
+ }
422
+ function coinModuleAddress(coinType) {
423
+ return coinType.substring(0, coinType.indexOf('::'));
424
+ }
425
+ /**
426
+ * Raw balance of `account` for a coin type (`0x1::aptos_coin::AptosCoin`) or a fungible asset
427
+ * metadata address. Coins: `CoinStore<T>` resource, falling back to the `0x1::coin::balance` view
428
+ * (which also counts the paired FA store after a coin -> FA migration). FA: `0x1::primary_fungible_store::balance`.
429
+ */
430
+ async function getBalance({ chain, account, coinType = exports.APT_COIN_TYPE, ledgerVersion }) {
431
+ var _a, _b;
432
+ if (isFungibleAssetAddress(coinType)) {
433
+ const [balance] = await view({ chain, function: '0x1::primary_fungible_store::balance', typeArguments: ['0x1::fungible_asset::Metadata'], args: [account, coinType], ledgerVersion });
434
+ return String(balance !== null && balance !== void 0 ? balance : '0');
435
+ }
436
+ const store = await getResource({ chain, account, type: `0x1::coin::CoinStore<${coinType}>`, ledgerVersion });
437
+ if (((_a = store === null || store === void 0 ? void 0 : store.coin) === null || _a === void 0 ? void 0 : _a.value) !== undefined && ((_b = store === null || store === void 0 ? void 0 : store.coin) === null || _b === void 0 ? void 0 : _b.value) !== null)
438
+ return String(store.coin.value);
439
+ const [balance] = await view({ chain, function: '0x1::coin::balance', typeArguments: [coinType], args: [account], ledgerVersion });
440
+ return String(balance !== null && balance !== void 0 ? balance : '0');
441
+ }
442
+ /**
443
+ * Token metadata: `0x1::coin::CoinInfo<T>` for coin types, `0x1::fungible_asset::Metadata` for
444
+ * fungible asset addresses. Throws when the token is unknown.
445
+ */
446
+ async function getCoinInfo({ chain, coinType, ledgerVersion }) {
447
+ var _a;
448
+ chain = resolveChain(chain);
449
+ if (isFungibleAssetAddress(coinType)) {
450
+ const data = await getResource({ chain, account: coinType, type: '0x1::fungible_asset::Metadata', ledgerVersion });
451
+ if (!data)
452
+ throw new Error(`[${chain}] no fungible asset metadata at ${coinType}`);
453
+ return { decimals: Number(data.decimals), symbol: data.symbol, name: data.name };
454
+ }
455
+ const data = await getResource({ chain, account: coinModuleAddress(coinType), type: `0x1::coin::CoinInfo<${coinType}>`, ledgerVersion });
456
+ if (!data)
457
+ throw new Error(`[${chain}] no CoinInfo for ${coinType}`);
458
+ const info = { decimals: Number(data.decimals), symbol: data.symbol, name: data.name };
459
+ const integer = optionValue((_a = optionValue(data.supply)) === null || _a === void 0 ? void 0 : _a.integer);
460
+ if ((integer === null || integer === void 0 ? void 0 : integer.value) !== undefined)
461
+ info.supply = String(integer.value);
462
+ return info;
463
+ }
464
+ async function getFungibleAssetSupply({ chain, metadata, ledgerVersion }) {
465
+ var _a, _b;
466
+ // 1. view sums both supply variants and is a single request
467
+ try {
468
+ const [res] = await view({ chain, function: '0x1::fungible_asset::supply', typeArguments: ['0x1::fungible_asset::Metadata'], args: [metadata], ledgerVersion });
469
+ const value = optionValue(res);
470
+ if (value !== undefined && value !== null)
471
+ return String(value);
472
+ }
473
+ catch (e) {
474
+ (0, debugLog_1.debugLog)(`[chains.aptos] fungible_asset::supply view failed for ${metadata}, falling back to resources: ${e === null || e === void 0 ? void 0 : e.message}`);
475
+ }
476
+ // 2. ConcurrentSupply (newer) then Supply resource; a 404 is a real answer here
477
+ const concurrent = await getResource({ chain, account: metadata, type: '0x1::fungible_asset::ConcurrentSupply', ledgerVersion });
478
+ if (((_a = concurrent === null || concurrent === void 0 ? void 0 : concurrent.current) === null || _a === void 0 ? void 0 : _a.value) !== undefined && ((_b = concurrent === null || concurrent === void 0 ? void 0 : concurrent.current) === null || _b === void 0 ? void 0 : _b.value) !== null)
479
+ return String(concurrent.current.value);
480
+ const supply = await getResource({ chain, account: metadata, type: '0x1::fungible_asset::Supply', ledgerVersion });
481
+ if ((supply === null || supply === void 0 ? void 0 : supply.current) !== undefined && (supply === null || supply === void 0 ? void 0 : supply.current) !== null)
482
+ return String(supply.current);
483
+ return null;
484
+ }
485
+ /**
486
+ * Raw total supply of a coin type or fungible asset address. Resolution order (ported from the
487
+ * server l2 adapter):
488
+ * 1. `0x1::coin::supply<T>` view: sums the legacy CoinInfo supply and the paired FA supply, which
489
+ * matters for coins that migrated to the FA standard (CoinInfo alone undercounts).
490
+ * 2. `CoinInfo<T>.supply` integer value, or the aggregator table item it points at (native APT).
491
+ * 3. The paired FA metadata (`0x1::coin::paired_metadata<T>`), then the FA branch.
492
+ * 4. FA: `0x1::fungible_asset::supply` view -> `ConcurrentSupply` -> `Supply` resource.
493
+ * Throws when nothing resolves (e.g. coins initialised with `monitor_supply = false`).
494
+ */
495
+ async function getCoinSupply({ chain, coinType, ledgerVersion }) {
496
+ var _a;
497
+ chain = resolveChain(chain);
498
+ let metadata;
499
+ if (!isFungibleAssetAddress(coinType)) {
500
+ try {
501
+ const [res] = await view({ chain, function: '0x1::coin::supply', typeArguments: [coinType], ledgerVersion });
502
+ const value = optionValue(res);
503
+ if (value !== undefined && value !== null)
504
+ return String(value);
505
+ }
506
+ catch (e) {
507
+ (0, debugLog_1.debugLog)(`[chains.aptos] coin::supply view failed for ${coinType}, falling back to CoinInfo: ${e === null || e === void 0 ? void 0 : e.message}`);
508
+ }
509
+ const info = await getResource({ chain, account: coinModuleAddress(coinType), type: `0x1::coin::CoinInfo<${coinType}>`, ledgerVersion });
510
+ const supply = optionValue(info === null || info === void 0 ? void 0 : info.supply);
511
+ const integer = optionValue(supply === null || supply === void 0 ? void 0 : supply.integer);
512
+ if ((integer === null || integer === void 0 ? void 0 : integer.value) !== undefined && (integer === null || integer === void 0 ? void 0 : integer.value) !== null)
513
+ return String(integer.value);
514
+ const aggregator = optionValue(supply === null || supply === void 0 ? void 0 : supply.aggregator);
515
+ if (aggregator === null || aggregator === void 0 ? void 0 : aggregator.handle) {
516
+ const item = await getTableItem({ chain, handle: aggregator.handle, keyType: 'address', valueType: 'u128', key: aggregator.key, ledgerVersion, allowNotFound: true });
517
+ if (typeof item === 'string' || typeof item === 'number')
518
+ return String(item);
519
+ }
520
+ try {
521
+ const [paired] = await view({ chain, function: '0x1::coin::paired_metadata', typeArguments: [coinType], ledgerVersion });
522
+ metadata = (_a = optionValue(paired)) === null || _a === void 0 ? void 0 : _a.inner;
523
+ }
524
+ catch (e) {
525
+ (0, debugLog_1.debugLog)(`[chains.aptos] coin::paired_metadata view failed for ${coinType}: ${e === null || e === void 0 ? void 0 : e.message}`);
526
+ }
527
+ if (!metadata)
528
+ throw new Error(`[${chain}] could not resolve supply for ${coinType}`);
529
+ }
530
+ else {
531
+ metadata = coinType;
532
+ }
533
+ const faSupply = await getFungibleAssetSupply({ chain, metadata, ledgerVersion });
534
+ if (faSupply === null)
535
+ throw new Error(`[${chain}] could not resolve supply for ${coinType}`);
536
+ return faSupply;
537
+ }
538
+ // ---------------------------------------------------------------------------
539
+ // pure helpers
540
+ // ---------------------------------------------------------------------------
541
+ /** Decode a hex string (with or without `0x`) as UTF-8. */
542
+ function hexToString(hex) {
543
+ if (typeof hex !== 'string')
544
+ return '';
545
+ let clean = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
546
+ if (clean.length % 2)
547
+ clean = '0' + clean;
548
+ return Buffer.from(clean, 'hex').toString('utf8');
549
+ }
550
+ /** Octas (1e-8 APT) to APT as a JS number. */
551
+ function octasToApt(octas) {
552
+ return Number(octas) / Math.pow(10, exports.APT_DECIMALS);
553
+ }
554
+ /** Fungible asset tokens are identified by an object address (no `::`), coins by `addr::module::Name`. */
555
+ function isFungibleAssetAddress(type) {
556
+ return typeof type === 'string' && !type.includes('::');
557
+ }
558
+ /** `0x1` -> `0x0000...0001` (64 hex chars, lowercase). Throws on non-hex input. */
559
+ function normalizeAddress(address) {
560
+ if (typeof address !== 'string')
561
+ throw new Error(`Invalid aptos address: ${address}`);
562
+ let hex = address.trim();
563
+ if (hex.startsWith('0x') || hex.startsWith('0X'))
564
+ hex = hex.slice(2);
565
+ hex = hex.toLowerCase();
566
+ if (!hex.length || hex.length > 64 || !/^[0-9a-f]+$/.test(hex))
567
+ throw new Error(`Invalid aptos address: ${address}`);
568
+ return '0x' + hex.padStart(64, '0');
569
+ }
570
+ /** Depth-aware split of generic arguments: `A<B, C>, D` -> `['A<B, C>', 'D']`. */
571
+ function splitTypeArgs(inner) {
572
+ const parts = [];
573
+ let depth = 0;
574
+ let current = '';
575
+ for (const ch of inner) {
576
+ if (ch === '<')
577
+ depth++;
578
+ else if (ch === '>')
579
+ depth--;
580
+ if (ch === ',' && depth === 0) {
581
+ parts.push(current.trim());
582
+ current = '';
583
+ continue;
584
+ }
585
+ current += ch;
586
+ }
587
+ if (current.trim())
588
+ parts.push(current.trim());
589
+ return parts;
590
+ }
591
+ /**
592
+ * Parse `0x1::coin::CoinInfo<0x1::aptos_coin::AptosCoin>` into address / module / name / generics.
593
+ * Primitive and vector types (`u64`, `vector<u8>`) parse with an empty address and module.
594
+ */
595
+ function parseTypeTag(type) {
596
+ const raw = type.trim();
597
+ const open = raw.indexOf('<');
598
+ let head = raw;
599
+ let typeArgs = [];
600
+ if (open !== -1) {
601
+ if (!raw.endsWith('>'))
602
+ throw new Error(`Invalid type tag: ${type}`);
603
+ head = raw.slice(0, open).trim();
604
+ typeArgs = splitTypeArgs(raw.slice(open + 1, -1));
605
+ }
606
+ const parts = head.split('::');
607
+ if (parts.length === 1)
608
+ return { raw, address: '', module: '', name: parts[0], typeArgs, isStruct: false };
609
+ if (parts.length !== 3 || parts.some(p => !p))
610
+ throw new Error(`Invalid type tag: ${type}`);
611
+ return { raw, address: parts[0], module: parts[1], name: parts[2], typeArgs, isStruct: true };
612
+ }