@defillama/sdk 5.0.236 → 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.
- package/build/ChainApi.js +13 -0
- package/build/abi/tron.js +7 -1
- package/build/providers.json +1 -1
- package/build/util/LlamaProvider.js +2 -0
- package/build/util/blocks.js +292 -84
- package/build/util/chainUtils/data.json +69 -0
- package/build/util/coins.js +14 -3
- package/build/util/common.js +4 -1
- package/build/util/computeTVL.js +1 -1
- package/build/util/env.d.ts +1 -0
- package/build/util/env.js +17 -0
- package/build/util/indexer.js +120 -14
- package/build/util/logs.d.ts +3 -0
- package/build/util/logs.js +57 -26
- package/build/util/updateProviderList.js +2 -1
- package/package.json +3 -3
|
@@ -270,6 +270,8 @@ const httpRPC = {
|
|
|
270
270
|
});
|
|
271
271
|
if (error)
|
|
272
272
|
throw error;
|
|
273
|
+
if (!result)
|
|
274
|
+
throw new Error(`block ${params[0]} not found`); // pruned or not yet synced, let callers recognise it
|
|
273
275
|
result.number = parseInt(result.number);
|
|
274
276
|
result.timestamp = parseInt(result.timestamp);
|
|
275
277
|
return result;
|
package/build/util/blocks.js
CHANGED
|
@@ -130,6 +130,116 @@ const intialBlocks = {
|
|
|
130
130
|
terra: 4724001,
|
|
131
131
|
crab: 4969901
|
|
132
132
|
};
|
|
133
|
+
// pruned nodes don't serve old blocks, we find the earliest available block per chain and remember it for this process
|
|
134
|
+
const earliestAvailableBlock = {};
|
|
135
|
+
const prunedBlockBuffer = 1000; // pruning keeps moving forward, so stay a bit ahead of the reported height
|
|
136
|
+
// known formats:
|
|
137
|
+
// sei: "requested height 150022713 has been pruned; earliest available is 229666772"
|
|
138
|
+
// sei (pre evm): "EVM is only supported from block 79123881 onwards"
|
|
139
|
+
// publicnode: "pruned history unavailable: requested 100, earliest available 51000000"
|
|
140
|
+
// cosmos/tendermint/evm: "height 100 is not available, lowest height is 47180386" (often in error.data)
|
|
141
|
+
// both patterns need a block/height/available word next to the number, so unrelated numbers in error pages don't match
|
|
142
|
+
const earliestBlockPatterns = [
|
|
143
|
+
/(?:earliest|lowest|oldest)\s+(?:available\s+(?:block\s+|height\s+)?|(?:block|height)\s+)(?:number\s+)?(?:is\s+|:\s*|=\s*)?#?(\d+)/i,
|
|
144
|
+
/(?:supported|available)\s+(?:from|since|after)\s+(?:block|height)\s+#?(\d+)/i,
|
|
145
|
+
];
|
|
146
|
+
// the node's message sits in different fields depending on who wrapped the error (ethers, axios, formError, llamaRPCError)
|
|
147
|
+
const errorMessageFields = ['message', 'shortMessage', 'reason', 'data', '_underlyingError', 'error', 'info', 'response', 'errors'];
|
|
148
|
+
function getErrorText(e, depth = 0) {
|
|
149
|
+
if (typeof e === 'string')
|
|
150
|
+
return e;
|
|
151
|
+
if (!e || typeof e !== 'object' || depth > 5)
|
|
152
|
+
return ''; // llamaRPCError: errors[i].error.message sits 4 deep
|
|
153
|
+
if (Array.isArray(e))
|
|
154
|
+
return e.map(i => getErrorText(i, depth + 1)).filter(Boolean).join(' ');
|
|
155
|
+
return errorMessageFields
|
|
156
|
+
.map(key => getErrorText(e[key], depth + 1))
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.join(' ');
|
|
159
|
+
}
|
|
160
|
+
function getEarliestAvailableFromError(e, requested) {
|
|
161
|
+
const text = getErrorText(e);
|
|
162
|
+
const found = earliestBlockPatterns
|
|
163
|
+
.map(pattern => { var _a; return (_a = text.match(pattern)) === null || _a === void 0 ? void 0 : _a[1]; })
|
|
164
|
+
.filter(Boolean)
|
|
165
|
+
.map(Number)
|
|
166
|
+
.filter(block => block > requested);
|
|
167
|
+
return found.length ? Math.max(...found) : undefined;
|
|
168
|
+
}
|
|
169
|
+
// true only when the node says the block doesn't exist for it, transient errors (timeouts, 429s) are not pruning
|
|
170
|
+
function isMissingBlockError(e) {
|
|
171
|
+
const text = getErrorText(e);
|
|
172
|
+
return /Can't get block of chain|not found|not available|pruned|missing trie node|does not have this data|unknown block|no block/i.test(text);
|
|
173
|
+
}
|
|
174
|
+
// last resort for nodes that return null/not found without telling where the history starts
|
|
175
|
+
async function searchEarliestAvailableBlock(low, chain) {
|
|
176
|
+
const { number: latest } = await (0, exports.getLatestBlock)(chain);
|
|
177
|
+
let hi = latest - prunedBlockBuffer;
|
|
178
|
+
if (hi <= low || !(await isBlockAvailable(hi)))
|
|
179
|
+
return; // node is broken, not pruned
|
|
180
|
+
while (hi - low > prunedBlockBuffer) {
|
|
181
|
+
const mid = Math.floor((low + hi) / 2);
|
|
182
|
+
if (await isBlockAvailable(mid))
|
|
183
|
+
hi = mid;
|
|
184
|
+
else
|
|
185
|
+
low = mid;
|
|
186
|
+
}
|
|
187
|
+
return hi;
|
|
188
|
+
// we only get here after the node confirmed pruning, so a failure in the pruned region is most likely pruning too
|
|
189
|
+
// (sei answers "method handler crashed" for blocks just above its reported horizon); retry once to rule out a blip
|
|
190
|
+
async function isBlockAvailable(block, retry = true) {
|
|
191
|
+
try {
|
|
192
|
+
await fetchBlockFromProvider(block, chain);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
const earliest = getEarliestAvailableFromError(e, block);
|
|
197
|
+
if (earliest)
|
|
198
|
+
low = Math.max(low, earliest - 1); // node told us, skip ahead
|
|
199
|
+
else if (!isMissingBlockError(e) && retry)
|
|
200
|
+
return isBlockAvailable(block, false);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// fetches the given block, or the earliest available block if the node has pruned it
|
|
206
|
+
async function fetchAvailableBlock(low, chain) {
|
|
207
|
+
var _a, _b;
|
|
208
|
+
low = Math.max(low, (_a = earliestAvailableBlock[chain]) !== null && _a !== void 0 ? _a : 0); // already known to be pruned, don't ask again
|
|
209
|
+
let originalError;
|
|
210
|
+
// a few hops: sei first reports the evm start block, then the pruned height
|
|
211
|
+
for (let i = 0; i < 3; i++) {
|
|
212
|
+
try {
|
|
213
|
+
return await fetchBlockFromProvider(low, chain);
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
originalError = originalError !== null && originalError !== void 0 ? originalError : e;
|
|
217
|
+
const earliest = getEarliestAvailableFromError(e, low);
|
|
218
|
+
if (!earliest)
|
|
219
|
+
break;
|
|
220
|
+
low = earliestAvailableBlock[chain] = earliest + prunedBlockBuffer;
|
|
221
|
+
(0, debugLog_1.debugLog)(`chain: ${chain} block is pruned, using earliest available block ${low}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// only search when the node says the block is missing, anything else is a transient failure
|
|
225
|
+
if (!isMissingBlockError(originalError))
|
|
226
|
+
throw originalError;
|
|
227
|
+
// parallel probes can all land in the pruned range, run a single search per chain
|
|
228
|
+
if (!earliestBlockSearches[chain]) {
|
|
229
|
+
earliestBlockSearches[chain] = searchEarliestAvailableBlock(low, chain)
|
|
230
|
+
.catch(() => undefined)
|
|
231
|
+
.finally(() => delete earliestBlockSearches[chain]);
|
|
232
|
+
}
|
|
233
|
+
const earliest = await earliestBlockSearches[chain];
|
|
234
|
+
if (!earliest)
|
|
235
|
+
throw originalError;
|
|
236
|
+
if (((_b = earliestAvailableBlock[chain]) !== null && _b !== void 0 ? _b : 0) < earliest + prunedBlockBuffer) {
|
|
237
|
+
earliestAvailableBlock[chain] = earliest + prunedBlockBuffer;
|
|
238
|
+
(0, debugLog_1.debugLog)(`chain: ${chain} block is pruned, found earliest available block ${earliestAvailableBlock[chain]} by searching`);
|
|
239
|
+
}
|
|
240
|
+
return fetchBlockFromProvider(earliestAvailableBlock[chain], chain);
|
|
241
|
+
}
|
|
242
|
+
const earliestBlockSearches = {};
|
|
133
243
|
let lastBlockCacheSave = Date.now();
|
|
134
244
|
const blockCacheSaveInterval = 1000 * 60 * 5; // 5 minutes
|
|
135
245
|
const { data: blockTimeCache, saveCacheFile: saveBlockCacheFile } = (0, cache_1.getTempLocalCache)({ file: 'BlockCache-v1.json', defaultData: {}, clearAfter: cache_1.ONE_WEEK, returnWithSaveFunction: true });
|
|
@@ -152,23 +262,97 @@ function getChainLimiter(chain) {
|
|
|
152
262
|
return (0, p_limit_1.default)((0, env_1.getParallelGetBlocksLimit)(chain));
|
|
153
263
|
}
|
|
154
264
|
}
|
|
265
|
+
// concurrent lookups for the same chain & timestamp share one search
|
|
266
|
+
const inflightLookups = {};
|
|
155
267
|
async function lookupBlock(timestamp, extraParams) {
|
|
156
|
-
const { chain = "ethereum" } = extraParams !== null && extraParams !== void 0 ? extraParams : {};
|
|
157
|
-
|
|
158
|
-
|
|
268
|
+
const { chain = "ethereum", allowedTimeRange, acceptableBlockImprecision } = extraParams !== null && extraParams !== void 0 ? extraParams : {};
|
|
269
|
+
if (timestamp > Date.now() / 1000)
|
|
270
|
+
throw new Error(`Requesting for block in the future ${timestamp} is in the future, current time is ${Math.floor(Date.now() / 1000)}`);
|
|
271
|
+
const key = [chain, timestamp, allowedTimeRange, acceptableBlockImprecision].join(':');
|
|
272
|
+
if (!inflightLookups[key]) {
|
|
273
|
+
const limiter = getChainLimiter(chain);
|
|
274
|
+
inflightLookups[key] = lookupBlockFromService(timestamp, extraParams)
|
|
275
|
+
.then(block => block !== null && block !== void 0 ? block : limiter(() => _lookupBlock(timestamp, extraParams)))
|
|
276
|
+
.finally(() => delete inflightLookups[key]);
|
|
277
|
+
}
|
|
278
|
+
return inflightLookups[key];
|
|
279
|
+
}
|
|
280
|
+
const blockServiceRoundSeconds = 300; // the service's default route rounds timestamps down to this
|
|
281
|
+
const blockServiceTimeout = 5000;
|
|
282
|
+
const blockServiceMaxFailures = 3;
|
|
283
|
+
const blockServicePauseDuration = 1000 * 60 * 5; // after repeated failures, go straight to the local search for a while
|
|
284
|
+
let blockServiceFailures = 0;
|
|
285
|
+
let blockServicePausedUntil = 0;
|
|
286
|
+
// ask the block-service when BLOCK_SERVICE_URL is set, any failure falls back to the local search
|
|
287
|
+
async function lookupBlockFromService(timestamp, extraParams) {
|
|
288
|
+
var _a;
|
|
289
|
+
const serviceUrl = (0, env_1.getBlockServiceUrl)();
|
|
290
|
+
if (!serviceUrl || Date.now() < blockServicePausedUntil)
|
|
291
|
+
return;
|
|
292
|
+
const { chain = "ethereum", allowedTimeRange = 5 * 60 } = extraParams !== null && extraParams !== void 0 ? extraParams : {};
|
|
293
|
+
if (chain === 'waves')
|
|
294
|
+
return;
|
|
295
|
+
const route = allowedTimeRange >= blockServiceRoundSeconds ? '' : '/precise';
|
|
296
|
+
try {
|
|
297
|
+
const { block, timestamp: blockTimestamp } = await (0, common_1.fetchJson)(`${serviceUrl}/block/${chain}/${timestamp}${route}`, { timeout: blockServiceTimeout });
|
|
298
|
+
if (!Number.isInteger(block) || !Number.isInteger(blockTimestamp))
|
|
299
|
+
throw new Error('bad response');
|
|
300
|
+
// same guard as the local search: a wildly off answer is worse than no answer
|
|
301
|
+
if (blockTimestamp > timestamp || timestamp - blockTimestamp > 3600)
|
|
302
|
+
throw new Error(`block ${block} (${blockTimestamp}) is too far from ${timestamp}`);
|
|
303
|
+
blockServiceFailures = 0;
|
|
304
|
+
addBlockToCache(chain, { number: block, timestamp: blockTimestamp });
|
|
305
|
+
return { block, number: block, timestamp: blockTimestamp };
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
if (++blockServiceFailures >= blockServiceMaxFailures) {
|
|
309
|
+
blockServicePausedUntil = Date.now() + blockServicePauseDuration;
|
|
310
|
+
blockServiceFailures = 0;
|
|
311
|
+
}
|
|
312
|
+
(0, debugLog_1.debugLog)(`block-service lookup failed for ${chain} ${timestamp}, falling back to local search: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function getChainBlockTimeCache(chain) {
|
|
316
|
+
if (!blockTimeCache[chain])
|
|
317
|
+
blockTimeCache[chain] = {};
|
|
318
|
+
return blockTimeCache[chain];
|
|
319
|
+
}
|
|
320
|
+
function addBlockToCache(chain, block) {
|
|
321
|
+
const { number, timestamp } = block;
|
|
322
|
+
getChainBlockTimeCache(chain)[number] = { number, timestamp, };
|
|
323
|
+
// save the block cache every 5 minutes
|
|
324
|
+
if (Date.now() - lastBlockCacheSave > blockCacheSaveInterval) {
|
|
325
|
+
lastBlockCacheSave = Date.now();
|
|
326
|
+
saveBlockCacheFile();
|
|
327
|
+
}
|
|
159
328
|
}
|
|
160
329
|
const earliestAvailableBlockRange = {
|
|
161
330
|
nibiru: 4 * 1e5, // nibiru hold only the last 400k block data
|
|
162
331
|
evmos: 2 * 1e5,
|
|
163
332
|
};
|
|
333
|
+
// block time measured over the most recent blocks, a far better predictor of the near past than the all time average
|
|
334
|
+
const blockTimeSampleSize = 10000;
|
|
335
|
+
const blockTimeCacheDuration = 1000 * 60 * 60; // 1 hour
|
|
336
|
+
const recentBlockTimes = {};
|
|
337
|
+
async function getRecentBlockTime(chain, anchor, floor, onBlock) {
|
|
338
|
+
const cached = recentBlockTimes[chain];
|
|
339
|
+
if (cached && Date.now() - cached.updatedAt < blockTimeCacheDuration)
|
|
340
|
+
return cached.blockTime;
|
|
341
|
+
const sample = await fetchAvailableBlock(Math.max(floor, anchor.number - blockTimeSampleSize), chain);
|
|
342
|
+
if (sample.number >= anchor.number)
|
|
343
|
+
return 2; // pruning horizon is above the anchor, nothing to measure, rough guess, don't cache
|
|
344
|
+
onBlock(sample);
|
|
345
|
+
const blockTime = (anchor.timestamp - sample.timestamp) / (anchor.number - sample.number);
|
|
346
|
+
if (!(blockTime > 0) || !isFinite(blockTime))
|
|
347
|
+
return 2; // not enough history to measure, rough guess, don't cache
|
|
348
|
+
recentBlockTimes[chain] = { blockTime, updatedAt: Date.now() };
|
|
349
|
+
return blockTime;
|
|
350
|
+
}
|
|
164
351
|
async function _lookupBlock(timestamp, extraParams) {
|
|
165
352
|
var _a, _b;
|
|
166
353
|
const { allowedTimeRange = 5 * 60, acceptableBlockImprecision = 10 } = extraParams !== null && extraParams !== void 0 ? extraParams : {};
|
|
167
354
|
const chain = (_a = extraParams === null || extraParams === void 0 ? void 0 : extraParams.chain) !== null && _a !== void 0 ? _a : "ethereum";
|
|
168
355
|
let time = Date.now();
|
|
169
|
-
if (timestamp > time / 1000) {
|
|
170
|
-
throw new Error(`Requesting for block in the future ${timestamp} is in the future, current time is ${Math.floor(time / 1000)}`);
|
|
171
|
-
}
|
|
172
356
|
if (chain === 'waves') {
|
|
173
357
|
const api = `https://nodes.wavesnodes.com/blocks/heightByTimestamp/${timestamp}`;
|
|
174
358
|
const data = await (0, common_1.fetchJson)(api);
|
|
@@ -178,100 +362,124 @@ async function _lookupBlock(timestamp, extraParams) {
|
|
|
178
362
|
number: +data.height,
|
|
179
363
|
};
|
|
180
364
|
}
|
|
181
|
-
|
|
182
|
-
let
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
let
|
|
186
|
-
let
|
|
187
|
-
|
|
188
|
-
lowBlock = undefined;
|
|
189
|
-
let block;
|
|
190
|
-
let i = 0;
|
|
191
|
-
let blockImprecision;
|
|
192
|
-
let imprecision;
|
|
365
|
+
const maxCalls = 30;
|
|
366
|
+
let calls = 0;
|
|
367
|
+
let lowBlock; // closest known block at or before the timestamp
|
|
368
|
+
let highBlock; // closest known block after the timestamp
|
|
369
|
+
let bestBlock;
|
|
370
|
+
let blockTime;
|
|
371
|
+
let lastBracketWidth = Infinity;
|
|
193
372
|
try {
|
|
194
|
-
let
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
highBlock
|
|
209
|
-
|
|
210
|
-
// Short-circuit in case we are trying to get the current block
|
|
211
|
-
return {
|
|
212
|
-
block: highBlock.number,
|
|
213
|
-
number: highBlock.number,
|
|
214
|
-
timestamp: highBlock.timestamp
|
|
215
|
-
};
|
|
373
|
+
let floor = getFloorBlock();
|
|
374
|
+
const cachedLow = getLowBlock();
|
|
375
|
+
if (cachedLow && cachedLow.number >= floor)
|
|
376
|
+
updateBounds(cachedLow, false);
|
|
377
|
+
const cachedHigh = getHighBlock();
|
|
378
|
+
if (cachedHigh)
|
|
379
|
+
updateBounds(cachedHigh, false);
|
|
380
|
+
// anchor on the chain head unless the cache already has a block after the timestamp
|
|
381
|
+
if (!highBlock || earliestAvailableBlockRange[chain]) {
|
|
382
|
+
const head = await (0, exports.getLatestBlock)(chain);
|
|
383
|
+
// Short-circuit in case we are trying to get the current block, or the rpc head lags behind the requested time
|
|
384
|
+
if (Math.abs(head.timestamp - timestamp) < 60 * 5 || head.timestamp <= timestamp)
|
|
385
|
+
return { block: head.number, number: head.number, timestamp: head.timestamp };
|
|
386
|
+
floor = getFloorBlock(head);
|
|
387
|
+
if (!highBlock || head.number < highBlock.number)
|
|
388
|
+
updateBounds(head, false);
|
|
216
389
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
390
|
+
while (!isPreciseEnough() && calls < maxCalls) {
|
|
391
|
+
floor = Math.max(floor, (_b = earliestAvailableBlock[chain]) !== null && _b !== void 0 ? _b : 0);
|
|
392
|
+
if (lowBlock && lowBlock.number < floor)
|
|
393
|
+
lowBlock = undefined; // pruned away, guesses below the floor would just repeat the horizon block
|
|
394
|
+
let guesses;
|
|
395
|
+
if (lowBlock) {
|
|
396
|
+
// bracketed: interpolate between the bounds, bisect when the previous step didn't at least halve the bracket
|
|
397
|
+
const width = highBlock.number - lowBlock.number;
|
|
398
|
+
if (width <= 1)
|
|
399
|
+
break;
|
|
400
|
+
let guess = lowBlock.number + Math.round((timestamp - lowBlock.timestamp) * width / (highBlock.timestamp - lowBlock.timestamp));
|
|
401
|
+
if (width > lastBracketWidth / 2)
|
|
402
|
+
guess = Math.floor((lowBlock.number + highBlock.number) / 2);
|
|
403
|
+
lastBracketWidth = width;
|
|
404
|
+
guesses = [clamp(guess, lowBlock.number + 1, highBlock.number - 1)];
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
// only an upper bound: extrapolate back from it using the recent block time
|
|
408
|
+
if (highBlock.number <= floor)
|
|
409
|
+
break; // requested time is before the available history
|
|
410
|
+
if (!blockTime) {
|
|
411
|
+
const recentBlockTime = await getRecentBlockTime(chain, highBlock, floor, block => { calls++; updateBounds(block); });
|
|
412
|
+
blockTime = blockTime !== null && blockTime !== void 0 ? blockTime : recentBlockTime; // updateBounds may have set a more local slope meanwhile, keep that
|
|
413
|
+
}
|
|
414
|
+
if (lowBlock)
|
|
415
|
+
continue; // the block time sample already bracketed the timestamp
|
|
416
|
+
const guess = clamp(Math.round(highBlock.number - (highBlock.timestamp - timestamp) / blockTime), floor, highBlock.number - 1);
|
|
417
|
+
// probe around the guess too, so a slightly off block time still brackets the timestamp in one round trip
|
|
418
|
+
const spread = Math.max(acceptableBlockImprecision, Math.round((highBlock.number - guess) * 0.02));
|
|
419
|
+
guesses = [...new Set([guess - spread, guess, guess + spread].map(b => clamp(b, floor, highBlock.number - 1)))];
|
|
420
|
+
}
|
|
421
|
+
const blocks = await Promise.all(guesses.map(b => fetchAvailableBlock(b, chain)));
|
|
422
|
+
calls += blocks.length;
|
|
423
|
+
blocks.forEach(block => updateBounds(block));
|
|
232
424
|
}
|
|
233
|
-
if (
|
|
234
|
-
(0, debugLog_1.debugLog)(`chain: ${chain} block: ${
|
|
235
|
-
if (
|
|
425
|
+
if (calls > 2 && env_1.ENV_CONSTANTS.GET_BLOCK_STATS)
|
|
426
|
+
(0, debugLog_1.debugLog)(`chain: ${chain} block: ${bestBlock.number} #calls: ${calls} imprecision: ${Number(getImprecision(bestBlock) / 60).toFixed(2)} (min) Time Taken: ${Number((Date.now() - time) / 1000).toFixed(2)} (in sec)`);
|
|
427
|
+
if (getImprecision(bestBlock) > 3600
|
|
236
428
|
&& chain !== "bsc" // this check is there because bsc halted the chain for few days
|
|
237
429
|
&& !(0, env_1.shouldSkipBlockValidation)(chain)) {
|
|
430
|
+
if (!lowBlock && highBlock.number <= floor)
|
|
431
|
+
throw new Error(`Requested timestamp ${timestamp} (${new Date(timestamp * 1000).toISOString()}) is before the earliest available block ${highBlock.number} (${new Date(highBlock.timestamp * 1000).toISOString()}) of ${chain}`);
|
|
238
432
|
throw new Error("Block selected is more than 1 hour away from the requested timestamp");
|
|
239
433
|
}
|
|
240
434
|
return {
|
|
241
|
-
number:
|
|
242
|
-
block:
|
|
243
|
-
timestamp:
|
|
435
|
+
number: bestBlock.number,
|
|
436
|
+
block: bestBlock.number,
|
|
437
|
+
timestamp: bestBlock.timestamp
|
|
244
438
|
};
|
|
245
439
|
}
|
|
246
440
|
catch (e) {
|
|
247
441
|
throw (0, common_1.formError)(e);
|
|
248
442
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
443
|
+
function getFloorBlock(head) {
|
|
444
|
+
var _a, _b;
|
|
445
|
+
let floor = (_a = intialBlocks[chain]) !== null && _a !== void 0 ? _a : 100;
|
|
446
|
+
const envLowValue = process.env[`${chain.toUpperCase()}_BLOCK_LOW`];
|
|
447
|
+
if (envLowValue)
|
|
448
|
+
floor = parseInt(envLowValue);
|
|
449
|
+
floor = Math.max(floor, (_b = earliestAvailableBlock[chain]) !== null && _b !== void 0 ? _b : 0);
|
|
450
|
+
if (head && earliestAvailableBlockRange[chain])
|
|
451
|
+
floor = Math.max(floor, head.number - earliestAvailableBlockRange[chain]);
|
|
452
|
+
return floor;
|
|
453
|
+
}
|
|
454
|
+
function getImprecision(block) {
|
|
455
|
+
return Math.abs(block.timestamp - timestamp);
|
|
261
456
|
}
|
|
262
|
-
function
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
return
|
|
457
|
+
function isPreciseEnough() {
|
|
458
|
+
if (bestBlock && getImprecision(bestBlock) <= allowedTimeRange)
|
|
459
|
+
return true;
|
|
460
|
+
return !!lowBlock && !!highBlock && highBlock.number - lowBlock.number <= acceptableBlockImprecision;
|
|
266
461
|
}
|
|
267
|
-
function
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
saveBlockCacheFile();
|
|
462
|
+
function updateBounds(block, saveToCache = true) {
|
|
463
|
+
if (saveToCache)
|
|
464
|
+
addBlockToCache(chain, block);
|
|
465
|
+
if (block.timestamp <= timestamp) {
|
|
466
|
+
if (!lowBlock || block.number > lowBlock.number)
|
|
467
|
+
lowBlock = block;
|
|
274
468
|
}
|
|
469
|
+
else if (!highBlock || block.number < highBlock.number) {
|
|
470
|
+
// while unbracketed, the slope between consecutive upper bounds is the most local block time we have
|
|
471
|
+
if (highBlock && !lowBlock) {
|
|
472
|
+
const localBlockTime = (highBlock.timestamp - block.timestamp) / (highBlock.number - block.number);
|
|
473
|
+
if (localBlockTime > 0)
|
|
474
|
+
blockTime = localBlockTime;
|
|
475
|
+
}
|
|
476
|
+
highBlock = block;
|
|
477
|
+
}
|
|
478
|
+
if (!bestBlock || getImprecision(block) < getImprecision(bestBlock))
|
|
479
|
+
bestBlock = block;
|
|
480
|
+
}
|
|
481
|
+
function clamp(value, min, max) {
|
|
482
|
+
return Math.min(Math.max(value, min), max);
|
|
275
483
|
}
|
|
276
484
|
function getLowBlock() {
|
|
277
485
|
return Object.values(getChainBlockTimeCache(chain))
|