@atomiqlabs/chain-evm 1.1.5 → 1.1.7

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.
@@ -90,7 +90,11 @@ function initializeBotanix(options, bitcoinRpc, network) {
90
90
  maxParallelLogRequests: options?.evmConfig?.maxParallelLogRequests ?? 5,
91
91
  maxParallelCalls: options?.evmConfig?.maxParallelCalls ?? 5,
92
92
  useAccessLists: options?.evmConfig?.useAccessLists,
93
- defaultAccessListAddresses: options?.evmConfig?.defaultAccessListAddresses
93
+ defaultAccessListAddresses: options?.evmConfig?.defaultAccessListAddresses,
94
+ finalityCheckStrategy: {
95
+ type: "timer",
96
+ delayMs: 1000
97
+ }
94
98
  }, options.retryPolicy, Fees);
95
99
  const btcRelay = new EVMBtcRelay_1.EVMBtcRelay(chainInterface, bitcoinRpc, network, options.btcRelayContract ?? defaultContractAddresses.btcRelayContract, options.btcRelayDeploymentHeight ?? defaultContractAddresses.btcRelayDeploymentHeight);
96
100
  const swapContract = new EVMSwapContract_1.EVMSwapContract(chainInterface, btcRelay, options.swapContract ?? defaultContractAddresses.swapContract, {
@@ -22,6 +22,10 @@ export type EVMConfiguration = {
22
22
  maxLogTopics: number;
23
23
  useAccessLists?: boolean;
24
24
  defaultAccessListAddresses?: string[];
25
+ finalityCheckStrategy?: {
26
+ type: "timer" | "blocks";
27
+ delayMs?: number;
28
+ };
25
29
  };
26
30
  export declare class EVMChainInterface<ChainId extends string = string> implements ChainInterface<EVMTx, EVMSigner, ChainId, Signer> {
27
31
  readonly chainId: ChainId;
@@ -14,7 +14,7 @@ const EVMSigner_1 = require("../wallet/EVMSigner");
14
14
  const EVMBrowserSigner_1 = require("../wallet/EVMBrowserSigner");
15
15
  class EVMChainInterface {
16
16
  constructor(chainId, evmChainId, provider, config, retryPolicy, evmFeeEstimator = new EVMFees_1.EVMFees(provider)) {
17
- var _a, _b;
17
+ var _a, _b, _c, _d;
18
18
  this.chainId = chainId;
19
19
  this.evmChainId = evmChainId;
20
20
  this.provider = provider;
@@ -22,6 +22,8 @@ class EVMChainInterface {
22
22
  this.config = config;
23
23
  (_a = this.config).safeBlockTag ?? (_a.safeBlockTag = "safe");
24
24
  (_b = this.config).finalizedBlockTag ?? (_b.finalizedBlockTag = "finalized");
25
+ (_c = this.config).finalityCheckStrategy ?? (_c.finalityCheckStrategy = { type: "timer" });
26
+ (_d = this.config.finalityCheckStrategy).delayMs ?? (_d.delayMs = 1000);
25
27
  this.logger = (0, Utils_1.getLogger)("EVMChainInterface(" + this.evmChainId + "): ");
26
28
  this.Fees = evmFeeEstimator;
27
29
  this.Tokens = new EVMTokens_1.EVMTokens(this);
@@ -38,6 +38,10 @@ export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
38
38
  protected readonly spvVaultContractLogFilter: EventFilter;
39
39
  protected readonly swapContractLogFilter: EventFilter;
40
40
  protected unconfirmedEventQueue: AtomiqTypedEvent[];
41
+ protected confirmedEventQueue: {
42
+ event: AtomiqTypedEvent;
43
+ block: Block;
44
+ }[];
41
45
  constructor(chainInterface: EVMChainInterface, evmSwapContract: EVMSwapContract, evmSpvVaultContract: EVMSpvVaultContract<any>, pollIntervalSeconds?: number);
42
46
  private addProcessedEvent;
43
47
  private isEventProcessed;
@@ -92,8 +96,11 @@ export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
92
96
  protected spvVaultContractListener: (log: Log) => void;
93
97
  protected swapContractListener: (log: Log) => void;
94
98
  protected blockListener: (blockNumber: number) => Promise<void>;
99
+ protected finalityCheckTimer: any;
95
100
  protected wsStarted: boolean;
101
+ protected checkUnconfirmedEventsFinality(): Promise<void>;
96
102
  protected addOrRemoveBlockListener(): Promise<void>;
103
+ protected startFinalityCheckTimer(): Promise<void>;
97
104
  protected setupWebsocket(): Promise<void>;
98
105
  init(): Promise<void>;
99
106
  stop(): Promise<void>;
@@ -21,6 +21,7 @@ class EVMChainEventsBrowser {
21
21
  this.listeners = [];
22
22
  this.logger = (0, Utils_1.getLogger)("EVMChainEventsBrowser: ");
23
23
  this.unconfirmedEventQueue = [];
24
+ this.confirmedEventQueue = [];
24
25
  this.wsStarted = false;
25
26
  this.chainInterface = chainInterface;
26
27
  this.provider = chainInterface.provider;
@@ -209,8 +210,9 @@ class EVMChainEventsBrowser {
209
210
  txId: event.transactionHash,
210
211
  timestamp //Maybe deprecated
211
212
  };
213
+ const eventsArr = [parsedEvent];
212
214
  for (let listener of this.listeners) {
213
- await listener([parsedEvent]);
215
+ await listener(eventsArr);
214
216
  }
215
217
  this.addProcessedEvent(event);
216
218
  })();
@@ -328,44 +330,76 @@ class EVMChainEventsBrowser {
328
330
  this.unconfirmedEventQueue.push(...events);
329
331
  return this.addOrRemoveBlockListener();
330
332
  }
331
- async addOrRemoveBlockListener() {
333
+ async checkUnconfirmedEventsFinality() {
332
334
  if (this.unconfirmedEventQueue.length > 0) {
333
- this.logger.debug(`addOrRemoveBlockListener(): Adding block listener, unconfirmed event count: ${this.unconfirmedEventQueue.length}`);
335
+ const latestSafeBlock = await this.provider.getBlock(this.chainInterface.config.safeBlockTag);
336
+ const events = this.unconfirmedEventQueue.filter(event => {
337
+ return event.blockNumber <= latestSafeBlock.number;
338
+ });
339
+ const blocks = {};
340
+ for (let event of events) {
341
+ const block = blocks[event.blockNumber] ?? (blocks[event.blockNumber] = await this.provider.getBlock(event.blockNumber));
342
+ if (block.hash === event.blockHash) {
343
+ //Valid event
344
+ const index = this.unconfirmedEventQueue.indexOf(event);
345
+ if (index !== -1)
346
+ this.unconfirmedEventQueue.splice(index, 1);
347
+ this.confirmedEventQueue.push({ event, block });
348
+ }
349
+ else {
350
+ //Block hash doesn't match
351
+ }
352
+ }
353
+ }
354
+ for (let confirmedEvent of this.confirmedEventQueue) {
355
+ await this.processEvents([confirmedEvent.event], confirmedEvent.block);
356
+ const index = this.confirmedEventQueue.indexOf(confirmedEvent);
357
+ if (index !== -1)
358
+ this.confirmedEventQueue.splice(index, 1);
359
+ }
360
+ }
361
+ async addOrRemoveBlockListener() {
362
+ if (this.chainInterface.config.finalityCheckStrategy.type !== "blocks")
363
+ return;
364
+ if (this.unconfirmedEventQueue.length > 0 || this.confirmedEventQueue.length > 0) {
365
+ this.logger.debug(`addOrRemoveBlockListener(): Adding block listener, unconfirmed/confirmed event count: ${this.unconfirmedEventQueue.length + this.confirmedEventQueue.length}`);
334
366
  await this.provider.on("block", this.blockListener);
335
367
  }
336
368
  else {
337
- this.logger.debug(`addOrRemoveBlockListener(): Removing block listener, unconfirmed event count: ${this.unconfirmedEventQueue.length}`);
369
+ this.logger.debug(`addOrRemoveBlockListener(): Removing block listener, unconfirmed/confirmed event count: ${this.unconfirmedEventQueue.length + this.confirmedEventQueue.length}`);
338
370
  await this.provider.off("block", this.blockListener);
339
371
  }
340
372
  }
373
+ async startFinalityCheckTimer() {
374
+ let check;
375
+ check = async () => {
376
+ if (!this.wsStarted)
377
+ return;
378
+ if (this.unconfirmedEventQueue.length > 0 || this.confirmedEventQueue.length > 0) {
379
+ try {
380
+ await this.checkUnconfirmedEventsFinality();
381
+ }
382
+ catch (e) {
383
+ this.logger.error(`startFinalityCheckTimer(): Error when checking past events: `, e);
384
+ }
385
+ }
386
+ if (!this.wsStarted)
387
+ return;
388
+ this.finalityCheckTimer = setTimeout(check, this.chainInterface.config.finalityCheckStrategy.delayMs);
389
+ };
390
+ await check();
391
+ }
341
392
  async setupWebsocket() {
342
393
  this.wsStarted = true;
343
394
  let processing = false;
344
395
  this.blockListener = async (blockNumber) => {
345
396
  if (processing)
346
397
  return;
347
- if (this.unconfirmedEventQueue.length === 0)
398
+ if (this.unconfirmedEventQueue.length === 0 && this.confirmedEventQueue.length === 0)
348
399
  return;
349
400
  processing = true;
350
401
  try {
351
- const latestSafeBlock = await this.provider.getBlock(this.chainInterface.config.safeBlockTag);
352
- const events = this.unconfirmedEventQueue.filter(event => {
353
- return event.blockNumber <= latestSafeBlock.number;
354
- });
355
- const blocks = {};
356
- for (let event of events) {
357
- const block = blocks[event.blockNumber] ?? (blocks[event.blockNumber] = await this.provider.getBlock(event.blockNumber));
358
- if (block.hash === event.blockHash) {
359
- //Valid event
360
- await this.processEvents([event], block);
361
- const index = this.unconfirmedEventQueue.indexOf(event);
362
- if (index !== -1)
363
- this.unconfirmedEventQueue.splice(index, 1);
364
- }
365
- else {
366
- //Block hash doesn't match
367
- }
368
- }
402
+ await this.checkUnconfirmedEventsFinality();
369
403
  }
370
404
  catch (e) {
371
405
  this.logger.error(`on('block'): Error when processing new block ${blockNumber}:`, e);
@@ -373,6 +407,10 @@ class EVMChainEventsBrowser {
373
407
  processing = false;
374
408
  await this.addOrRemoveBlockListener();
375
409
  };
410
+ if (this.chainInterface.config.safeBlockTag === "safe" || this.chainInterface.config.safeBlockTag === "finalized") {
411
+ if (this.chainInterface.config.finalityCheckStrategy.type === "timer")
412
+ this.startFinalityCheckTimer();
413
+ }
376
414
  await this.provider.on(this.spvVaultContractLogFilter, this.spvVaultContractListener = (log) => {
377
415
  let events = this.evmSpvVaultContract.Events.toTypedEvents([log]);
378
416
  events = events.filter(val => !val.removed);
@@ -403,6 +441,7 @@ class EVMChainEventsBrowser {
403
441
  await this.provider.off(this.swapContractLogFilter, this.swapContractListener);
404
442
  await this.provider.off("block", this.blockListener);
405
443
  this.wsStarted = false;
444
+ clearTimeout(this.finalityCheckTimer);
406
445
  }
407
446
  }
408
447
  registerListener(cbk) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-evm",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "EVM specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -127,7 +127,11 @@ export function initializeBotanix(
127
127
  maxParallelLogRequests: options?.evmConfig?.maxParallelLogRequests ?? 5,
128
128
  maxParallelCalls: options?.evmConfig?.maxParallelCalls ?? 5,
129
129
  useAccessLists: options?.evmConfig?.useAccessLists,
130
- defaultAccessListAddresses: options?.evmConfig?.defaultAccessListAddresses
130
+ defaultAccessListAddresses: options?.evmConfig?.defaultAccessListAddresses,
131
+ finalityCheckStrategy: {
132
+ type: "timer",
133
+ delayMs: 1000
134
+ }
131
135
  }, options.retryPolicy, Fees);
132
136
 
133
137
  const btcRelay = new EVMBtcRelay(
@@ -35,7 +35,12 @@ export type EVMConfiguration = {
35
35
  maxLogTopics: number,
36
36
 
37
37
  useAccessLists?: boolean,
38
- defaultAccessListAddresses?: string[]
38
+ defaultAccessListAddresses?: string[],
39
+
40
+ finalityCheckStrategy?: {
41
+ type: "timer" | "blocks"
42
+ delayMs?: number
43
+ }
39
44
  };
40
45
 
41
46
  export class EVMChainInterface<ChainId extends string = string> implements ChainInterface<EVMTx, EVMSigner, ChainId, Signer> {
@@ -73,6 +78,8 @@ export class EVMChainInterface<ChainId extends string = string> implements Chain
73
78
  this.config = config;
74
79
  this.config.safeBlockTag ??= "safe";
75
80
  this.config.finalizedBlockTag ??= "finalized";
81
+ this.config.finalityCheckStrategy ??= {type: "timer"};
82
+ this.config.finalityCheckStrategy.delayMs ??= 1000;
76
83
 
77
84
  this.logger = getLogger("EVMChainInterface("+this.evmChainId+"): ");
78
85
 
@@ -60,6 +60,7 @@ export class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
60
60
  protected readonly swapContractLogFilter: EventFilter;
61
61
 
62
62
  protected unconfirmedEventQueue: AtomiqTypedEvent[] = [];
63
+ protected confirmedEventQueue: {event: AtomiqTypedEvent, block: Block}[] = [];
63
64
 
64
65
  constructor(
65
66
  chainInterface: EVMChainInterface,
@@ -304,8 +305,9 @@ export class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
304
305
  txId: event.transactionHash,
305
306
  timestamp //Maybe deprecated
306
307
  } as any;
308
+ const eventsArr = [parsedEvent];
307
309
  for(let listener of this.listeners) {
308
- await listener([parsedEvent]);
310
+ await listener(eventsArr);
309
311
  }
310
312
  this.addProcessedEvent(event);
311
313
  })();
@@ -445,45 +447,76 @@ export class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
445
447
  protected spvVaultContractListener: (log: Log) => void;
446
448
  protected swapContractListener: (log: Log) => void;
447
449
  protected blockListener: (blockNumber: number) => Promise<void>;
450
+ protected finalityCheckTimer: any;
448
451
  protected wsStarted: boolean = false;
449
452
 
450
- protected async addOrRemoveBlockListener() {
453
+ protected async checkUnconfirmedEventsFinality() {
451
454
  if(this.unconfirmedEventQueue.length>0) {
452
- this.logger.debug(`addOrRemoveBlockListener(): Adding block listener, unconfirmed event count: ${this.unconfirmedEventQueue.length}`);
455
+ const latestSafeBlock = await this.provider.getBlock(this.chainInterface.config.safeBlockTag);
456
+
457
+ const events = this.unconfirmedEventQueue.filter(event => {
458
+ return event.blockNumber <= latestSafeBlock.number;
459
+ });
460
+
461
+ const blocks: {[blockNumber: number]: Block} = {};
462
+ for(let event of events) {
463
+ const block = blocks[event.blockNumber] ?? (blocks[event.blockNumber] = await this.provider.getBlock(event.blockNumber));
464
+ if(block.hash===event.blockHash) {
465
+ //Valid event
466
+ const index = this.unconfirmedEventQueue.indexOf(event);
467
+ if(index!==-1) this.unconfirmedEventQueue.splice(index, 1);
468
+ this.confirmedEventQueue.push({event, block});
469
+ } else {
470
+ //Block hash doesn't match
471
+ }
472
+ }
473
+ }
474
+
475
+ for(let confirmedEvent of this.confirmedEventQueue) {
476
+ await this.processEvents([confirmedEvent.event], confirmedEvent.block);
477
+ const index = this.confirmedEventQueue.indexOf(confirmedEvent);
478
+ if(index!==-1) this.confirmedEventQueue.splice(index, 1);
479
+ }
480
+ }
481
+
482
+ protected async addOrRemoveBlockListener() {
483
+ if(this.chainInterface.config.finalityCheckStrategy.type!=="blocks") return;
484
+ if(this.unconfirmedEventQueue.length>0 || this.confirmedEventQueue.length>0) {
485
+ this.logger.debug(`addOrRemoveBlockListener(): Adding block listener, unconfirmed/confirmed event count: ${this.unconfirmedEventQueue.length + this.confirmedEventQueue.length}`);
453
486
  await this.provider.on("block", this.blockListener);
454
487
  } else {
455
- this.logger.debug(`addOrRemoveBlockListener(): Removing block listener, unconfirmed event count: ${this.unconfirmedEventQueue.length}`);
488
+ this.logger.debug(`addOrRemoveBlockListener(): Removing block listener, unconfirmed/confirmed event count: ${this.unconfirmedEventQueue.length + this.confirmedEventQueue.length}`);
456
489
  await this.provider.off("block", this.blockListener);
457
490
  }
458
491
  }
459
492
 
493
+ protected async startFinalityCheckTimer() {
494
+ let check: () => Promise<void>;
495
+ check = async () => {
496
+ if(!this.wsStarted) return;
497
+ if(this.unconfirmedEventQueue.length>0 || this.confirmedEventQueue.length>0) {
498
+ try {
499
+ await this.checkUnconfirmedEventsFinality();
500
+ } catch (e) {
501
+ this.logger.error(`startFinalityCheckTimer(): Error when checking past events: `, e);
502
+ }
503
+ }
504
+ if(!this.wsStarted) return;
505
+ this.finalityCheckTimer = setTimeout(check, this.chainInterface.config.finalityCheckStrategy.delayMs);
506
+ };
507
+ await check();
508
+ }
509
+
460
510
  protected async setupWebsocket() {
461
511
  this.wsStarted = true;
462
512
 
463
513
  let processing = false;
464
514
  this.blockListener = async (blockNumber: number) => {
465
515
  if(processing) return;
466
- if(this.unconfirmedEventQueue.length===0) return;
516
+ if(this.unconfirmedEventQueue.length===0 && this.confirmedEventQueue.length===0) return;
467
517
  processing = true;
468
518
  try {
469
- const latestSafeBlock = await this.provider.getBlock(this.chainInterface.config.safeBlockTag);
470
-
471
- const events = this.unconfirmedEventQueue.filter(event => {
472
- return event.blockNumber <= latestSafeBlock.number;
473
- });
474
-
475
- const blocks: {[blockNumber: number]: Block} = {};
476
- for(let event of events) {
477
- const block = blocks[event.blockNumber] ?? (blocks[event.blockNumber] = await this.provider.getBlock(event.blockNumber));
478
- if(block.hash===event.blockHash) {
479
- //Valid event
480
- await this.processEvents([event], block);
481
- const index = this.unconfirmedEventQueue.indexOf(event);
482
- if(index!==-1) this.unconfirmedEventQueue.splice(index, 1);
483
- } else {
484
- //Block hash doesn't match
485
- }
486
- }
519
+ await this.checkUnconfirmedEventsFinality();
487
520
  } catch (e) {
488
521
  this.logger.error(`on('block'): Error when processing new block ${blockNumber}:`, e);
489
522
  }
@@ -491,6 +524,10 @@ export class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
491
524
  await this.addOrRemoveBlockListener();
492
525
  }
493
526
 
527
+ if(this.chainInterface.config.safeBlockTag==="safe" || this.chainInterface.config.safeBlockTag==="finalized") {
528
+ if(this.chainInterface.config.finalityCheckStrategy.type==="timer") this.startFinalityCheckTimer();
529
+ }
530
+
494
531
  await this.provider.on(this.spvVaultContractLogFilter, this.spvVaultContractListener = (log) => {
495
532
  let events = this.evmSpvVaultContract.Events.toTypedEvents([log]);
496
533
  events = events.filter(val => !val.removed);
@@ -522,6 +559,7 @@ export class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
522
559
  await this.provider.off(this.swapContractLogFilter, this.swapContractListener);
523
560
  await this.provider.off("block", this.blockListener);
524
561
  this.wsStarted = false;
562
+ clearTimeout(this.finalityCheckTimer);
525
563
  }
526
564
  }
527
565