@atomiqlabs/chain-evm 1.0.0-dev.50 → 1.0.0-dev.52
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/chains/botanix/BotanixChainType.d.ts +1 -2
- package/dist/chains/botanix/BotanixInitializer.js +1 -4
- package/dist/chains/citrea/CitreaChainType.d.ts +1 -2
- package/dist/chains/citrea/CitreaInitializer.js +1 -4
- package/dist/evm/ReconnectingWebSocketProvider.d.ts +18 -0
- package/dist/evm/ReconnectingWebSocketProvider.js +65 -0
- package/dist/evm/SocketProvider.d.ts +110 -0
- package/dist/evm/SocketProvider.js +322 -0
- package/dist/evm/WebSocketProviderWithRetries.d.ts +15 -0
- package/dist/evm/WebSocketProviderWithRetries.js +19 -0
- package/dist/evm/events/EVMChainEvents.js +2 -0
- package/dist/evm/events/EVMChainEventsBrowser.d.ts +18 -2
- package/dist/evm/events/EVMChainEventsBrowser.js +118 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +1 -1
- package/src/chains/botanix/BotanixChainType.ts +1 -2
- package/src/chains/botanix/BotanixInitializer.ts +1 -4
- package/src/chains/citrea/CitreaChainType.ts +1 -2
- package/src/chains/citrea/CitreaInitializer.ts +1 -4
- package/src/evm/ReconnectingWebSocketProvider.ts +82 -0
- package/src/evm/SocketProvider.ts +353 -0
- package/src/evm/WebSocketProviderWithRetries.ts +27 -0
- package/src/evm/events/EVMChainEvents.ts +1 -0
- package/src/evm/events/EVMChainEventsBrowser.ts +148 -15
- package/src/index.ts +2 -1
- package/dist/evm/events/EVMChainEventsBrowserWS.d.ts +0 -66
- package/dist/evm/events/EVMChainEventsBrowserWS.js +0 -264
- package/src/evm/events/EVMChainEventsBrowserWS.ts +0 -354
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChainEvents, ClaimEvent, EventListener, InitializeEvent, RefundEvent, SpvVaultClaimEvent, SpvVaultCloseEvent, SpvVaultDepositEvent, SpvVaultFrontEvent, SpvVaultOpenEvent } from "@atomiqlabs/base";
|
|
2
2
|
import { IClaimHandler } from "../swaps/handlers/claim/ClaimHandlers";
|
|
3
3
|
import { EVMSwapData } from "../swaps/EVMSwapData";
|
|
4
|
-
import { Block, JsonRpcApiProvider } from "ethers";
|
|
4
|
+
import { Block, JsonRpcApiProvider, EventFilter, Log } from "ethers";
|
|
5
5
|
import { EVMSwapContract } from "../swaps/EVMSwapContract";
|
|
6
6
|
import { EVMSpvVaultContract } from "../spv_swap/EVMSpvVaultContract";
|
|
7
7
|
import { EVMChainInterface } from "../chain/EVMChainInterface";
|
|
@@ -16,12 +16,16 @@ export type EVMEventListenerState = {
|
|
|
16
16
|
logIndex: number;
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
|
+
type AtomiqTypedEvent = (TypedEventLog<EscrowManager["filters"]["Initialize" | "Refund" | "Claim"]> | TypedEventLog<SpvVaultManager["filters"]["Opened" | "Deposited" | "Fronted" | "Claimed" | "Closed"]>);
|
|
19
20
|
/**
|
|
20
21
|
* EVM on-chain event handler for front-end systems without access to fs, uses WS or long-polling to subscribe, might lose
|
|
21
22
|
* out on some events if the network is unreliable, front-end systems should take this into consideration and not
|
|
22
23
|
* rely purely on events
|
|
23
24
|
*/
|
|
24
25
|
export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
|
|
26
|
+
private eventsProcessing;
|
|
27
|
+
private processedEvents;
|
|
28
|
+
private processedEventsIndex;
|
|
25
29
|
protected readonly listeners: EventListener<EVMSwapData>[];
|
|
26
30
|
protected readonly provider: JsonRpcApiProvider;
|
|
27
31
|
protected readonly chainInterface: EVMChainInterface;
|
|
@@ -31,7 +35,12 @@ export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
|
|
|
31
35
|
protected stopped: boolean;
|
|
32
36
|
protected pollIntervalSeconds: number;
|
|
33
37
|
private timeout;
|
|
38
|
+
protected readonly spvVaultContractLogFilter: EventFilter;
|
|
39
|
+
protected readonly swapContractLogFilter: EventFilter;
|
|
40
|
+
protected unconfirmedEventQueue: AtomiqTypedEvent[];
|
|
34
41
|
constructor(chainInterface: EVMChainInterface, evmSwapContract: EVMSwapContract, evmSpvVaultContract: EVMSpvVaultContract<any>, pollIntervalSeconds?: number);
|
|
42
|
+
private addProcessedEvent;
|
|
43
|
+
private isEventProcessed;
|
|
35
44
|
findInitSwapData(call: EVMTxTrace, escrowHash: string, claimHandler: IClaimHandler<any, any>): EVMSwapData;
|
|
36
45
|
/**
|
|
37
46
|
* Returns async getter for fetching on-demand initialize event swap data
|
|
@@ -57,7 +66,7 @@ export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
|
|
|
57
66
|
* @param currentBlock
|
|
58
67
|
* @protected
|
|
59
68
|
*/
|
|
60
|
-
protected processEvents(events: (TypedEventLog<EscrowManager["filters"]["Initialize" | "Refund" | "Claim"]> | TypedEventLog<SpvVaultManager["filters"]["Opened" | "Deposited" | "Fronted" | "Claimed" | "Closed"]>)[], currentBlock
|
|
69
|
+
protected processEvents(events: (TypedEventLog<EscrowManager["filters"]["Initialize" | "Refund" | "Claim"]> | TypedEventLog<SpvVaultManager["filters"]["Opened" | "Deposited" | "Fronted" | "Claimed" | "Closed"]>)[], currentBlock?: Block): Promise<void>;
|
|
61
70
|
protected checkEventsEcrowManager(currentBlock: Block, lastProcessedEvent?: {
|
|
62
71
|
blockHash: string;
|
|
63
72
|
logIndex: number;
|
|
@@ -79,8 +88,15 @@ export declare class EVMChainEventsBrowser implements ChainEvents<EVMSwapData> {
|
|
|
79
88
|
* @protected
|
|
80
89
|
*/
|
|
81
90
|
protected setupPoll(lastState?: EVMEventListenerState[], saveLatestProcessedBlockNumber?: (newState: EVMEventListenerState[]) => Promise<void>): Promise<void>;
|
|
91
|
+
protected handleWsEvents(events: AtomiqTypedEvent[]): Promise<void>;
|
|
92
|
+
protected spvVaultContractListener: (log: Log) => void;
|
|
93
|
+
protected swapContractListener: (log: Log) => void;
|
|
94
|
+
protected blockListener: (blockNumber: number) => Promise<void>;
|
|
95
|
+
protected wsStarted: boolean;
|
|
96
|
+
protected setupWebsocket(): Promise<void>;
|
|
82
97
|
init(): Promise<void>;
|
|
83
98
|
stop(): Promise<void>;
|
|
84
99
|
registerListener(cbk: EventListener<EVMSwapData>): void;
|
|
85
100
|
unregisterListener(cbk: EventListener<EVMSwapData>): boolean;
|
|
86
101
|
}
|
|
102
|
+
export {};
|
|
@@ -7,6 +7,7 @@ const ethers_1 = require("ethers");
|
|
|
7
7
|
const Utils_1 = require("../../utils/Utils");
|
|
8
8
|
const EVMSpvVaultContract_1 = require("../spv_swap/EVMSpvVaultContract");
|
|
9
9
|
const LOGS_SLIDING_WINDOW_LENGTH = 60;
|
|
10
|
+
const PROCESSED_EVENTS_BACKLOG = 1000;
|
|
10
11
|
/**
|
|
11
12
|
* EVM on-chain event handler for front-end systems without access to fs, uses WS or long-polling to subscribe, might lose
|
|
12
13
|
* out on some events if the network is unreliable, front-end systems should take this into consideration and not
|
|
@@ -14,13 +15,33 @@ const LOGS_SLIDING_WINDOW_LENGTH = 60;
|
|
|
14
15
|
*/
|
|
15
16
|
class EVMChainEventsBrowser {
|
|
16
17
|
constructor(chainInterface, evmSwapContract, evmSpvVaultContract, pollIntervalSeconds = 5) {
|
|
18
|
+
this.eventsProcessing = {};
|
|
19
|
+
this.processedEvents = [];
|
|
20
|
+
this.processedEventsIndex = 0;
|
|
17
21
|
this.listeners = [];
|
|
18
22
|
this.logger = (0, Utils_1.getLogger)("EVMChainEventsBrowser: ");
|
|
23
|
+
this.unconfirmedEventQueue = [];
|
|
24
|
+
this.wsStarted = false;
|
|
19
25
|
this.chainInterface = chainInterface;
|
|
20
26
|
this.provider = chainInterface.provider;
|
|
21
27
|
this.evmSwapContract = evmSwapContract;
|
|
22
28
|
this.evmSpvVaultContract = evmSpvVaultContract;
|
|
23
29
|
this.pollIntervalSeconds = pollIntervalSeconds;
|
|
30
|
+
this.spvVaultContractLogFilter = {
|
|
31
|
+
address: this.evmSpvVaultContract.contractAddress
|
|
32
|
+
};
|
|
33
|
+
this.swapContractLogFilter = {
|
|
34
|
+
address: this.evmSwapContract.contractAddress
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
addProcessedEvent(event) {
|
|
38
|
+
this.processedEvents[this.processedEventsIndex] = event.transactionHash + ":" + event.transactionIndex;
|
|
39
|
+
this.processedEventsIndex += 1;
|
|
40
|
+
if (this.processedEventsIndex >= PROCESSED_EVENTS_BACKLOG)
|
|
41
|
+
this.processedEventsIndex = 0;
|
|
42
|
+
}
|
|
43
|
+
isEventProcessed(event) {
|
|
44
|
+
return this.processedEvents.includes(event.transactionHash + ":" + event.transactionIndex);
|
|
24
45
|
}
|
|
25
46
|
findInitSwapData(call, escrowHash, claimHandler) {
|
|
26
47
|
if (call.to.toLowerCase() === this.evmSwapContract.contractAddress.toLowerCase()) {
|
|
@@ -141,8 +162,12 @@ class EVMChainEventsBrowser {
|
|
|
141
162
|
* @protected
|
|
142
163
|
*/
|
|
143
164
|
async processEvents(events, currentBlock) {
|
|
144
|
-
const parsedEvents = [];
|
|
145
165
|
for (let event of events) {
|
|
166
|
+
const eventIdentifier = event.transactionHash + ":" + event.transactionIndex;
|
|
167
|
+
if (this.isEventProcessed(event)) {
|
|
168
|
+
this.logger.debug("processEvents(): skipping already processed event: " + eventIdentifier);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
146
171
|
let parsedEvent;
|
|
147
172
|
switch (event.eventName) {
|
|
148
173
|
case "Claim":
|
|
@@ -170,16 +195,32 @@ class EVMChainEventsBrowser {
|
|
|
170
195
|
parsedEvent = this.parseSpvCloseEvent(event);
|
|
171
196
|
break;
|
|
172
197
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
if (this.eventsProcessing[eventIdentifier] != null) {
|
|
199
|
+
this.logger.debug("processEvents(): awaiting event that is currently processing: " + eventIdentifier);
|
|
200
|
+
await this.eventsProcessing[eventIdentifier];
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const promise = (async () => {
|
|
204
|
+
const timestamp = event.blockNumber === currentBlock?.number ? currentBlock.timestamp : await this.chainInterface.Blocks.getBlockTime(event.blockNumber);
|
|
205
|
+
parsedEvent.meta = {
|
|
206
|
+
blockTime: timestamp,
|
|
207
|
+
txId: event.transactionHash,
|
|
208
|
+
timestamp //Maybe deprecated
|
|
209
|
+
};
|
|
210
|
+
for (let listener of this.listeners) {
|
|
211
|
+
await listener([parsedEvent]);
|
|
212
|
+
}
|
|
213
|
+
this.addProcessedEvent(event);
|
|
214
|
+
})();
|
|
215
|
+
this.eventsProcessing[eventIdentifier] = promise;
|
|
216
|
+
try {
|
|
217
|
+
await promise;
|
|
218
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
183
224
|
}
|
|
184
225
|
}
|
|
185
226
|
async checkEventsEcrowManager(currentBlock, lastProcessedEvent, lastBlockNumber) {
|
|
@@ -269,8 +310,67 @@ class EVMChainEventsBrowser {
|
|
|
269
310
|
};
|
|
270
311
|
await func();
|
|
271
312
|
}
|
|
313
|
+
//Websocket
|
|
314
|
+
handleWsEvents(events) {
|
|
315
|
+
if (this.chainInterface.config.safeBlockTag === "latest" || this.chainInterface.config.safeBlockTag === "pending") {
|
|
316
|
+
return this.processEvents(events);
|
|
317
|
+
}
|
|
318
|
+
this.unconfirmedEventQueue.push(...events);
|
|
319
|
+
}
|
|
320
|
+
async setupWebsocket() {
|
|
321
|
+
this.wsStarted = true;
|
|
322
|
+
await this.provider.on(this.spvVaultContractLogFilter, this.spvVaultContractListener = (log) => {
|
|
323
|
+
let events = this.evmSpvVaultContract.Events.toTypedEvents([log]);
|
|
324
|
+
events = events.filter(val => !val.removed);
|
|
325
|
+
this.handleWsEvents(events);
|
|
326
|
+
});
|
|
327
|
+
await this.provider.on(this.swapContractLogFilter, this.swapContractListener = (log) => {
|
|
328
|
+
let events = this.evmSwapContract.Events.toTypedEvents([log]);
|
|
329
|
+
events = events.filter(val => !val.removed && (val.eventName === "Initialize" || val.eventName === "Refund" || val.eventName === "Claim"));
|
|
330
|
+
this.handleWsEvents(events);
|
|
331
|
+
});
|
|
332
|
+
const safeBlockTag = this.chainInterface.config.safeBlockTag;
|
|
333
|
+
let processing = false;
|
|
334
|
+
if (safeBlockTag !== "latest" && safeBlockTag !== "pending")
|
|
335
|
+
await this.provider.on("block", this.blockListener = async (blockNumber) => {
|
|
336
|
+
if (processing)
|
|
337
|
+
return;
|
|
338
|
+
processing = true;
|
|
339
|
+
try {
|
|
340
|
+
const latestSafeBlock = await this.provider.getBlock(this.chainInterface.config.safeBlockTag);
|
|
341
|
+
const events = [];
|
|
342
|
+
this.unconfirmedEventQueue = this.unconfirmedEventQueue.filter(event => {
|
|
343
|
+
if (event.blockNumber <= latestSafeBlock.number) {
|
|
344
|
+
events.push(event);
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
return true;
|
|
348
|
+
});
|
|
349
|
+
const blocks = {};
|
|
350
|
+
for (let event of events) {
|
|
351
|
+
const block = blocks[event.blockNumber] ?? (blocks[event.blockNumber] = await this.provider.getBlock(event.blockNumber));
|
|
352
|
+
if (block.hash === event.blockHash) {
|
|
353
|
+
//Valid event
|
|
354
|
+
await this.processEvents([event], block);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
//Block hash doesn't match
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
this.logger.error(`on('block'): Error when processing new block ${blockNumber}:`, e);
|
|
363
|
+
}
|
|
364
|
+
processing = false;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
272
367
|
async init() {
|
|
273
|
-
this.
|
|
368
|
+
if (this.provider.websocket != null) {
|
|
369
|
+
await this.setupWebsocket();
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
await this.setupPoll();
|
|
373
|
+
}
|
|
274
374
|
this.stopped = false;
|
|
275
375
|
return Promise.resolve();
|
|
276
376
|
}
|
|
@@ -278,6 +378,12 @@ class EVMChainEventsBrowser {
|
|
|
278
378
|
this.stopped = true;
|
|
279
379
|
if (this.timeout != null)
|
|
280
380
|
clearTimeout(this.timeout);
|
|
381
|
+
if (this.wsStarted) {
|
|
382
|
+
await this.provider.off(this.spvVaultContractLogFilter, this.spvVaultContractListener);
|
|
383
|
+
await this.provider.off(this.swapContractLogFilter, this.swapContractListener);
|
|
384
|
+
await this.provider.off("block", this.blockListener);
|
|
385
|
+
this.wsStarted = false;
|
|
386
|
+
}
|
|
281
387
|
}
|
|
282
388
|
registerListener(cbk) {
|
|
283
389
|
this.listeners.push(cbk);
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export * from "./evm/contract/modules/EVMContractEvents";
|
|
|
14
14
|
export * from "./evm/contract/EVMContractBase";
|
|
15
15
|
export * from "./evm/contract/EVMContractModule";
|
|
16
16
|
export * from "./evm/events/EVMChainEventsBrowser";
|
|
17
|
-
export * from "./evm/events/EVMChainEventsBrowserWS";
|
|
18
17
|
export * from "./evm/spv_swap/EVMSpvVaultContract";
|
|
19
18
|
export * from "./evm/spv_swap/EVMSpvWithdrawalData";
|
|
20
19
|
export * from "./evm/spv_swap/EVMSpvVaultData";
|
|
@@ -40,3 +39,5 @@ export * from "./chains/citrea/CitreaFees";
|
|
|
40
39
|
export * from "./chains/botanix/BotanixInitializer";
|
|
41
40
|
export * from "./chains/botanix/BotanixChainType";
|
|
42
41
|
export * from "./evm/JsonRpcProviderWithRetries";
|
|
42
|
+
export * from "./evm/WebSocketProviderWithRetries";
|
|
43
|
+
export * from "./evm/ReconnectingWebSocketProvider";
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,6 @@ __exportStar(require("./evm/contract/modules/EVMContractEvents"), exports);
|
|
|
30
30
|
__exportStar(require("./evm/contract/EVMContractBase"), exports);
|
|
31
31
|
__exportStar(require("./evm/contract/EVMContractModule"), exports);
|
|
32
32
|
__exportStar(require("./evm/events/EVMChainEventsBrowser"), exports);
|
|
33
|
-
__exportStar(require("./evm/events/EVMChainEventsBrowserWS"), exports);
|
|
34
33
|
__exportStar(require("./evm/spv_swap/EVMSpvVaultContract"), exports);
|
|
35
34
|
__exportStar(require("./evm/spv_swap/EVMSpvWithdrawalData"), exports);
|
|
36
35
|
__exportStar(require("./evm/spv_swap/EVMSpvVaultData"), exports);
|
|
@@ -56,3 +55,5 @@ __exportStar(require("./chains/citrea/CitreaFees"), exports);
|
|
|
56
55
|
__exportStar(require("./chains/botanix/BotanixInitializer"), exports);
|
|
57
56
|
__exportStar(require("./chains/botanix/BotanixChainType"), exports);
|
|
58
57
|
__exportStar(require("./evm/JsonRpcProviderWithRetries"), exports);
|
|
58
|
+
__exportStar(require("./evm/WebSocketProviderWithRetries"), exports);
|
|
59
|
+
__exportStar(require("./evm/ReconnectingWebSocketProvider"), exports);
|
package/package.json
CHANGED
|
@@ -10,7 +10,6 @@ import { EVMSpvWithdrawalData } from "../../evm/spv_swap/EVMSpvWithdrawalData";
|
|
|
10
10
|
import {EVMSwapContract} from "../../evm/swaps/EVMSwapContract";
|
|
11
11
|
import {EVMBtcRelay} from "../../evm/btcrelay/EVMBtcRelay";
|
|
12
12
|
import {EVMSpvVaultContract} from "../../evm/spv_swap/EVMSpvVaultContract";
|
|
13
|
-
import {EVMChainEventsBrowserWS} from "../../evm/events/EVMChainEventsBrowserWS";
|
|
14
13
|
|
|
15
14
|
export type BotanixChainType = ChainType<
|
|
16
15
|
"BOTANIX",
|
|
@@ -21,7 +20,7 @@ export type BotanixChainType = ChainType<
|
|
|
21
20
|
EVMSwapData,
|
|
22
21
|
EVMSwapContract<"BOTANIX">,
|
|
23
22
|
EVMChainInterface<"BOTANIX", 3636>,
|
|
24
|
-
EVMChainEventsBrowser
|
|
23
|
+
EVMChainEventsBrowser,
|
|
25
24
|
EVMBtcRelay<any>,
|
|
26
25
|
EVMSpvVaultData,
|
|
27
26
|
EVMSpvWithdrawalData,
|
|
@@ -10,7 +10,6 @@ import {EVMSwapData} from "../../evm/swaps/EVMSwapData";
|
|
|
10
10
|
import {EVMSpvVaultData} from "../../evm/spv_swap/EVMSpvVaultData";
|
|
11
11
|
import {EVMSpvWithdrawalData} from "../../evm/spv_swap/EVMSpvWithdrawalData";
|
|
12
12
|
import {BotanixChainType} from "./BotanixChainType";
|
|
13
|
-
import {EVMChainEventsBrowserWS} from "../../evm/events/EVMChainEventsBrowserWS";
|
|
14
13
|
|
|
15
14
|
const BotanixChainIds = {
|
|
16
15
|
MAINNET: null,
|
|
@@ -147,9 +146,7 @@ export function initializeBotanix(
|
|
|
147
146
|
options.spvVaultDeploymentHeight ?? defaultContractAddresses.spvVaultDeploymentHeight
|
|
148
147
|
)
|
|
149
148
|
|
|
150
|
-
const chainEvents = (
|
|
151
|
-
new EVMChainEventsBrowserWS(chainInterface, swapContract, spvVaultContract) :
|
|
152
|
-
new EVMChainEventsBrowser(chainInterface, swapContract, spvVaultContract);
|
|
149
|
+
const chainEvents = new EVMChainEventsBrowser(chainInterface, swapContract, spvVaultContract);
|
|
153
150
|
|
|
154
151
|
return {
|
|
155
152
|
chainId: "BOTANIX",
|
|
@@ -10,7 +10,6 @@ import { EVMSpvWithdrawalData } from "../../evm/spv_swap/EVMSpvWithdrawalData";
|
|
|
10
10
|
import {CitreaSwapContract} from "./CitreaSwapContract";
|
|
11
11
|
import {CitreaBtcRelay} from "./CitreaBtcRelay";
|
|
12
12
|
import {CitreaSpvVaultContract} from "./CitreaSpvVaultContract";
|
|
13
|
-
import {EVMChainEventsBrowserWS} from "../../evm/events/EVMChainEventsBrowserWS";
|
|
14
13
|
|
|
15
14
|
export type CitreaChainType = ChainType<
|
|
16
15
|
"CITREA",
|
|
@@ -21,7 +20,7 @@ export type CitreaChainType = ChainType<
|
|
|
21
20
|
EVMSwapData,
|
|
22
21
|
CitreaSwapContract,
|
|
23
22
|
EVMChainInterface<"CITREA", 5115>,
|
|
24
|
-
EVMChainEventsBrowser
|
|
23
|
+
EVMChainEventsBrowser,
|
|
25
24
|
CitreaBtcRelay<any>,
|
|
26
25
|
EVMSpvVaultData,
|
|
27
26
|
EVMSpvWithdrawalData,
|
|
@@ -11,7 +11,6 @@ import {CitreaBtcRelay} from "./CitreaBtcRelay";
|
|
|
11
11
|
import {CitreaSwapContract} from "./CitreaSwapContract";
|
|
12
12
|
import {CitreaTokens} from "./CitreaTokens";
|
|
13
13
|
import {CitreaSpvVaultContract} from "./CitreaSpvVaultContract";
|
|
14
|
-
import {EVMChainEventsBrowserWS} from "../../evm/events/EVMChainEventsBrowserWS";
|
|
15
14
|
|
|
16
15
|
const CitreaChainIds = {
|
|
17
16
|
MAINNET: null,
|
|
@@ -154,9 +153,7 @@ export function initializeCitrea(
|
|
|
154
153
|
options.spvVaultDeploymentHeight ?? defaultContractAddresses.spvVaultDeploymentHeight
|
|
155
154
|
)
|
|
156
155
|
|
|
157
|
-
const chainEvents = (
|
|
158
|
-
new EVMChainEventsBrowserWS(chainInterface, swapContract, spvVaultContract) :
|
|
159
|
-
new EVMChainEventsBrowser(chainInterface, swapContract, spvVaultContract);
|
|
156
|
+
const chainEvents = new EVMChainEventsBrowser(chainInterface, swapContract, spvVaultContract);
|
|
160
157
|
|
|
161
158
|
return {
|
|
162
159
|
chainId: "CITREA",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {JsonRpcApiProviderOptions} from "ethers";
|
|
2
|
+
import type {Networkish, WebSocketLike} from "ethers";
|
|
3
|
+
import {SocketProvider} from "./SocketProvider";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class ReconnectingWebSocketProvider extends SocketProvider {
|
|
7
|
+
|
|
8
|
+
reconnectSeconds: number = 15;
|
|
9
|
+
pingIntervalSeconds: number = 60;
|
|
10
|
+
|
|
11
|
+
pingInterval: any;
|
|
12
|
+
reconnectTimer: any;
|
|
13
|
+
|
|
14
|
+
url: string;
|
|
15
|
+
websocket: null | WebSocketLike & {onclose?: (...args: any[]) => void, ping?: () => void};
|
|
16
|
+
|
|
17
|
+
constructor(url: string, network?: Networkish, options?: JsonRpcApiProviderOptions) {
|
|
18
|
+
super(network, options);
|
|
19
|
+
this.url = url;
|
|
20
|
+
this.connect();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private connect() {
|
|
24
|
+
this.websocket = new WebSocket(this.url);
|
|
25
|
+
|
|
26
|
+
this.websocket.onopen = () => {
|
|
27
|
+
this._connected();
|
|
28
|
+
this._start();
|
|
29
|
+
|
|
30
|
+
this.pingInterval = setInterval(() => {
|
|
31
|
+
this.websocket.ping();
|
|
32
|
+
}, this.pingIntervalSeconds * 1000);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
this.websocket.onerror = (err) => {
|
|
36
|
+
if(this.destroyed) return;
|
|
37
|
+
this.websocket = null;
|
|
38
|
+
if(this.pingInterval!=null) clearInterval(this.pingInterval);
|
|
39
|
+
|
|
40
|
+
//Fail all in-flight requests
|
|
41
|
+
this._disconnected();
|
|
42
|
+
|
|
43
|
+
console.error(`Websocket connection error retrying in ${this.reconnectSeconds} seconds...`, err);
|
|
44
|
+
this.reconnectTimer = setTimeout(() => this.connect(), this.reconnectSeconds * 1000);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
this.websocket.onmessage = (message: { data: string }) => {
|
|
48
|
+
this._processMessage(message.data);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
this.websocket.onclose = (event) => {
|
|
52
|
+
if(this.destroyed) return;
|
|
53
|
+
this.websocket = null;
|
|
54
|
+
if(this.pingInterval!=null) clearInterval(this.pingInterval);
|
|
55
|
+
|
|
56
|
+
//Fail all in-flight requests
|
|
57
|
+
this._disconnected();
|
|
58
|
+
|
|
59
|
+
console.error(`Websocket connection closed retrying in ${this.reconnectSeconds} seconds...`, event);
|
|
60
|
+
this.reconnectTimer = setTimeout(() => this.connect(), this.reconnectSeconds * 1000);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async _write(message: string): Promise<void> {
|
|
65
|
+
this.websocket.send(message);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async destroy(): Promise<void> {
|
|
69
|
+
if (this.websocket != null) {
|
|
70
|
+
this.websocket.close();
|
|
71
|
+
this.websocket = null;
|
|
72
|
+
}
|
|
73
|
+
if(this.reconnectTimer!=null) {
|
|
74
|
+
clearTimeout(this.reconnectTimer);
|
|
75
|
+
}
|
|
76
|
+
if(this.pingInterval!=null) {
|
|
77
|
+
clearInterval(this.pingInterval);
|
|
78
|
+
}
|
|
79
|
+
super.destroy();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|