@xyo-network/chain-sdk 4.0.5 → 4.0.6
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/dist/browser/modules/ethereum/block/blockRangeChunks.d.ts +8 -0
- package/dist/browser/modules/ethereum/block/blockRangeChunks.d.ts.map +1 -0
- package/dist/browser/modules/ethereum/block/estimateBlockNumberAt.d.ts +17 -0
- package/dist/browser/modules/ethereum/block/estimateBlockNumberAt.d.ts.map +1 -0
- package/dist/browser/modules/ethereum/block/index.d.ts +2 -0
- package/dist/browser/modules/ethereum/block/index.d.ts.map +1 -1
- package/dist/browser/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts +27 -0
- package/dist/browser/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts.map +1 -1
- package/dist/browser/modules/ethereum/drivers/EvmStakeViewer.d.ts.map +1 -1
- package/dist/browser/modules/ethereum/rpc/isRpcRateLimitError.d.ts +6 -0
- package/dist/browser/modules/ethereum/rpc/isRpcRateLimitError.d.ts.map +1 -0
- package/dist/browser/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts +19 -0
- package/dist/browser/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts.map +1 -0
- package/dist/neutral/ethereum.mjs +249 -65
- package/dist/neutral/ethereum.mjs.map +4 -4
- package/dist/neutral/modules/ethereum/block/blockRangeChunks.d.ts +8 -0
- package/dist/neutral/modules/ethereum/block/blockRangeChunks.d.ts.map +1 -0
- package/dist/neutral/modules/ethereum/block/estimateBlockNumberAt.d.ts +17 -0
- package/dist/neutral/modules/ethereum/block/estimateBlockNumberAt.d.ts.map +1 -0
- package/dist/neutral/modules/ethereum/block/index.d.ts +2 -0
- package/dist/neutral/modules/ethereum/block/index.d.ts.map +1 -1
- package/dist/neutral/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts +27 -0
- package/dist/neutral/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts.map +1 -1
- package/dist/neutral/modules/ethereum/drivers/EvmStakeViewer.d.ts.map +1 -1
- package/dist/neutral/modules/ethereum/rpc/isRpcRateLimitError.d.ts +6 -0
- package/dist/neutral/modules/ethereum/rpc/isRpcRateLimitError.d.ts.map +1 -0
- package/dist/neutral/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts +19 -0
- package/dist/neutral/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts.map +1 -0
- package/dist/node/modules/ethereum/block/blockRangeChunks.d.ts +8 -0
- package/dist/node/modules/ethereum/block/blockRangeChunks.d.ts.map +1 -0
- package/dist/node/modules/ethereum/block/estimateBlockNumberAt.d.ts +17 -0
- package/dist/node/modules/ethereum/block/estimateBlockNumberAt.d.ts.map +1 -0
- package/dist/node/modules/ethereum/block/index.d.ts +2 -0
- package/dist/node/modules/ethereum/block/index.d.ts.map +1 -1
- package/dist/node/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts +27 -0
- package/dist/node/modules/ethereum/drivers/EvmStakeEventsViewer.d.ts.map +1 -1
- package/dist/node/modules/ethereum/drivers/EvmStakeViewer.d.ts.map +1 -1
- package/dist/node/modules/ethereum/rpc/isRpcRateLimitError.d.ts +6 -0
- package/dist/node/modules/ethereum/rpc/isRpcRateLimitError.d.ts.map +1 -0
- package/dist/node/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts +19 -0
- package/dist/node/modules/ethereum/rpc/withRpcRateLimitBackoff.d.ts.map +1 -0
- package/package.json +16 -16
|
@@ -162,6 +162,42 @@ var BaseEthProvider = class {
|
|
|
162
162
|
}
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
+
// src/modules/ethereum/block/blockRangeChunks.ts
|
|
166
|
+
function blockRangeChunks(fromBlock, toBlock, chunkSize) {
|
|
167
|
+
if (!Number.isInteger(chunkSize) || chunkSize < 1) {
|
|
168
|
+
throw new TypeError(`chunkSize must be an integer >= 1, got ${chunkSize}`);
|
|
169
|
+
}
|
|
170
|
+
if (!Number.isFinite(fromBlock) || !Number.isFinite(toBlock)) {
|
|
171
|
+
throw new TypeError(`fromBlock and toBlock must be finite numbers, got [${fromBlock}, ${toBlock}]`);
|
|
172
|
+
}
|
|
173
|
+
if (fromBlock > toBlock) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const chunks = [];
|
|
177
|
+
for (let start = fromBlock; start <= toBlock; start += chunkSize) {
|
|
178
|
+
const end = Math.min(start + chunkSize - 1, toBlock);
|
|
179
|
+
chunks.push([start, end]);
|
|
180
|
+
}
|
|
181
|
+
return chunks;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/modules/ethereum/block/estimateBlockNumberAt.ts
|
|
185
|
+
import { assertEx } from "@ariestools/sdk";
|
|
186
|
+
var DEFAULT_EVM_BLOCK_TIME_SEC = 12;
|
|
187
|
+
var DEFAULT_ESTIMATE_BLOCK_DATE = new Date(Date.UTC(2025, 5, 1));
|
|
188
|
+
function estimateBlockNumberFromHead(headNumber, headTimestampSec, targetDate = DEFAULT_ESTIMATE_BLOCK_DATE, avgBlockTimeSec = DEFAULT_EVM_BLOCK_TIME_SEC) {
|
|
189
|
+
const targetTimestampSec = Math.floor(targetDate.getTime() / 1e3);
|
|
190
|
+
if (targetTimestampSec >= headTimestampSec) {
|
|
191
|
+
return headNumber;
|
|
192
|
+
}
|
|
193
|
+
const blocksBack = Math.ceil((headTimestampSec - targetTimestampSec) / avgBlockTimeSec);
|
|
194
|
+
return Math.max(0, headNumber - blocksBack);
|
|
195
|
+
}
|
|
196
|
+
async function estimateBlockNumberAt(provider, targetDate = DEFAULT_ESTIMATE_BLOCK_DATE, avgBlockTimeSec = DEFAULT_EVM_BLOCK_TIME_SEC) {
|
|
197
|
+
const head = assertEx(await provider.getBlock("latest"), () => new Error("Failed to get latest eth block"));
|
|
198
|
+
return estimateBlockNumberFromHead(head.number, head.timestamp, targetDate, avgBlockTimeSec);
|
|
199
|
+
}
|
|
200
|
+
|
|
165
201
|
// src/modules/ethereum/block/rate/blockRate.ts
|
|
166
202
|
import { isDefined, isFalsy } from "@ariestools/sdk";
|
|
167
203
|
import {
|
|
@@ -210,7 +246,7 @@ var calculateEthBlockRate = async (provider, range, timeUnit) => {
|
|
|
210
246
|
|
|
211
247
|
// src/modules/ethereum/block/rate/timeRate.ts
|
|
212
248
|
import {
|
|
213
|
-
assertEx,
|
|
249
|
+
assertEx as assertEx2,
|
|
214
250
|
isDefined as isDefined2,
|
|
215
251
|
isDefinedNotNull
|
|
216
252
|
} from "@ariestools/sdk";
|
|
@@ -222,12 +258,12 @@ import {
|
|
|
222
258
|
var DEFAULT_TOLERANCE_MS = 3e4;
|
|
223
259
|
var DEFAULT_MAX_ATTEMPTS = 20;
|
|
224
260
|
var calculateEthTimeRate = async (provider, timeConfig, startBlockNumber, timeUnit, toleranceMs = DEFAULT_TOLERANCE_MS, maxAttempts = DEFAULT_MAX_ATTEMPTS) => {
|
|
225
|
-
|
|
261
|
+
assertEx2(Object.keys(timeConfig ?? {}).length === 1, () => "Only one time unit should be specified in timeConfig");
|
|
226
262
|
const startBlock = isDefinedNotNull(startBlockNumber) ? await provider.getBlock(BigInt(startBlockNumber)) : null;
|
|
227
|
-
|
|
228
|
-
const resolvedStartBlock = isDefinedNotNull(startBlock) ? startBlock :
|
|
263
|
+
assertEx2(isDefinedNotNull(startBlockNumber) && isDefinedNotNull(startBlock), () => "Start block could not be resolved");
|
|
264
|
+
const resolvedStartBlock = isDefinedNotNull(startBlock) ? startBlock : assertEx2(await provider.getBlock("latest"));
|
|
229
265
|
const timeInMilliseconds = getTimeConfigInMilliseconds(timeConfig);
|
|
230
|
-
|
|
266
|
+
assertEx2(timeInMilliseconds > 0, () => "Time duration must be greater than zero");
|
|
231
267
|
const blocksPerMillisecondRate = 1 / (12 * 1e3);
|
|
232
268
|
const initialBlocksInDuration = Math.floor(blocksPerMillisecondRate * timeInMilliseconds);
|
|
233
269
|
const endBlockNumber = await findEndBlockRecursive(
|
|
@@ -246,14 +282,14 @@ var calculateEthTimeRate = async (provider, timeConfig, startBlockNumber, timeUn
|
|
|
246
282
|
};
|
|
247
283
|
var findEndBlockRecursive = async (provider, startBlock, targetTimeMs, estimatedBlocksBack, toleranceMs, attemptsRemaining) => {
|
|
248
284
|
console.log(`Attempts remaining: ${attemptsRemaining}, Estimated blocks back: ${estimatedBlocksBack}`);
|
|
249
|
-
|
|
285
|
+
assertEx2(attemptsRemaining > 0, () => "Maximum attempts reached while searching for end block");
|
|
250
286
|
const startBlockEpoch = startBlock.timestamp;
|
|
251
287
|
const estimatedEndBlockNumber = startBlock.number - estimatedBlocksBack;
|
|
252
288
|
if (estimatedEndBlockNumber < 0) {
|
|
253
289
|
throw new Error("Estimated end block number is less than zero");
|
|
254
290
|
}
|
|
255
291
|
const endBlock = await provider.getBlock(estimatedEndBlockNumber);
|
|
256
|
-
const resolvedEndBlock =
|
|
292
|
+
const resolvedEndBlock = assertEx2(
|
|
257
293
|
isDefined2(endBlock) ? endBlock : void 0,
|
|
258
294
|
() => `Could not retrieve block ${estimatedEndBlockNumber} for time rate calculation`
|
|
259
295
|
);
|
|
@@ -296,7 +332,7 @@ import { createGenesisBlock } from "#protocol";
|
|
|
296
332
|
// src/modules/ethereum/createChainContract.ts
|
|
297
333
|
import {
|
|
298
334
|
asAddress,
|
|
299
|
-
assertEx as
|
|
335
|
+
assertEx as assertEx3,
|
|
300
336
|
ETH_ZERO_ADDRESS,
|
|
301
337
|
hexToBigInt,
|
|
302
338
|
toEthAddress
|
|
@@ -324,7 +360,7 @@ var createChainContract = async (signer, stakingTokenAddress, genesisBlockHash,
|
|
|
324
360
|
);
|
|
325
361
|
await deployResponse.waitForDeployment();
|
|
326
362
|
const xyoChainContractAddress = await deployResponse.getAddress();
|
|
327
|
-
return
|
|
363
|
+
return assertEx3(asAddress(xyoChainContractAddress), () => "Invalid staking contract address");
|
|
328
364
|
} catch (ex) {
|
|
329
365
|
console.log(ex);
|
|
330
366
|
throw ex;
|
|
@@ -334,7 +370,7 @@ var createChainContract = async (signer, stakingTokenAddress, genesisBlockHash,
|
|
|
334
370
|
// src/modules/ethereum/createChainRewardsContract.ts
|
|
335
371
|
import {
|
|
336
372
|
asAddress as asAddress2,
|
|
337
|
-
assertEx as
|
|
373
|
+
assertEx as assertEx4,
|
|
338
374
|
toFixedPoint
|
|
339
375
|
} from "@ariestools/sdk";
|
|
340
376
|
import { XyoChainRewards__factory as XyoChainRewardsFactory } from "@xyo-network/typechain";
|
|
@@ -361,7 +397,7 @@ var createChainRewardsContract = async (signer, genesisReward = 0n, gas = gasCon
|
|
|
361
397
|
);
|
|
362
398
|
await deployResponse.waitForDeployment();
|
|
363
399
|
const xyoChainRewardsAddress = await deployResponse.getAddress();
|
|
364
|
-
return
|
|
400
|
+
return assertEx4(asAddress2(xyoChainRewardsAddress), () => "Invalid rewards contract address");
|
|
365
401
|
} catch (ex) {
|
|
366
402
|
console.log(ex);
|
|
367
403
|
throw ex;
|
|
@@ -371,7 +407,7 @@ var createChainRewardsContract = async (signer, genesisReward = 0n, gas = gasCon
|
|
|
371
407
|
// src/modules/ethereum/nextContractAddress.ts
|
|
372
408
|
import {
|
|
373
409
|
asAddress as asAddress3,
|
|
374
|
-
assertEx as
|
|
410
|
+
assertEx as assertEx5,
|
|
375
411
|
toEthAddress as toEthAddress2
|
|
376
412
|
} from "@ariestools/sdk";
|
|
377
413
|
import { getCreateAddress } from "ethers/address";
|
|
@@ -379,11 +415,11 @@ var nextContractAddressForAddress = async (address, provider) => {
|
|
|
379
415
|
const deployerAddress = toEthAddress2(address);
|
|
380
416
|
const nonce = await provider.getTransactionCount(deployerAddress);
|
|
381
417
|
const contractAddress = getCreateAddress({ from: deployerAddress, nonce });
|
|
382
|
-
return
|
|
418
|
+
return assertEx5(asAddress3(contractAddress), () => "Unable to convert contract address to address");
|
|
383
419
|
};
|
|
384
420
|
var nextContractAddress = async (signer) => {
|
|
385
|
-
const provider =
|
|
386
|
-
const address =
|
|
421
|
+
const provider = assertEx5(signer.provider, () => "Unable to obtain provider from signer");
|
|
422
|
+
const address = assertEx5(asAddress3(await signer.getAddress()), () => "Unable to obtain address from signer");
|
|
387
423
|
return await nextContractAddressForAddress(address, provider);
|
|
388
424
|
};
|
|
389
425
|
|
|
@@ -414,7 +450,7 @@ async function createChain(signer, stakingTokenAddress, initialBlockProducer, re
|
|
|
414
450
|
|
|
415
451
|
// src/modules/ethereum/drivers/EvmStakeEventsViewer.ts
|
|
416
452
|
import {
|
|
417
|
-
assertEx as
|
|
453
|
+
assertEx as assertEx8,
|
|
418
454
|
exists,
|
|
419
455
|
isDefined as isDefined3,
|
|
420
456
|
toAddress as toAddress3,
|
|
@@ -444,7 +480,7 @@ var AbstractEvmProvider = class extends AbstractCreatableProvider {
|
|
|
444
480
|
};
|
|
445
481
|
|
|
446
482
|
// src/modules/ethereum/providers/AbstractEvmRunner.ts
|
|
447
|
-
import { assertEx as
|
|
483
|
+
import { assertEx as assertEx6 } from "@ariestools/sdk";
|
|
448
484
|
|
|
449
485
|
// src/modules/ethereum/providers/AbstractEvmViewer.ts
|
|
450
486
|
var AbstractEvmViewer = class extends AbstractEvmProvider {
|
|
@@ -453,8 +489,8 @@ var AbstractEvmViewer = class extends AbstractEvmProvider {
|
|
|
453
489
|
// src/modules/ethereum/providers/AbstractEvmRunner.ts
|
|
454
490
|
var AbstractEvmRunner = class extends AbstractEvmViewer {
|
|
455
491
|
static async paramsHandler(params) {
|
|
456
|
-
const runner =
|
|
457
|
-
const provider =
|
|
492
|
+
const runner = assertEx6(params?.runner, () => new Error("No runner defined"));
|
|
493
|
+
const provider = assertEx6(runner.provider, () => new Error("Runner must have a provider defined"));
|
|
458
494
|
return { ...await super.paramsHandler({ ...params, provider }), runner };
|
|
459
495
|
}
|
|
460
496
|
};
|
|
@@ -462,7 +498,7 @@ var AbstractEvmRunner = class extends AbstractEvmViewer {
|
|
|
462
498
|
// src/modules/ethereum/providers/EvmChainContractViewer.ts
|
|
463
499
|
import {
|
|
464
500
|
asHash,
|
|
465
|
-
assertEx as
|
|
501
|
+
assertEx as assertEx7,
|
|
466
502
|
toAddress as toAddress2,
|
|
467
503
|
toEthAddress as toEthAddress5,
|
|
468
504
|
toHex
|
|
@@ -495,7 +531,7 @@ var EvmChainContractViewer = class extends AbstractEvmViewer {
|
|
|
495
531
|
let contractViewer = this;
|
|
496
532
|
let forkedAtBlockNumber = await contractViewer.forkedAtBlockNumber();
|
|
497
533
|
while (forkedAtBlockNumber !== null && blockNumber <= forkedAtBlockNumber) {
|
|
498
|
-
contractViewer =
|
|
534
|
+
contractViewer = assertEx7(await contractViewer.forkedChainContractViewer(), () => "Forked chain contract viewer is undefined");
|
|
499
535
|
forkedAtBlockNumber = await contractViewer.forkedAtBlockNumber();
|
|
500
536
|
chainId = await contractViewer.chainId();
|
|
501
537
|
}
|
|
@@ -641,6 +677,93 @@ EvmStakeTotalsViewer = __decorateClass([
|
|
|
641
677
|
creatableProvider3()
|
|
642
678
|
], EvmStakeTotalsViewer);
|
|
643
679
|
|
|
680
|
+
// src/modules/ethereum/rpc/withRpcRateLimitBackoff.ts
|
|
681
|
+
import { delay } from "@ariestools/sdk";
|
|
682
|
+
|
|
683
|
+
// src/modules/ethereum/rpc/isRpcRateLimitError.ts
|
|
684
|
+
function isRpcRateLimitError(error) {
|
|
685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
686
|
+
const queue = [error];
|
|
687
|
+
while (queue.length > 0) {
|
|
688
|
+
const current = queue.pop();
|
|
689
|
+
if (current === void 0 || current === null || seen.has(current)) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
seen.add(current);
|
|
693
|
+
if (typeof current === "string") {
|
|
694
|
+
if (matchesRateLimitText(current)) {
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (typeof current === "number") {
|
|
700
|
+
if (isRateLimitCode(current)) {
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (typeof current !== "object") {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (Array.isArray(current)) {
|
|
709
|
+
const items = current;
|
|
710
|
+
queue.push(...items);
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
const record = current;
|
|
714
|
+
for (const key of ["code", "message", "shortMessage", "reason", "status", "statusCode"]) {
|
|
715
|
+
if (key in record) {
|
|
716
|
+
queue.push(record[key]);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
for (const key of ["error", "info", "data", "value", "body", "payload"]) {
|
|
720
|
+
if (key in record) {
|
|
721
|
+
queue.push(record[key]);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
function isRateLimitCode(code) {
|
|
728
|
+
return code === 429 || code === -32005 || code === -32016;
|
|
729
|
+
}
|
|
730
|
+
function matchesRateLimitText(text) {
|
|
731
|
+
const normalized = text.toLowerCase();
|
|
732
|
+
return normalized.includes("too many requests") || normalized.includes("rate limit") || normalized.includes("rate exceeded") || normalized.includes("call rate limit") || normalized.includes("exceeded the quota");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// src/modules/ethereum/rpc/withRpcRateLimitBackoff.ts
|
|
736
|
+
async function withRpcRateLimitBackoff(fn, options = {}) {
|
|
737
|
+
const {
|
|
738
|
+
backoff = 2,
|
|
739
|
+
initialDelayMs = 1e3,
|
|
740
|
+
maxAttempts = 8,
|
|
741
|
+
maxDelayMs = 3e4,
|
|
742
|
+
onRetry
|
|
743
|
+
} = options;
|
|
744
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
|
|
745
|
+
throw new TypeError(`maxAttempts must be an integer >= 1, got ${maxAttempts}`);
|
|
746
|
+
}
|
|
747
|
+
let delayMs = initialDelayMs;
|
|
748
|
+
let lastError;
|
|
749
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
750
|
+
try {
|
|
751
|
+
return await fn();
|
|
752
|
+
} catch (error) {
|
|
753
|
+
lastError = error;
|
|
754
|
+
const isLastAttempt = attempt >= maxAttempts;
|
|
755
|
+
if (!isRpcRateLimitError(error) || isLastAttempt) {
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
758
|
+
const waitMs = Math.min(delayMs, maxDelayMs);
|
|
759
|
+
onRetry?.(error, attempt, waitMs);
|
|
760
|
+
await delay(waitMs);
|
|
761
|
+
delayMs = Math.min(delayMs * backoff, maxDelayMs);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
throw lastError;
|
|
765
|
+
}
|
|
766
|
+
|
|
644
767
|
// src/modules/ethereum/drivers/EvmStakeEventZod.ts
|
|
645
768
|
import {
|
|
646
769
|
EthAddressZod,
|
|
@@ -696,11 +819,25 @@ var asEvmStakeWithdrawnEvent = zodAsFactory(EvmStakeWithdrawnEventZod, "asEvmSta
|
|
|
696
819
|
var toEvmStakeWithdrawnEvent = zodToFactory(EvmStakeWithdrawnEventZod, "toEvmStakeWithdrawnEvent");
|
|
697
820
|
|
|
698
821
|
// src/modules/ethereum/drivers/EvmStakeEventsViewer.ts
|
|
822
|
+
var DEFAULT_EVENT_QUERY_BLOCK_CHUNK_SIZE = 2e3;
|
|
823
|
+
var DEFAULT_EVENT_QUERY_MAX_ATTEMPTS = 8;
|
|
824
|
+
var DEFAULT_EVENT_QUERY_INITIAL_DELAY_MS = 1e3;
|
|
699
825
|
var EvmStakeEventsViewer = class extends AbstractEvmViewer {
|
|
700
826
|
moniker = EvmStakeEventsViewer.defaultMoniker;
|
|
827
|
+
/** Set at start when `params.deploymentBlock` is omitted (estimated 2025-06-01). */
|
|
828
|
+
defaultDeploymentBlock = 0;
|
|
701
829
|
eventCache = /* @__PURE__ */ new Map();
|
|
702
830
|
get deploymentBlock() {
|
|
703
|
-
return this.params.deploymentBlock ??
|
|
831
|
+
return this.params.deploymentBlock ?? this.defaultDeploymentBlock;
|
|
832
|
+
}
|
|
833
|
+
get eventQueryBlockChunkSize() {
|
|
834
|
+
return this.params.eventQueryBlockChunkSize ?? DEFAULT_EVENT_QUERY_BLOCK_CHUNK_SIZE;
|
|
835
|
+
}
|
|
836
|
+
get eventQueryInitialDelayMs() {
|
|
837
|
+
return this.params.eventQueryInitialDelayMs ?? DEFAULT_EVENT_QUERY_INITIAL_DELAY_MS;
|
|
838
|
+
}
|
|
839
|
+
get eventQueryMaxAttempts() {
|
|
840
|
+
return this.params.eventQueryMaxAttempts ?? DEFAULT_EVENT_QUERY_MAX_ATTEMPTS;
|
|
704
841
|
}
|
|
705
842
|
async positionCount(range) {
|
|
706
843
|
const events = await this.stakeEvents(range);
|
|
@@ -747,9 +884,7 @@ var EvmStakeEventsViewer = class extends AbstractEvmViewer {
|
|
|
747
884
|
}
|
|
748
885
|
const validatedEvent = validatedEventResult.data;
|
|
749
886
|
const { eventName, blockNumber } = validatedEvent;
|
|
750
|
-
this.logger?.log(`Processing ${eventName} event for stake ID ${id} at block ${blockNumber}`);
|
|
751
887
|
const cacheKey = `${Number(id)}|${eventName}|${staker}`;
|
|
752
|
-
const existingStakeEvent = this.eventCache.get(cacheKey);
|
|
753
888
|
const stakeEvent = {
|
|
754
889
|
name: eventName,
|
|
755
890
|
args: {
|
|
@@ -760,62 +895,101 @@ var EvmStakeEventsViewer = class extends AbstractEvmViewer {
|
|
|
760
895
|
},
|
|
761
896
|
time: blockNumber
|
|
762
897
|
};
|
|
763
|
-
if (isDefined3(existingStakeEvent)) {
|
|
764
|
-
this.logger?.warn(`Received ${eventName} event for stake ID ${id} that is already cached, updating cache...`);
|
|
765
|
-
this.logger?.warn("1");
|
|
766
|
-
this.logger?.warn("Previous event", existingStakeEvent);
|
|
767
|
-
this.logger?.warn("2");
|
|
768
|
-
this.logger?.warn("New event", stakeEvent);
|
|
769
|
-
this.logger?.warn("3");
|
|
770
|
-
}
|
|
771
898
|
this.eventCache.set(cacheKey, stakeEvent);
|
|
772
899
|
return stakeEvent;
|
|
773
900
|
}
|
|
774
901
|
async startHandler() {
|
|
775
902
|
try {
|
|
776
|
-
const
|
|
777
|
-
await
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
await
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
903
|
+
const provider = assertEx8(this.contract.runner?.provider, () => new Error("Failed to get provider"));
|
|
904
|
+
const historyEndBlock = assertEx8(await provider.getBlockNumber(), () => new Error("Failed to get currentEthBlock"));
|
|
905
|
+
if (!isDefined3(this.params.deploymentBlock)) {
|
|
906
|
+
this.defaultDeploymentBlock = await estimateBlockNumberAt(provider);
|
|
907
|
+
}
|
|
908
|
+
await this.loadHistoricalEvents(this.deploymentBlock, historyEndBlock);
|
|
909
|
+
await this.attachLiveListeners();
|
|
910
|
+
const latestBlock = assertEx8(await provider.getBlockNumber(), () => new Error("Failed to get latestEthBlock"));
|
|
911
|
+
if (latestBlock > historyEndBlock) {
|
|
912
|
+
await this.loadHistoricalEvents(historyEndBlock + 1, latestBlock);
|
|
913
|
+
}
|
|
787
914
|
} catch (error) {
|
|
788
915
|
this.logger?.error("EthereumChainStakeEvents failed to start:", error);
|
|
789
916
|
throw error;
|
|
790
917
|
}
|
|
791
918
|
}
|
|
792
|
-
async
|
|
919
|
+
async attachLiveListeners() {
|
|
920
|
+
await this.contract.on(this.contract.getEvent("StakeAdded"), (staked, staker, id, amount, event) => {
|
|
921
|
+
this.onEventHandler(staked, staker, id, amount, event);
|
|
922
|
+
});
|
|
923
|
+
await this.contract.on(this.contract.getEvent("StakeRemoved"), (staked, staker, id, amount, event) => {
|
|
924
|
+
this.onEventHandler(staked, staker, id, amount, event);
|
|
925
|
+
});
|
|
926
|
+
await this.contract.on(this.contract.getEvent("StakeWithdrawn"), (staked, staker, id, amount, event) => {
|
|
927
|
+
this.onEventHandler(staked, staker, id, amount, event);
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
async loadHistoricalEvents(fromBlock, toBlock) {
|
|
931
|
+
if (fromBlock > toBlock) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
793
934
|
const stakeEventGroups = await Promise.all([
|
|
794
|
-
this.stakeEventsNative([
|
|
795
|
-
this.stakeEventsNative([
|
|
796
|
-
this.stakeEventsNative([
|
|
935
|
+
this.stakeEventsNative([fromBlock, toBlock], { name: "StakeAdded" }),
|
|
936
|
+
this.stakeEventsNative([fromBlock, toBlock], { name: "StakeRemoved" }),
|
|
937
|
+
this.stakeEventsNative([fromBlock, toBlock], { name: "StakeWithdrawn" })
|
|
797
938
|
]);
|
|
798
939
|
const allStakeEvents = stakeEventGroups.flat().toSorted((a, b) => a.time - b.time);
|
|
799
|
-
|
|
940
|
+
const chunks = blockRangeChunks(fromBlock, toBlock, this.eventQueryBlockChunkSize);
|
|
941
|
+
this.logger?.log(
|
|
942
|
+
`Cached ${allStakeEvents.length} stake events from blocks ${fromBlock}-${toBlock} (${chunks.length} chunk(s) of up to ${this.eventQueryBlockChunkSize} blocks)`
|
|
943
|
+
);
|
|
800
944
|
for (const event of allStakeEvents) {
|
|
801
945
|
this.eventCache.set(`${Number(event.args.id)}|${event.name}|${event.args.staker}`, event);
|
|
802
946
|
}
|
|
803
947
|
}
|
|
804
948
|
async stakeEventsNative(range = [0, "latest"], filter) {
|
|
805
949
|
const name = filter?.name;
|
|
806
|
-
if (isDefined3(name)) {
|
|
807
|
-
|
|
808
|
-
const stakerFilter = filter?.args?.staker;
|
|
809
|
-
const stakedFilterEth = isDefined3(stakedFilter) ? toEthAddress8(stakedFilter) : void 0;
|
|
810
|
-
const stakerFilterEth = isDefined3(stakerFilter) ? toEthAddress8(stakerFilter) : void 0;
|
|
811
|
-
const ethFilter = this.contract.filters[name](stakedFilterEth, stakerFilterEth);
|
|
812
|
-
const ethEvents = await this.contract.queryFilter(ethFilter, range[0], range[1]);
|
|
813
|
-
return ethEvents.map((e) => {
|
|
814
|
-
assertEx7(e.eventName === name, () => `Event name mismatch expected '${name}' but got '${e.eventName}'`);
|
|
815
|
-
return this.onEventHandler(e.args.staked, e.args.by, e.args.id, e.args.amount, e, true);
|
|
816
|
-
}).filter(exists);
|
|
950
|
+
if (!isDefined3(name)) {
|
|
951
|
+
throw new Error("Only filtered stake events (by name) are supported");
|
|
817
952
|
}
|
|
818
|
-
|
|
953
|
+
const stakedFilter = filter?.args?.staked;
|
|
954
|
+
const stakerFilter = filter?.args?.staker;
|
|
955
|
+
const stakedFilterEth = isDefined3(stakedFilter) ? toEthAddress8(stakedFilter) : void 0;
|
|
956
|
+
const stakerFilterEth = isDefined3(stakerFilter) ? toEthAddress8(stakerFilter) : void 0;
|
|
957
|
+
const ethFilter = this.contract.filters[name](stakedFilterEth, stakerFilterEth);
|
|
958
|
+
const fromBlock = range[0];
|
|
959
|
+
const toBlock = range[1] === "latest" ? assertEx8(await this.contract.runner?.provider?.getBlockNumber(), () => new Error("Failed to resolve latest block")) : range[1];
|
|
960
|
+
if (fromBlock > toBlock) {
|
|
961
|
+
return [];
|
|
962
|
+
}
|
|
963
|
+
const chunkSize = this.eventQueryBlockChunkSize;
|
|
964
|
+
const chunks = blockRangeChunks(fromBlock, toBlock, chunkSize);
|
|
965
|
+
const ethEventGroups = [];
|
|
966
|
+
for (const [chunkFrom, chunkTo] of chunks) {
|
|
967
|
+
try {
|
|
968
|
+
ethEventGroups.push(await withRpcRateLimitBackoff(
|
|
969
|
+
() => this.contract.queryFilter(ethFilter, chunkFrom, chunkTo),
|
|
970
|
+
{
|
|
971
|
+
initialDelayMs: this.eventQueryInitialDelayMs,
|
|
972
|
+
maxAttempts: this.eventQueryMaxAttempts,
|
|
973
|
+
onRetry: (_error, attempt, delayMs) => {
|
|
974
|
+
this.logger?.warn(
|
|
975
|
+
`Rate limited fetching ${name} logs for blocks ${chunkFrom}-${chunkTo} (attempt ${attempt}/${this.eventQueryMaxAttempts}); backing off ${delayMs}ms`
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
));
|
|
980
|
+
} catch (error) {
|
|
981
|
+
this.logger?.error(
|
|
982
|
+
`eth_getLogs failed for ${name} blocks ${chunkFrom}-${chunkTo} after rate-limit retries (from=${fromBlock} to=${toBlock}):`,
|
|
983
|
+
error
|
|
984
|
+
);
|
|
985
|
+
throw error;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const ethEvents = ethEventGroups.flat();
|
|
989
|
+
return ethEvents.map((e) => {
|
|
990
|
+
assertEx8(e.eventName === name, () => `Event name mismatch expected '${name}' but got '${e.eventName}'`);
|
|
991
|
+
return this.onEventHandler(e.args.staked, e.args.by, e.args.id, e.args.amount, e, true);
|
|
992
|
+
}).filter(exists);
|
|
819
993
|
}
|
|
820
994
|
};
|
|
821
995
|
__publicField(EvmStakeEventsViewer, "connectionTypes", ["evm-rpc"]);
|
|
@@ -829,7 +1003,8 @@ EvmStakeEventsViewer = __decorateClass([
|
|
|
829
1003
|
|
|
830
1004
|
// src/modules/ethereum/drivers/EvmStakeViewer.ts
|
|
831
1005
|
import {
|
|
832
|
-
assertEx as
|
|
1006
|
+
assertEx as assertEx9,
|
|
1007
|
+
isDefined as isDefined4,
|
|
833
1008
|
toAddress as toAddress4,
|
|
834
1009
|
toEthAddress as toEthAddress9
|
|
835
1010
|
} from "@ariestools/sdk";
|
|
@@ -883,6 +1058,7 @@ var EvmStakeViewer = class extends AbstractEvmViewer {
|
|
|
883
1058
|
async createHandler() {
|
|
884
1059
|
await super.createHandler();
|
|
885
1060
|
this._stakeEventsViewer = await this.locator.getInstance(StakeEventsViewerMoniker2);
|
|
1061
|
+
this._deploymentBlock = isDefined4(this.params.deploymentBlock) ? this.params.deploymentBlock : await estimateBlockNumberAt(this.params.provider);
|
|
886
1062
|
}
|
|
887
1063
|
minWithdrawalBlocks() {
|
|
888
1064
|
return 0;
|
|
@@ -983,14 +1159,14 @@ var EvmStakeViewer = class extends AbstractEvmViewer {
|
|
|
983
1159
|
});
|
|
984
1160
|
});
|
|
985
1161
|
await this.contract.on(this.contract.getEvent("StakeRemoved"), (staked, staker, id, amount, event) => {
|
|
986
|
-
const stake =
|
|
1162
|
+
const stake = assertEx9(this._stakeCache.get(Number(id)));
|
|
987
1163
|
this._stakeCache.set(Number(id), {
|
|
988
1164
|
...stake,
|
|
989
1165
|
removeBlock: event.blockNumber
|
|
990
1166
|
});
|
|
991
1167
|
});
|
|
992
1168
|
await this.contract.on(this.contract.getEvent("StakeWithdrawn"), (staked, staker, id, amount, event) => {
|
|
993
|
-
const stake =
|
|
1169
|
+
const stake = assertEx9(this._stakeCache.get(Number(id)));
|
|
994
1170
|
this._stakeCache.set(Number(id), {
|
|
995
1171
|
...stake,
|
|
996
1172
|
withdrawBlock: event.blockNumber
|
|
@@ -1023,7 +1199,7 @@ var EvmStakeViewer = class extends AbstractEvmViewer {
|
|
|
1023
1199
|
return;
|
|
1024
1200
|
}
|
|
1025
1201
|
if (event.name === "StakeRemoved") {
|
|
1026
|
-
const stake =
|
|
1202
|
+
const stake = assertEx9(this._stakeCache.get(event.args.id));
|
|
1027
1203
|
this._stakeCache.set(event.args.id, {
|
|
1028
1204
|
...stake,
|
|
1029
1205
|
removeBlock: event.time
|
|
@@ -1031,7 +1207,7 @@ var EvmStakeViewer = class extends AbstractEvmViewer {
|
|
|
1031
1207
|
return;
|
|
1032
1208
|
}
|
|
1033
1209
|
if (event.name === "StakeWithdrawn") {
|
|
1034
|
-
const stake =
|
|
1210
|
+
const stake = assertEx9(this._stakeCache.get(event.args.id));
|
|
1035
1211
|
this._stakeCache.set(event.args.id, {
|
|
1036
1212
|
...stake,
|
|
1037
1213
|
withdrawBlock: event.time
|
|
@@ -1062,6 +1238,11 @@ export {
|
|
|
1062
1238
|
AbstractEvmRunner,
|
|
1063
1239
|
AbstractEvmViewer,
|
|
1064
1240
|
BaseEthProvider,
|
|
1241
|
+
DEFAULT_ESTIMATE_BLOCK_DATE,
|
|
1242
|
+
DEFAULT_EVENT_QUERY_BLOCK_CHUNK_SIZE,
|
|
1243
|
+
DEFAULT_EVENT_QUERY_INITIAL_DELAY_MS,
|
|
1244
|
+
DEFAULT_EVENT_QUERY_MAX_ATTEMPTS,
|
|
1245
|
+
DEFAULT_EVM_BLOCK_TIME_SEC,
|
|
1065
1246
|
EthHashRegEx,
|
|
1066
1247
|
EthHashZod,
|
|
1067
1248
|
EvmChainContractViewer,
|
|
@@ -1082,11 +1263,14 @@ export {
|
|
|
1082
1263
|
asEvmStakeEvent,
|
|
1083
1264
|
asEvmStakeRemovedEvent,
|
|
1084
1265
|
asEvmStakeWithdrawnEvent,
|
|
1266
|
+
blockRangeChunks,
|
|
1085
1267
|
calculateEthBlockRate,
|
|
1086
1268
|
calculateEthTimeRate,
|
|
1087
1269
|
createChain,
|
|
1088
1270
|
createChainContract,
|
|
1089
1271
|
createChainRewardsContract,
|
|
1272
|
+
estimateBlockNumberAt,
|
|
1273
|
+
estimateBlockNumberFromHead,
|
|
1090
1274
|
ethBlockRate,
|
|
1091
1275
|
getDefaultGasConfig,
|
|
1092
1276
|
getEthBlockRateBlocks,
|