@defillama/sdk 5.0.234 → 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/ChainApi.js +1 -0
- package/build/abi/multicall3.js +4 -0
- 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 +16 -1
- 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 -285
- 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/logs.js +1 -2
- 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 +40 -17
- package/package.json +20 -7
- package/README.md +0 -41
|
@@ -0,0 +1,618 @@
|
|
|
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.tendermint = exports.isCosmosChain = exports.BECH32_PREFIXES = exports.ibcChains = exports.EVM_COSMOS_CHAINS = exports.highGasLimitEndpoints = exports.chainSubpaths = exports.DEFAULT_ENDPOINTS = void 0;
|
|
7
|
+
exports.getEndpoints = getEndpoints;
|
|
8
|
+
exports.getEndpoint = getEndpoint;
|
|
9
|
+
exports.encodeQuery = encodeQuery;
|
|
10
|
+
exports.isContractAddress = isContractAddress;
|
|
11
|
+
exports.parseBlockTime = parseBlockTime;
|
|
12
|
+
exports.isPrunedHeightError = isPrunedHeightError;
|
|
13
|
+
exports.query = query;
|
|
14
|
+
exports.queryV1Beta1 = queryV1Beta1;
|
|
15
|
+
exports.paginate = paginate;
|
|
16
|
+
exports.queryV1Beta1All = queryV1Beta1All;
|
|
17
|
+
exports.getBalances = getBalances;
|
|
18
|
+
exports.getDenomBalance = getDenomBalance;
|
|
19
|
+
exports.getBalance = getBalance;
|
|
20
|
+
exports.getTokenBalance = getTokenBalance;
|
|
21
|
+
exports.totalSupply = totalSupply;
|
|
22
|
+
exports.getSupplyAll = getSupplyAll;
|
|
23
|
+
exports.queryContract = queryContract;
|
|
24
|
+
exports.queryContractWithRetries = queryContractWithRetries;
|
|
25
|
+
exports.queryManyContracts = queryManyContracts;
|
|
26
|
+
exports.queryContracts = queryContracts;
|
|
27
|
+
exports.queryContractStore = queryContractStore;
|
|
28
|
+
exports.getContractInfo = getContractInfo;
|
|
29
|
+
exports.getTokenInfo = getTokenInfo;
|
|
30
|
+
exports.getLpMinter = getLpMinter;
|
|
31
|
+
exports.getBlock = getBlock;
|
|
32
|
+
exports.getLatestBlock = getLatestBlock;
|
|
33
|
+
exports.getBlockTime = getBlockTime;
|
|
34
|
+
exports.getBlockAtTimestamp = getBlockAtTimestamp;
|
|
35
|
+
exports.getCosmosBlock = getCosmosBlock;
|
|
36
|
+
exports.getCosmosProvider = getCosmosProvider;
|
|
37
|
+
/**
|
|
38
|
+
* Cosmos-SDK LCD/REST client for every IBC chain (cosmos hub, osmosis, terra,
|
|
39
|
+
* neutron, injective, sei, kava, ...). Raw values only: no token maps, no
|
|
40
|
+
* balance sheets, no TVL coupling.
|
|
41
|
+
*
|
|
42
|
+
* Replaces / consolidates:
|
|
43
|
+
* - sdk/src/util/cosmos.ts (endpoint map, isCosmosChain, getCosmosBlock,
|
|
44
|
+
* getCosmosProvider - re-exported from there for backwards compat)
|
|
45
|
+
* - DefiLlama-Adapters/projects/helper/chain/cosmos.js (endPoints, chainSubpaths,
|
|
46
|
+
* highGasLimitEndpoints, multipleEndpoints, getEndpoint, query, queryV1Beta1,
|
|
47
|
+
* queryV1Beta1V2, getTokenBalance, getBalance, getDenomBalance, getBalance2,
|
|
48
|
+
* totalSupply, lpMinter, queryContract, queryContractWithRetries,
|
|
49
|
+
* queryManyContracts, queryContracts, queryContractStore)
|
|
50
|
+
* - peggedassets-server/src/adapters/peggedAssets/helper/getSupply.ts
|
|
51
|
+
* (cosmosEndpoints, cosmosSupply / osmosisSupply -> totalSupply)
|
|
52
|
+
* - server/defi/l2/utils.ts (provenance block time lookups,
|
|
53
|
+
* getProvenanceHeightForTimestamp -> getBlock / getBlockAtTimestamp,
|
|
54
|
+
* x-cosmos-block-height header usage)
|
|
55
|
+
* - server/coins/src/getBlock.ts (cosmosBlockProvider -> getCosmosProvider)
|
|
56
|
+
* - dimension-adapters/helpers/cosmosChainFees.ts (tendermint `status` / `block`
|
|
57
|
+
* JSON-RPC shape -> `tendermint` sub-API)
|
|
58
|
+
*
|
|
59
|
+
* Endpoint resolution (LCD / REST), first non-empty wins, comma separated values
|
|
60
|
+
* are a fallback list rotated on failure:
|
|
61
|
+
* 1. env `<CHAIN>_LCD` (also `SDK_<CHAIN>_LCD` / `LLAMA_SDK_<CHAIN>_LCD`)
|
|
62
|
+
* 2. env `<CHAIN>_RPC` - ONLY when the chain is not an EVM chain. Chains such as
|
|
63
|
+
* kava, cronos, evmos, sei or injective use `<CHAIN>_RPC` for their EVM
|
|
64
|
+
* JSON-RPC, so it is never treated as an LCD url for them (backwards compat
|
|
65
|
+
* with the old util/cosmos.ts which honoured `<CHAIN>_RPC` for everything).
|
|
66
|
+
* 3. `DEFAULT_ENDPOINTS[chain]`
|
|
67
|
+
* 4. `https://rest.cosmos.directory/<chain>`
|
|
68
|
+
*
|
|
69
|
+
* Tendermint JSON-RPC (`tendermint.*`): env `<CHAIN>_TENDERMINT_RPC`, then
|
|
70
|
+
* `https://rpc.cosmos.directory/<chain>`.
|
|
71
|
+
*
|
|
72
|
+
* Historical queries pass the height through the `x-cosmos-block-height` header
|
|
73
|
+
* (the legacy `?height=` query param is not used anymore).
|
|
74
|
+
*/
|
|
75
|
+
const p_limit_1 = __importDefault(require("p-limit"));
|
|
76
|
+
const env_1 = require("../util/env");
|
|
77
|
+
const LlamaProvider_1 = require("../util/LlamaProvider");
|
|
78
|
+
const debugLog_1 = require("../util/debugLog");
|
|
79
|
+
const rpc_1 = require("./rpc");
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// config
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// where to find chain info
|
|
84
|
+
// https://wiki.f5nodes.com/quicksilver/endpoints/
|
|
85
|
+
// https://proxy.atomscan.com/chains.json
|
|
86
|
+
// https://cosmos-chain.directory/chains
|
|
87
|
+
// https://celestia.publicnode.com/
|
|
88
|
+
// https://api.axelarscan.io/api/getTVL
|
|
89
|
+
/** Default LCD endpoints. Comma separated values are fallbacks, tried in order. */
|
|
90
|
+
exports.DEFAULT_ENDPOINTS = {
|
|
91
|
+
crescent: "https://rest.cosmos.directory/crescent,https://mainnet.crescent.network:1317",
|
|
92
|
+
osmosis: "https://rest-osmosis.ecostake.com,https://osmosis-api.polkachu.com,https://rest.cosmos.directory/osmosis",
|
|
93
|
+
cosmos: "https://cosmos-api.polkachu.com,https://cosmoshub-lcd.stakely.io,https://rest.cosmos.directory/cosmoshub",
|
|
94
|
+
kujira: "https://rest.cosmos.directory/kujira,https://kuji-api.kleomedes.network",
|
|
95
|
+
comdex: "https://rest.cosmos.directory/comdex,https://rest.comdex.one",
|
|
96
|
+
terra: "https://terra-classic-lcd.publicnode.com",
|
|
97
|
+
terra2: "https://terra-lcd.publicnode.com",
|
|
98
|
+
umee: "https://umee-api.polkachu.com",
|
|
99
|
+
orai: "https://lcd.orai.io",
|
|
100
|
+
juno: "https://juno.api.m.stavr.tech",
|
|
101
|
+
cronos: "https://rest.mainnet.crypto.org",
|
|
102
|
+
chihuahua: "https://rest.cosmos.directory/chihuahua",
|
|
103
|
+
stargaze: "https://rest.stargaze-apis.com",
|
|
104
|
+
quicksilver: "https://quicksilver.api.m.anode.team,https://rest.cosmos.directory/quicksilver",
|
|
105
|
+
persistence: "https://rest.cosmos.directory/persistence",
|
|
106
|
+
secret: "https://lcd-secret.keplr.app,https://lcd.secret.express",
|
|
107
|
+
injective: "https://injective-rest.publicnode.com,https://sentry.lcd.injective.network:443",
|
|
108
|
+
migaloo: "https://migaloo-api.polkachu.com",
|
|
109
|
+
fxcore: "https://fx-rest.functionx.io",
|
|
110
|
+
xpla: "https://dimension-lcd.xpla.dev",
|
|
111
|
+
kava: "https://api2.kava.io",
|
|
112
|
+
akash: "https://rest.cosmos.directory/akash",
|
|
113
|
+
neutron: "https://neutron-rest.publicnode.com,https://rest-kralum.neutron-1.neutron.org",
|
|
114
|
+
quasar: "https://quasar-api.polkachu.com",
|
|
115
|
+
gravitybridge: "https://gravity-api.polkachu.com,https://gravitychain.io:1317",
|
|
116
|
+
sei: "https://rest.sei-apis.com,https://sei-api.polkachu.com,https://sei-rest.publicnode.com,https://sei-rest.brocha.in,https://sei-m.api.n0ok.net,https://sei-api.lavenderfive.com,https://api-sei.stingray.plus",
|
|
117
|
+
aura: "https://rest.cosmos.directory/aura,https://lcd.aura.network",
|
|
118
|
+
archway: "https://api.mainnet.archway.io",
|
|
119
|
+
sifchain: "https://sifchain-api.polkachu.com",
|
|
120
|
+
nolus: "https://lcd.nolus.network,https://pirin-cl.nolus.network:1317",
|
|
121
|
+
nibiru: "https://lcd.nibiru.fi",
|
|
122
|
+
bostrom: "https://lcd.bostrom.cybernode.ai",
|
|
123
|
+
joltify: "https://lcd.joltify.io",
|
|
124
|
+
milkyway: "https://lcd.mainnet.milkyway.zone:443",
|
|
125
|
+
kopi: "https://rest.kopi.money",
|
|
126
|
+
noble: "https://noble-api.polkachu.com",
|
|
127
|
+
mantra: "https://api.mantrachain.io",
|
|
128
|
+
elys: "https://api.elys.network", // https://api.elys.network/#/Query/ElysAmmPoolAll
|
|
129
|
+
pryzm: "https://api.pryzm.zone",
|
|
130
|
+
agoric: "https://agoric-api.polkachu.com",
|
|
131
|
+
allora: "https://allora-api.polkachu.com",
|
|
132
|
+
band: "https://laozi1.bandchain.org/api",
|
|
133
|
+
celestia: "https://celestia-rest.publicnode.com",
|
|
134
|
+
dydx: "https://dydx-rest.publicnode.com",
|
|
135
|
+
dungeon: "https://api.dungeongames.io",
|
|
136
|
+
carbon: "https://api.carbon.network",
|
|
137
|
+
evmos: "https://evmos-api.polkachu.com",
|
|
138
|
+
fetchhub: "https://rest-fetchhub.fetch.ai",
|
|
139
|
+
regen: "https://rest-regen.ecostake.com",
|
|
140
|
+
sommelier: "https://rest.cosmos.directory/sommelier",
|
|
141
|
+
stride: "https://stride-api.polkachu.com",
|
|
142
|
+
babylon: "https://babylon-api.polkachu.com",
|
|
143
|
+
milkyway_rollup: "https://archival-rest-moo-1.anvil.asia-southeast.initia.xyz",
|
|
144
|
+
titan: "https://titan-lcd.titanlab.io",
|
|
145
|
+
provenance: "https://api.provenance.io",
|
|
146
|
+
xion: "https://api.xion-mainnet-1.burnt.com",
|
|
147
|
+
embr: "https://rest-embrmainnet-1.anvil.asia-southeast.initia.xyz",
|
|
148
|
+
civitia: "https://rest-civitia-1.anvil.asia-southeast.initia.xyz",
|
|
149
|
+
echelon_initia: "https://rest-echelon-1.anvil.asia-southeast.initia.xyz",
|
|
150
|
+
inertia: "https://rest.inrt.fi",
|
|
151
|
+
union: "https://rest.union.build",
|
|
152
|
+
zigchain: "https://public-zigchain-lcd.numia.xyz",
|
|
153
|
+
axiome: "http://api-docs.axiomeinfo.org:1317",
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Module-specific REST prefix used by `queryV1Beta1` (e.g. `/osmosis/gamm/...`,
|
|
157
|
+
* `/kava/hard/...`). Standard sdk modules (bank, staking, ...) always live under
|
|
158
|
+
* `/cosmos/`, use `query` / `getBalances` / `totalSupply` for those.
|
|
159
|
+
*/
|
|
160
|
+
exports.chainSubpaths = {
|
|
161
|
+
crescent: "crescent",
|
|
162
|
+
osmosis: "osmosis",
|
|
163
|
+
provenance: "provenance",
|
|
164
|
+
comdex: "comdex",
|
|
165
|
+
umee: "umee",
|
|
166
|
+
kava: "kava",
|
|
167
|
+
joltify: "joltify",
|
|
168
|
+
};
|
|
169
|
+
/** Some contract calls need an endpoint with a higher gas limit. contract address -> endpoint */
|
|
170
|
+
exports.highGasLimitEndpoints = {
|
|
171
|
+
// 'sei1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3shh3qfl': "https://rest.sei-apis.com",
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Cosmos chains that also expose an EVM JSON-RPC. For these `<CHAIN>_RPC` is the
|
|
175
|
+
* EVM url and is never used as LCD, and `isCosmosChain` stays false so block
|
|
176
|
+
* lookups keep going through the EVM provider (same as before this module).
|
|
177
|
+
*/
|
|
178
|
+
exports.EVM_COSMOS_CHAINS = ['kava', 'cronos', 'evmos', 'sei', 'aura', 'nibiru', 'dungeon', 'titan'];
|
|
179
|
+
/**
|
|
180
|
+
* Chains always treated as cosmos chains by `isCosmosChain` (legacy list from
|
|
181
|
+
* util/cosmos.ts plus the pure cosmos chains of the adapters map).
|
|
182
|
+
*/
|
|
183
|
+
exports.ibcChains = [
|
|
184
|
+
'terra', 'terra2', 'crescent', 'osmosis', 'kujira', 'stargaze', 'juno', 'injective', 'cosmos', 'comdex', 'umee', 'orai',
|
|
185
|
+
'persistence', 'fxcore', 'neutron', 'quasar', 'chihuahua', 'archway', 'migaloo', 'secret', 'xpla', 'bostrom',
|
|
186
|
+
'akash', 'noble', 'celestia', 'dydx', 'stride', 'mantra', 'elys', 'pryzm', 'agoric', 'band', 'carbon', 'regen', 'sommelier', 'babylon', 'xion', 'union', 'zigchain', 'provenance', 'nolus', 'joltify', 'milkyway', 'quicksilver', 'gravitybridge', 'sifchain', 'kopi', 'allora', 'fetchhub',
|
|
187
|
+
];
|
|
188
|
+
/** bech32 human readable prefix per chain (falls back to the chain name) */
|
|
189
|
+
exports.BECH32_PREFIXES = {
|
|
190
|
+
cosmos: 'cosmos', osmosis: 'osmo', terra: 'terra', terra2: 'terra', injective: 'inj', secret: 'secret', neutron: 'neutron',
|
|
191
|
+
kujira: 'kujira', sei: 'sei', stargaze: 'stars', juno: 'juno', archway: 'archway', migaloo: 'migaloo', xpla: 'xpla', orai: 'orai',
|
|
192
|
+
persistence: 'persistence', chihuahua: 'chihuahua', comdex: 'comdex', umee: 'umee', crescent: 'cre', quasar: 'quasar', nolus: 'nolus',
|
|
193
|
+
bostrom: 'bostrom', fxcore: 'fx', kava: 'kava', cronos: 'cro', evmos: 'evmos', akash: 'akash', gravitybridge: 'gravity', sifchain: 'sif',
|
|
194
|
+
nibiru: 'nibi', joltify: 'jolt', milkyway: 'milk', noble: 'noble', mantra: 'mantra', elys: 'elys', pryzm: 'pryzm', agoric: 'agoric',
|
|
195
|
+
band: 'band', celestia: 'celestia', dydx: 'dydx', carbon: 'swth', fetchhub: 'fetch', regen: 'regen', sommelier: 'somm', stride: 'stride',
|
|
196
|
+
babylon: 'bbn', provenance: 'pb', xion: 'xion', quicksilver: 'quick', union: 'union', zigchain: 'zig', aura: 'aura', kopi: 'kopi',
|
|
197
|
+
allora: 'allo', dungeon: 'dungeon', titan: 'titan', axiome: 'axm', inertia: 'init', embr: 'init', civitia: 'init', echelon_initia: 'init', milkyway_rollup: 'init',
|
|
198
|
+
};
|
|
199
|
+
const COSMOS_DIRECTORY_REST = 'https://rest.cosmos.directory';
|
|
200
|
+
const COSMOS_DIRECTORY_RPC = 'https://rpc.cosmos.directory';
|
|
201
|
+
const MAX_PAGES = 1000;
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// endpoints
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
function isEvmLikeChain(chain) {
|
|
206
|
+
return exports.EVM_COSMOS_CHAINS.includes(chain) || (0, LlamaProvider_1.isEvmChain)(chain);
|
|
207
|
+
}
|
|
208
|
+
const isCosmosChain = (chain) => exports.ibcChains.includes(chain) || (!!exports.DEFAULT_ENDPOINTS[chain] && !isEvmLikeChain(chain));
|
|
209
|
+
exports.isCosmosChain = isCosmosChain;
|
|
210
|
+
/** Resolve the LCD endpoint list for a chain, see the module header for the order. */
|
|
211
|
+
function getEndpoints({ chain }) {
|
|
212
|
+
if (!chain)
|
|
213
|
+
throw new Error('cosmos.getEndpoints: chain is required');
|
|
214
|
+
const envKey = `${chain.toUpperCase()}_LCD`;
|
|
215
|
+
const fromLcdEnv = (0, rpc_1.toEndpointList)((0, env_1.getEnvValue)(envKey));
|
|
216
|
+
if (fromLcdEnv.length)
|
|
217
|
+
return fromLcdEnv;
|
|
218
|
+
if (!isEvmLikeChain(chain)) {
|
|
219
|
+
const fromRpcEnv = (0, rpc_1.toEndpointList)((0, env_1.getEnvRPC)(chain));
|
|
220
|
+
if (fromRpcEnv.length)
|
|
221
|
+
return fromRpcEnv;
|
|
222
|
+
}
|
|
223
|
+
return (0, rpc_1.getEndpoints)(chain, exports.DEFAULT_ENDPOINTS[chain], { envKey, fallback: `${COSMOS_DIRECTORY_REST}/${chain}` });
|
|
224
|
+
}
|
|
225
|
+
/** First LCD endpoint. `contract` switches to a high gas limit endpoint when one is configured for it. */
|
|
226
|
+
function getEndpoint({ chain, contract }) {
|
|
227
|
+
if (contract && exports.highGasLimitEndpoints[contract])
|
|
228
|
+
return exports.highGasLimitEndpoints[contract];
|
|
229
|
+
return getEndpoints({ chain })[0];
|
|
230
|
+
}
|
|
231
|
+
function resolveQueryEndpoints({ chain, contract, endpoint }) {
|
|
232
|
+
const explicit = (0, rpc_1.toEndpointList)(endpoint);
|
|
233
|
+
if (explicit.length)
|
|
234
|
+
return explicit;
|
|
235
|
+
if (contract && exports.highGasLimitEndpoints[contract])
|
|
236
|
+
return [exports.highGasLimitEndpoints[contract]];
|
|
237
|
+
return getEndpoints({ chain });
|
|
238
|
+
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// helpers
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
/** base64 encode a cosmwasm query message (objects are JSON.stringified first) */
|
|
243
|
+
function encodeQuery(data) {
|
|
244
|
+
const str = typeof data === 'string' ? data : JSON.stringify(data);
|
|
245
|
+
return Buffer.from(str).toString('base64');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Heuristic: cosmwasm contract addresses are 32 byte bech32 strings (~63 chars)
|
|
249
|
+
* while accounts are 20 bytes (~43 chars).
|
|
250
|
+
*/
|
|
251
|
+
function isContractAddress(chain, address) {
|
|
252
|
+
var _a;
|
|
253
|
+
if (typeof address !== 'string' || address.length <= 50)
|
|
254
|
+
return false;
|
|
255
|
+
const prefix = (_a = exports.BECH32_PREFIXES[chain]) !== null && _a !== void 0 ? _a : chain;
|
|
256
|
+
const re = new RegExp(`^${prefix}1[02-9ac-hj-np-z]{38,}$`);
|
|
257
|
+
return re.test(address);
|
|
258
|
+
}
|
|
259
|
+
function blockHeaders(block) {
|
|
260
|
+
if (block === undefined || block === null || block === 'latest')
|
|
261
|
+
return undefined;
|
|
262
|
+
return { 'x-cosmos-block-height': String(block) };
|
|
263
|
+
}
|
|
264
|
+
function withQuery(path, params = {}) {
|
|
265
|
+
const entries = Object.entries(params).filter(([, v]) => v !== undefined && v !== null);
|
|
266
|
+
if (!entries.length)
|
|
267
|
+
return path;
|
|
268
|
+
const qs = entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join('&');
|
|
269
|
+
return path + (path.includes('?') ? '&' : '?') + qs;
|
|
270
|
+
}
|
|
271
|
+
/** Cosmos block times carry nanosecond precision; trim to millis before parsing. */
|
|
272
|
+
function parseBlockTime(iso) {
|
|
273
|
+
return Math.floor(Date.parse(String(iso).replace(/(\.\d{3})\d+/, '$1')) / 1000);
|
|
274
|
+
}
|
|
275
|
+
function errorMessage(e) {
|
|
276
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
277
|
+
return String((_h = (_g = (_f = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : (_e = (_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.error) !== null && _f !== void 0 ? _f : e === null || e === void 0 ? void 0 : e.message) !== null && _g !== void 0 ? _g : e) !== null && _h !== void 0 ? _h : '').toLowerCase();
|
|
278
|
+
}
|
|
279
|
+
function isNotFoundError(e) {
|
|
280
|
+
var _a, _b, _c;
|
|
281
|
+
const status = (_b = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : e === null || e === void 0 ? void 0 : e.status;
|
|
282
|
+
return status === 404 || /\[404\]/.test(String((_c = e === null || e === void 0 ? void 0 : e.message) !== null && _c !== void 0 ? _c : ''));
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Explicit "this height is pruned / does not exist" answers from a node. A bare 404
|
|
286
|
+
* is NOT enough: LCDs also answer 404 for routes they do not serve (older chains
|
|
287
|
+
* without `/cosmos/base/tendermint/v1beta1`), and that case must fall back to the
|
|
288
|
+
* legacy `/blocks/{h}` route instead of being reported as a pruned block.
|
|
289
|
+
*/
|
|
290
|
+
function isPrunedHeightError(e) {
|
|
291
|
+
if (e === null || e === void 0 ? void 0 : e.cosmosBlockMissing)
|
|
292
|
+
return true;
|
|
293
|
+
const message = errorMessage(e);
|
|
294
|
+
return /not available|lowest height|pruned|height \d+ .*(is not|must be|greater|exceed)|must be less than or equal to the current blockchain height|is greater than the current|invalid height|block height .*out of range|could not find results for height/.test(message);
|
|
295
|
+
}
|
|
296
|
+
/** GET `<lcd>/<path>`; rotates over the chain endpoints on failure. Returns the raw JSON body. */
|
|
297
|
+
async function query({ chain, path, block, params, endpoint, contract, timeout, retries }) {
|
|
298
|
+
const endpoints = resolveQueryEndpoints({ chain, contract, endpoint });
|
|
299
|
+
const fullPath = withQuery(path, params);
|
|
300
|
+
const headers = blockHeaders(block);
|
|
301
|
+
const options = { path: fullPath, label: `${chain} GET ${path.split('?')[0].slice(0, 80)}` };
|
|
302
|
+
if (headers)
|
|
303
|
+
options.headers = headers;
|
|
304
|
+
if (timeout !== undefined)
|
|
305
|
+
options.timeout = timeout;
|
|
306
|
+
if (retries !== undefined)
|
|
307
|
+
options.retries = retries;
|
|
308
|
+
return (0, rpc_1.httpGet)(endpoints, options);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* GET `<lcd>/<subpath>/<url>` where subpath is `chainSubpaths[chain]` or `cosmos`
|
|
312
|
+
* (module specific queries such as `gamm/v1beta1/pools` on osmosis).
|
|
313
|
+
*/
|
|
314
|
+
async function queryV1Beta1({ chain, url, block, paginationKey, subpath, endpoint }) {
|
|
315
|
+
var _a;
|
|
316
|
+
const prefix = (_a = subpath !== null && subpath !== void 0 ? subpath : exports.chainSubpaths[chain]) !== null && _a !== void 0 ? _a : 'cosmos';
|
|
317
|
+
const path = withQuery(`${prefix}/${url.replace(/^\/+/, '')}`, paginationKey ? { 'pagination.key': paginationKey } : {});
|
|
318
|
+
return query({ chain, path, block, endpoint });
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Follow `pagination.next_key` over `path` and return the concatenated `dataKey`
|
|
322
|
+
* items. When `dataKey` is omitted the first array field of the response is used.
|
|
323
|
+
*/
|
|
324
|
+
async function paginate({ chain, path, dataKey, limit = 100, block, endpoint }) {
|
|
325
|
+
var _a, _b, _c, _d;
|
|
326
|
+
const pagedPath = withQuery(path, { 'pagination.limit': limit });
|
|
327
|
+
let res = await query({ chain, path: pagedPath, block, endpoint });
|
|
328
|
+
if (!dataKey) {
|
|
329
|
+
dataKey = Object.keys(res !== null && res !== void 0 ? res : {}).find(k => k !== 'pagination' && Array.isArray(res[k]));
|
|
330
|
+
if (!dataKey)
|
|
331
|
+
throw new Error(`cosmos.paginate: no array field in response. Keys: ${Object.keys(res !== null && res !== void 0 ? res : {}).join(', ')}`);
|
|
332
|
+
}
|
|
333
|
+
let items = (_a = res[dataKey]) !== null && _a !== void 0 ? _a : [];
|
|
334
|
+
let nextKey = (_b = res.pagination) === null || _b === void 0 ? void 0 : _b.next_key;
|
|
335
|
+
let pages = 1;
|
|
336
|
+
while (nextKey) {
|
|
337
|
+
if (++pages > MAX_PAGES)
|
|
338
|
+
throw new Error(`cosmos.paginate: pagination exceeded ${MAX_PAGES} pages`);
|
|
339
|
+
res = await query({ chain, path: withQuery(pagedPath, { 'pagination.key': nextKey }), block, endpoint });
|
|
340
|
+
items = items.concat((_c = res[dataKey]) !== null && _c !== void 0 ? _c : []);
|
|
341
|
+
nextKey = (_d = res.pagination) === null || _d === void 0 ? void 0 : _d.next_key;
|
|
342
|
+
}
|
|
343
|
+
return items;
|
|
344
|
+
}
|
|
345
|
+
/** Auto-paginating `queryV1Beta1`; returns the concatenated `dataKey` items. */
|
|
346
|
+
async function queryV1Beta1All({ chain, url, dataKey, limit = 100, block, subpath, endpoint }) {
|
|
347
|
+
var _a;
|
|
348
|
+
const prefix = (_a = subpath !== null && subpath !== void 0 ? subpath : exports.chainSubpaths[chain]) !== null && _a !== void 0 ? _a : 'cosmos';
|
|
349
|
+
return paginate({ chain, path: `${prefix}/${url.replace(/^\/+/, '')}`, dataKey, limit, block, endpoint });
|
|
350
|
+
}
|
|
351
|
+
/** All native balances of `owner` (every page). */
|
|
352
|
+
async function getBalances({ chain, owner, block }) {
|
|
353
|
+
return paginate({ chain, path: `cosmos/bank/v1beta1/balances/${owner}`, dataKey: 'balances', limit: 1000, block });
|
|
354
|
+
}
|
|
355
|
+
/** Native balance of one denom, `"0"` when the account does not hold it. */
|
|
356
|
+
async function getDenomBalance({ chain, denom, owner, block }) {
|
|
357
|
+
var _a, _b;
|
|
358
|
+
try {
|
|
359
|
+
const res = await query({ chain, path: `cosmos/bank/v1beta1/balances/${owner}/by_denom`, params: { denom }, block, retries: 1 });
|
|
360
|
+
if (((_a = res === null || res === void 0 ? void 0 : res.balance) === null || _a === void 0 ? void 0 : _a.amount) !== undefined)
|
|
361
|
+
return String(res.balance.amount);
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
(0, debugLog_1.debugLog)(`[chains.cosmos] ${chain} by_denom failed for ${owner}, falling back to full balance list: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
365
|
+
}
|
|
366
|
+
const balances = await getBalances({ chain, owner, block });
|
|
367
|
+
const found = balances.find(i => i.denom === denom);
|
|
368
|
+
return found ? String(found.amount) : '0';
|
|
369
|
+
}
|
|
370
|
+
/** Balance of a cw20 contract (`token` looks like a contract address) or a native denom. */
|
|
371
|
+
async function getBalance({ chain, token, owner, block }) {
|
|
372
|
+
var _a;
|
|
373
|
+
if (isContractAddress(chain, token)) {
|
|
374
|
+
const data = await queryContract({ chain, contract: token, data: { balance: { address: owner } }, block });
|
|
375
|
+
return String((_a = data === null || data === void 0 ? void 0 : data.balance) !== null && _a !== void 0 ? _a : '0');
|
|
376
|
+
}
|
|
377
|
+
return getDenomBalance({ chain, denom: token, owner, block });
|
|
378
|
+
}
|
|
379
|
+
/** Balance for an astroport style asset info (`{ native_token: { denom } }` / `{ token: { contract_addr } }`) or a plain string. */
|
|
380
|
+
async function getTokenBalance({ chain, token, owner, block }) {
|
|
381
|
+
var _a, _b, _c;
|
|
382
|
+
const denomOrContract = typeof token === 'string' ? token : ((_b = (_a = token === null || token === void 0 ? void 0 : token.native_token) === null || _a === void 0 ? void 0 : _a.denom) !== null && _b !== void 0 ? _b : (_c = token === null || token === void 0 ? void 0 : token.token) === null || _c === void 0 ? void 0 : _c.contract_addr);
|
|
383
|
+
if (!denomOrContract)
|
|
384
|
+
throw new Error(`cosmos.getTokenBalance: unrecognised token ${JSON.stringify(token)}`);
|
|
385
|
+
return getBalance({ chain, token: denomOrContract, owner, block });
|
|
386
|
+
}
|
|
387
|
+
/** Total supply of a native denom via `bank/v1beta1/supply/by_denom`. */
|
|
388
|
+
async function totalSupply({ chain, denom, block }) {
|
|
389
|
+
var _a;
|
|
390
|
+
const res = await query({ chain, path: 'cosmos/bank/v1beta1/supply/by_denom', params: { denom }, block });
|
|
391
|
+
const amount = (_a = res === null || res === void 0 ? void 0 : res.amount) === null || _a === void 0 ? void 0 : _a.amount;
|
|
392
|
+
if (amount === undefined)
|
|
393
|
+
throw new Error(`cosmos.totalSupply: no amount in response for ${chain} ${denom}`);
|
|
394
|
+
return String(amount);
|
|
395
|
+
}
|
|
396
|
+
/** Total supply of every denom (all pages). */
|
|
397
|
+
async function getSupplyAll({ chain, block }) {
|
|
398
|
+
return paginate({ chain, path: 'cosmos/bank/v1beta1/supply', dataKey: 'supply', limit: 1000, block });
|
|
399
|
+
}
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
// cosmwasm
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
/** cosmwasm smart query; `data` is the query message (object or JSON string). Returns the `data` field. */
|
|
404
|
+
async function queryContract({ chain, contract, data, block, endpoint }) {
|
|
405
|
+
const res = await query({ chain, contract, endpoint, block, path: `cosmwasm/wasm/v1/contract/${contract}/smart/${encodeQuery(data)}` });
|
|
406
|
+
return res === null || res === void 0 ? void 0 : res.data;
|
|
407
|
+
}
|
|
408
|
+
/** Same as `queryContract`; every configured endpoint is tried before giving up. */
|
|
409
|
+
async function queryContractWithRetries({ chain, contract, data, block }) {
|
|
410
|
+
var _a;
|
|
411
|
+
const endpoints = getEndpoints({ chain });
|
|
412
|
+
let lastError;
|
|
413
|
+
for (const endpoint of endpoints) {
|
|
414
|
+
try {
|
|
415
|
+
return await queryContract({ chain, contract, data, block, endpoint });
|
|
416
|
+
}
|
|
417
|
+
catch (e) {
|
|
418
|
+
lastError = e;
|
|
419
|
+
(0, debugLog_1.debugLog)(`[chains.cosmos] ${chain} queryContract ${contract} failed on ${endpoint}: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
throw lastError;
|
|
423
|
+
}
|
|
424
|
+
/** Run the same smart query against many contracts. Results keep the input order; failures resolve to `undefined` when `permitFailure` is set. */
|
|
425
|
+
async function queryManyContracts({ chain, contracts, data, block, permitFailure = false, concurrency = 25 }) {
|
|
426
|
+
const limit = (0, p_limit_1.default)(Math.max(1, concurrency));
|
|
427
|
+
return Promise.all(contracts.map(contract => limit(async () => {
|
|
428
|
+
try {
|
|
429
|
+
return await queryContract({ chain, contract, data, block });
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
if (permitFailure)
|
|
433
|
+
return undefined;
|
|
434
|
+
throw e;
|
|
435
|
+
}
|
|
436
|
+
})));
|
|
437
|
+
}
|
|
438
|
+
/** All contract addresses instantiated from `codeId` (all pages). */
|
|
439
|
+
async function queryContracts({ chain, codeId, limit = 100 }) {
|
|
440
|
+
return paginate({ chain, path: `cosmwasm/wasm/v1/code/${codeId}/contracts`, dataKey: 'contracts', limit });
|
|
441
|
+
}
|
|
442
|
+
/** Legacy (terra classic) `/wasm/contracts/{contract}/store?query_msg=` query. Returns the `result` field. */
|
|
443
|
+
async function queryContractStore({ chain, contract, queryParam, block }) {
|
|
444
|
+
const msg = typeof queryParam === 'string' ? queryParam : JSON.stringify(queryParam);
|
|
445
|
+
const res = await query({ chain, contract, block, path: `wasm/contracts/${contract}/store`, params: { query_msg: msg } });
|
|
446
|
+
return res === null || res === void 0 ? void 0 : res.result;
|
|
447
|
+
}
|
|
448
|
+
/** `/cosmwasm/wasm/v1/contract/{contract}` -> `contract_info` (code_id, creator, admin, label, ...). */
|
|
449
|
+
async function getContractInfo({ chain, contract }) {
|
|
450
|
+
var _a;
|
|
451
|
+
const res = await query({ chain, contract, path: `cosmwasm/wasm/v1/contract/${contract}` });
|
|
452
|
+
return (_a = res === null || res === void 0 ? void 0 : res.contract_info) !== null && _a !== void 0 ? _a : res;
|
|
453
|
+
}
|
|
454
|
+
/** cw20 `token_info` -> { name, symbol, decimals, total_supply } */
|
|
455
|
+
async function getTokenInfo({ chain, contract, block }) {
|
|
456
|
+
return queryContract({ chain, contract, data: { token_info: {} }, block });
|
|
457
|
+
}
|
|
458
|
+
/** cw20 `minter` -> minter address (the pair contract for LP tokens) */
|
|
459
|
+
async function getLpMinter({ chain, token, block }) {
|
|
460
|
+
const data = await queryContract({ chain, contract: token, data: { minter: {} }, block });
|
|
461
|
+
return data === null || data === void 0 ? void 0 : data.minter;
|
|
462
|
+
}
|
|
463
|
+
function parseBlockResponse(data, chain, height) {
|
|
464
|
+
var _a, _b, _c;
|
|
465
|
+
const header = (_b = (_a = data === null || data === void 0 ? void 0 : data.block) === null || _a === void 0 ? void 0 : _a.header) !== null && _b !== void 0 ? _b : (_c = data === null || data === void 0 ? void 0 : data.sdk_block) === null || _c === void 0 ? void 0 : _c.header;
|
|
466
|
+
if (!(header === null || header === void 0 ? void 0 : header.height) || !(header === null || header === void 0 ? void 0 : header.time))
|
|
467
|
+
throw new Error(`cosmos.getBlock: malformed block response for ${chain} ${height}`);
|
|
468
|
+
return { number: Number(header.height), timestamp: parseBlockTime(header.time) };
|
|
469
|
+
}
|
|
470
|
+
/** Block header via `/cosmos/base/tendermint/v1beta1/blocks/{h}`, falling back to the legacy `/blocks/{h}` route. */
|
|
471
|
+
async function getBlock({ chain, height = 'latest' }) {
|
|
472
|
+
var _a, _b;
|
|
473
|
+
const h = height === undefined || height === null ? 'latest' : String(height);
|
|
474
|
+
const shouldRetry = (e) => !isPrunedHeightError(e) && (0, rpc_1.isRetryableError)(e);
|
|
475
|
+
let primaryError;
|
|
476
|
+
try {
|
|
477
|
+
const data = await (0, rpc_1.httpGet)(getEndpoints({ chain }), { path: `cosmos/base/tendermint/v1beta1/blocks/${h}`, shouldRetry, label: `${chain} block ${h}` });
|
|
478
|
+
return parseBlockResponse(data, chain, h);
|
|
479
|
+
}
|
|
480
|
+
catch (e) {
|
|
481
|
+
// only an explicit pruning / height error skips the fallback; a 404 may just be a missing route
|
|
482
|
+
if (isPrunedHeightError(e))
|
|
483
|
+
throw e;
|
|
484
|
+
primaryError = e;
|
|
485
|
+
(0, debugLog_1.debugLog)(`[chains.cosmos] ${chain} v1beta1 blocks/${h} failed, trying legacy /blocks route: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e}`);
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
const data = await (0, rpc_1.httpGet)(getEndpoints({ chain }), { path: `blocks/${h}`, shouldRetry, label: `${chain} legacy block ${h}` });
|
|
489
|
+
return parseBlockResponse(data, chain, h);
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
if (isPrunedHeightError(e))
|
|
493
|
+
throw e;
|
|
494
|
+
// a numeric height that both routes answer 404 for is a missing block (pruned or not yet
|
|
495
|
+
// produced), flagged so `getBlockAtTimestamp` treats it like an explicit pruning error
|
|
496
|
+
if (h !== 'latest' && isNotFoundError(e) && isNotFoundError(primaryError)) {
|
|
497
|
+
const missing = new Error(`[chains.cosmos] ${chain} block ${h} not found on any route (pruned or not yet produced): ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
498
|
+
missing.cosmosBlockMissing = true;
|
|
499
|
+
throw missing;
|
|
500
|
+
}
|
|
501
|
+
throw e;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async function getLatestBlock({ chain }) {
|
|
505
|
+
return getBlock({ chain, height: 'latest' });
|
|
506
|
+
}
|
|
507
|
+
const blockTimeCache = {};
|
|
508
|
+
function getBlockTimeCache(chain) {
|
|
509
|
+
if (!blockTimeCache[chain])
|
|
510
|
+
blockTimeCache[chain] = new Map();
|
|
511
|
+
return blockTimeCache[chain];
|
|
512
|
+
}
|
|
513
|
+
/** "height 5 is not available, lowest height is 69555892" -> 69555892 */
|
|
514
|
+
function parseLowestHeight(e) {
|
|
515
|
+
var _a, _b, _c, _d;
|
|
516
|
+
const match = /lowest height is (\d+)/.exec(String((_d = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : e === null || e === void 0 ? void 0 : e.message) !== null && _d !== void 0 ? _d : ''));
|
|
517
|
+
return match ? Number(match[1]) : undefined;
|
|
518
|
+
}
|
|
519
|
+
async function probeBlockTime(chain, height) {
|
|
520
|
+
var _a;
|
|
521
|
+
const cache = getBlockTimeCache(chain);
|
|
522
|
+
if (cache.has(height))
|
|
523
|
+
return { time: (_a = cache.get(height)) !== null && _a !== void 0 ? _a : null };
|
|
524
|
+
let time;
|
|
525
|
+
let lowestHeight;
|
|
526
|
+
try {
|
|
527
|
+
time = (await getBlock({ chain, height })).timestamp;
|
|
528
|
+
}
|
|
529
|
+
catch (e) {
|
|
530
|
+
if (!isPrunedHeightError(e))
|
|
531
|
+
throw e;
|
|
532
|
+
time = null;
|
|
533
|
+
lowestHeight = parseLowestHeight(e);
|
|
534
|
+
}
|
|
535
|
+
if (cache.size > 100000)
|
|
536
|
+
cache.clear();
|
|
537
|
+
cache.set(height, time);
|
|
538
|
+
return { time, lowestHeight };
|
|
539
|
+
}
|
|
540
|
+
/** Block time in seconds, `null` when the height is pruned / unavailable. Cached per chain. */
|
|
541
|
+
async function getBlockTime({ chain, height }) {
|
|
542
|
+
return (await probeBlockTime(chain, height)).time;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Largest height whose block time is <= `timestamp` (binary search over block
|
|
546
|
+
* headers, pruned heights count as "too old"). Throws when every retained block is
|
|
547
|
+
* newer than `timestamp`.
|
|
548
|
+
*/
|
|
549
|
+
async function getBlockAtTimestamp({ chain, timestamp }) {
|
|
550
|
+
const latest = await getLatestBlock({ chain });
|
|
551
|
+
if (timestamp >= latest.timestamp)
|
|
552
|
+
return latest;
|
|
553
|
+
getBlockTimeCache(chain).set(latest.number, latest.timestamp);
|
|
554
|
+
let lo = 1;
|
|
555
|
+
let hi = latest.number;
|
|
556
|
+
let answer;
|
|
557
|
+
let calls = 0;
|
|
558
|
+
while (lo <= hi) {
|
|
559
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
560
|
+
const { time, lowestHeight } = await probeBlockTime(chain, mid);
|
|
561
|
+
calls++;
|
|
562
|
+
if (time === null) {
|
|
563
|
+
// pruned: everything below is unavailable too, jump to the node's retention horizon when it tells us
|
|
564
|
+
lo = lowestHeight && lowestHeight > mid ? lowestHeight : mid + 1;
|
|
565
|
+
}
|
|
566
|
+
else if (time <= timestamp) {
|
|
567
|
+
answer = { number: mid, timestamp: time };
|
|
568
|
+
lo = mid + 1;
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
hi = mid - 1;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
(0, debugLog_1.debugLog)(`[chains.cosmos] ${chain} block at ${timestamp}: ${answer === null || answer === void 0 ? void 0 : answer.number} (${calls} lookups)`);
|
|
575
|
+
if (!answer)
|
|
576
|
+
throw new Error(`cosmos.getBlockAtTimestamp: no block at or before ${timestamp} on ${chain} (node retention horizon)`);
|
|
577
|
+
return answer;
|
|
578
|
+
}
|
|
579
|
+
// legacy API (util/cosmos.ts), signatures kept verbatim ------------------------
|
|
580
|
+
async function getCosmosBlock(block = 'latest', chain = 'cosmos') {
|
|
581
|
+
var _a;
|
|
582
|
+
try {
|
|
583
|
+
return await getBlock({ chain, height: block });
|
|
584
|
+
}
|
|
585
|
+
catch (e) {
|
|
586
|
+
const message = `Error fetching cosmos block -
|
|
587
|
+
chain: ${chain}, block: ${block}, endPoint: ${getEndpoints({ chain }).join(',')}, error: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : JSON.stringify(e)}`;
|
|
588
|
+
throw new Error(message);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function getCosmosProvider(chain) {
|
|
592
|
+
return {
|
|
593
|
+
getBlock: (block = 'latest') => getCosmosBlock(block, chain)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
// ---------------------------------------------------------------------------
|
|
597
|
+
// tendermint json-rpc
|
|
598
|
+
// ---------------------------------------------------------------------------
|
|
599
|
+
exports.tendermint = {
|
|
600
|
+
/** `<CHAIN>_TENDERMINT_RPC`, then `https://rpc.cosmos.directory/<chain>` */
|
|
601
|
+
getEndpoints({ chain }) {
|
|
602
|
+
return (0, rpc_1.getEndpoints)(chain, undefined, { envKey: `${chain.toUpperCase()}_TENDERMINT_RPC`, fallback: `${COSMOS_DIRECTORY_RPC}/${chain}` });
|
|
603
|
+
},
|
|
604
|
+
/** JSON-RPC `status` -> { node_info, sync_info: { latest_block_height, latest_block_time, earliest_block_height, ... } } */
|
|
605
|
+
async getStatus({ chain }) {
|
|
606
|
+
return (0, rpc_1.jsonRpc)('status', {}, { chain, endpoints: exports.tendermint.getEndpoints({ chain }) });
|
|
607
|
+
},
|
|
608
|
+
/** JSON-RPC `block` -> { block_id, block: { header, data, ... } } (latest when height is omitted) */
|
|
609
|
+
async getTendermintBlock({ chain, height }) {
|
|
610
|
+
const params = height !== undefined && height !== 'latest' ? { height: String(height) } : {};
|
|
611
|
+
return (0, rpc_1.jsonRpc)('block', params, { chain, endpoints: exports.tendermint.getEndpoints({ chain }) });
|
|
612
|
+
},
|
|
613
|
+
/** `{ number, timestamp }` from the tendermint `block` call */
|
|
614
|
+
async getBlock({ chain, height }) {
|
|
615
|
+
const res = await exports.tendermint.getTendermintBlock({ chain, height });
|
|
616
|
+
return parseBlockResponse(res, chain, height !== null && height !== void 0 ? height : 'latest');
|
|
617
|
+
},
|
|
618
|
+
};
|