@atomiqlabs/chain-starknet 7.0.4 → 7.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/starknet/StarknetInitializer.d.ts +2 -1
- package/dist/starknet/StarknetInitializer.js +6 -1
- package/dist/starknet/chain/StarknetChainInterface.d.ts +3 -2
- package/dist/starknet/chain/StarknetChainInterface.js +2 -1
- package/dist/starknet/contract/modules/StarknetContractEvents.d.ts +3 -3
- package/dist/starknet/events/StarknetChainEvents.js +2 -0
- package/dist/starknet/events/StarknetChainEventsBrowser.d.ts +13 -2
- package/dist/starknet/events/StarknetChainEventsBrowser.js +111 -21
- package/package.json +3 -2
- package/src/starknet/StarknetInitializer.ts +7 -2
- package/src/starknet/chain/StarknetChainInterface.ts +4 -1
- package/src/starknet/contract/modules/StarknetContractEvents.ts +2 -2
- package/src/starknet/events/StarknetChainEvents.ts +1 -0
- package/src/starknet/events/StarknetChainEventsBrowser.ts +152 -23
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { constants, Provider } from "starknet";
|
|
1
|
+
import { constants, Provider, WebSocketChannel } from "starknet";
|
|
2
2
|
import { StarknetFees } from "./chain/modules/StarknetFees";
|
|
3
3
|
import { StarknetConfig, StarknetRetryPolicy } from "./chain/StarknetChainInterface";
|
|
4
4
|
import { BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType } from "@atomiqlabs/base";
|
|
@@ -7,6 +7,7 @@ export type StarknetAssetsType = BaseTokenType<"ETH" | "STRK" | "WBTC" | "TBTC"
|
|
|
7
7
|
export declare const StarknetAssets: StarknetAssetsType;
|
|
8
8
|
export type StarknetOptions = {
|
|
9
9
|
rpcUrl: string | Provider;
|
|
10
|
+
wsUrl?: string | WebSocketChannel;
|
|
10
11
|
retryPolicy?: StarknetRetryPolicy;
|
|
11
12
|
chainId?: constants.StarknetChainId;
|
|
12
13
|
swapContract?: string;
|
|
@@ -42,10 +42,15 @@ function initializeStarknet(options, bitcoinRpc, network) {
|
|
|
42
42
|
const provider = typeof (options.rpcUrl) === "string" ?
|
|
43
43
|
new RpcProviderWithRetries_1.RpcProviderWithRetries({ nodeUrl: options.rpcUrl }) :
|
|
44
44
|
options.rpcUrl;
|
|
45
|
+
let wsChannel;
|
|
46
|
+
if (options.wsUrl != null)
|
|
47
|
+
wsChannel = typeof (options.wsUrl) === "string" ?
|
|
48
|
+
new starknet_1.WebSocketChannel({ nodeUrl: options.wsUrl, websocket: typeof window !== "undefined" && typeof window.WebSocket !== "undefined" ? window.WebSocket : require("ws") }) :
|
|
49
|
+
options.wsUrl;
|
|
45
50
|
const Fees = options.fees ?? new StarknetFees_1.StarknetFees(provider);
|
|
46
51
|
const chainId = options.chainId ??
|
|
47
52
|
(network === base_1.BitcoinNetwork.MAINNET ? starknet_1.constants.StarknetChainId.SN_MAIN : starknet_1.constants.StarknetChainId.SN_SEPOLIA);
|
|
48
|
-
const chainInterface = new StarknetChainInterface_1.StarknetChainInterface(chainId, provider, options.retryPolicy, Fees, options.starknetConfig);
|
|
53
|
+
const chainInterface = new StarknetChainInterface_1.StarknetChainInterface(chainId, provider, wsChannel, options.retryPolicy, Fees, options.starknetConfig);
|
|
49
54
|
const btcRelay = new StarknetBtcRelay_1.StarknetBtcRelay(chainInterface, bitcoinRpc, network, options.btcRelayContract);
|
|
50
55
|
const swapContract = new StarknetSwapContract_1.StarknetSwapContract(chainInterface, btcRelay, options.swapContract, options.handlerContracts);
|
|
51
56
|
const spvVaultContract = new StarknetSpvVaultContract_1.StarknetSpvVaultContract(chainInterface, btcRelay, bitcoinRpc, options.spvVaultContract);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Provider, constants, Account } from "starknet";
|
|
1
|
+
import { Provider, constants, Account, WebSocketChannel } from "starknet";
|
|
2
2
|
import { StarknetTransactions, StarknetTx } from "./modules/StarknetTransactions";
|
|
3
3
|
import { StarknetFees } from "./modules/StarknetFees";
|
|
4
4
|
import { StarknetTokens } from "./modules/StarknetTokens";
|
|
@@ -21,6 +21,7 @@ export type StarknetConfig = {
|
|
|
21
21
|
};
|
|
22
22
|
export declare class StarknetChainInterface implements ChainInterface<StarknetTx, StarknetSigner, "STARKNET", Account> {
|
|
23
23
|
readonly chainId = "STARKNET";
|
|
24
|
+
readonly wsChannel?: WebSocketChannel;
|
|
24
25
|
readonly provider: Provider;
|
|
25
26
|
readonly retryPolicy: StarknetRetryPolicy;
|
|
26
27
|
readonly starknetChainId: constants.StarknetChainId;
|
|
@@ -33,7 +34,7 @@ export declare class StarknetChainInterface implements ChainInterface<StarknetTx
|
|
|
33
34
|
readonly Blocks: StarknetBlocks;
|
|
34
35
|
protected readonly logger: import("../../utils/Utils").LoggerType;
|
|
35
36
|
readonly config: StarknetConfig;
|
|
36
|
-
constructor(chainId: constants.StarknetChainId, provider: Provider, retryPolicy?: StarknetRetryPolicy, feeEstimator?: StarknetFees, options?: StarknetConfig);
|
|
37
|
+
constructor(chainId: constants.StarknetChainId, provider: Provider, wsChannel?: WebSocketChannel, retryPolicy?: StarknetRetryPolicy, feeEstimator?: StarknetFees, options?: StarknetConfig);
|
|
37
38
|
getBalance(signer: string, tokenAddress: string): Promise<bigint>;
|
|
38
39
|
getNativeCurrencyAddress(): string;
|
|
39
40
|
isValidToken(tokenIdentifier: string): boolean;
|
|
@@ -16,7 +16,7 @@ const buffer_1 = require("buffer");
|
|
|
16
16
|
const StarknetKeypairWallet_1 = require("../wallet/accounts/StarknetKeypairWallet");
|
|
17
17
|
const StarknetBrowserSigner_1 = require("../wallet/StarknetBrowserSigner");
|
|
18
18
|
class StarknetChainInterface {
|
|
19
|
-
constructor(chainId, provider, retryPolicy, feeEstimator = new StarknetFees_1.StarknetFees(provider), options) {
|
|
19
|
+
constructor(chainId, provider, wsChannel, retryPolicy, feeEstimator = new StarknetFees_1.StarknetFees(provider), options) {
|
|
20
20
|
var _a, _b, _c, _d;
|
|
21
21
|
this.chainId = "STARKNET";
|
|
22
22
|
this.logger = (0, Utils_1.getLogger)("StarknetChainInterface: ");
|
|
@@ -28,6 +28,7 @@ class StarknetChainInterface {
|
|
|
28
28
|
(_b = this.config).getLogChunkSize ?? (_b.getLogChunkSize = 100);
|
|
29
29
|
(_c = this.config).maxGetLogKeys ?? (_c.maxGetLogKeys = 64);
|
|
30
30
|
(_d = this.config).maxParallelCalls ?? (_d.maxParallelCalls = 10);
|
|
31
|
+
this.wsChannel = wsChannel;
|
|
31
32
|
this.Fees = feeEstimator;
|
|
32
33
|
this.Tokens = new StarknetTokens_1.StarknetTokens(this);
|
|
33
34
|
this.Transactions = new StarknetTransactions_1.StarknetTransactions(this);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Abi } from "abi-wan-kanabi";
|
|
2
2
|
import { EventToPrimitiveType, ExtractAbiEventNames } from "abi-wan-kanabi/dist/kanabi";
|
|
3
|
-
import { StarknetEvents } from "../../chain/modules/StarknetEvents";
|
|
3
|
+
import { StarknetEvent, StarknetEvents } from "../../chain/modules/StarknetEvents";
|
|
4
4
|
import { StarknetContractBase } from "../StarknetContractBase";
|
|
5
5
|
import { StarknetChainInterface } from "../../chain/StarknetChainInterface";
|
|
6
6
|
export type StarknetAbiEvent<TAbi extends Abi, TEventName extends ExtractAbiEventNames<TAbi>> = {
|
|
@@ -16,8 +16,8 @@ export declare class StarknetContractEvents<TAbi extends Abi> extends StarknetEv
|
|
|
16
16
|
readonly contract: StarknetContractBase<TAbi>;
|
|
17
17
|
readonly abi: TAbi;
|
|
18
18
|
constructor(chainInterface: StarknetChainInterface, contract: StarknetContractBase<TAbi>, abi: TAbi);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
toStarknetAbiEvents<T extends ExtractAbiEventNames<TAbi>>(blockEvents: StarknetEvent[]): StarknetAbiEvent<TAbi, T>[];
|
|
20
|
+
toFilter<T extends ExtractAbiEventNames<TAbi>>(events: T[], keys: (string | string[])[]): string[][];
|
|
21
21
|
/**
|
|
22
22
|
* Returns the events occuring in a range of starknet block as identified by the contract and keys,
|
|
23
23
|
* returns pending events if no startHeight & endHeight is passed
|
|
@@ -45,6 +45,8 @@ class StarknetChainEvents extends StarknetChainEventsBrowser_1.StarknetChainEven
|
|
|
45
45
|
}
|
|
46
46
|
async init() {
|
|
47
47
|
const lastEventsState = await this.getLastEventData();
|
|
48
|
+
if (this.wsChannel != null)
|
|
49
|
+
await this.setupWebsocket();
|
|
48
50
|
await this.setupPoll(lastEventsState, (newState) => this.saveLastEventData(newState));
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChainEvents, ClaimEvent, EventListener, InitializeEvent, RefundEvent, SpvVaultClaimEvent, SpvVaultCloseEvent, SpvVaultDepositEvent, SpvVaultFrontEvent, SpvVaultOpenEvent } from "@atomiqlabs/base";
|
|
2
2
|
import { StarknetSwapData } from "../swaps/StarknetSwapData";
|
|
3
3
|
import { StarknetSwapContract } from "../swaps/StarknetSwapContract";
|
|
4
|
-
import { BigNumberish, Provider } from "starknet";
|
|
4
|
+
import { BigNumberish, Provider, SubscriptionStarknetEventsEvent, WebSocketChannel } from "starknet";
|
|
5
5
|
import { StarknetAbiEvent } from "../contract/modules/StarknetContractEvents";
|
|
6
6
|
import { EscrowManagerAbiType } from "../swaps/EscrowManagerAbi";
|
|
7
7
|
import { ExtractAbiFunctionNames } from "abi-wan-kanabi/dist/kanabi";
|
|
@@ -25,17 +25,26 @@ export type StarknetEventListenerState = {
|
|
|
25
25
|
* rely purely on events
|
|
26
26
|
*/
|
|
27
27
|
export declare class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData> {
|
|
28
|
+
private eventsProcessing;
|
|
29
|
+
private processedEvents;
|
|
30
|
+
protected readonly Chain: StarknetChainInterface;
|
|
28
31
|
protected readonly listeners: EventListener<StarknetSwapData>[];
|
|
32
|
+
protected readonly wsChannel?: WebSocketChannel;
|
|
29
33
|
protected readonly provider: Provider;
|
|
30
34
|
protected readonly starknetSwapContract: StarknetSwapContract;
|
|
31
35
|
protected readonly starknetSpvVaultContract: StarknetSpvVaultContract;
|
|
32
36
|
protected readonly logger: import("../../utils/Utils").LoggerType;
|
|
37
|
+
protected escrowContractSubscription: SubscriptionStarknetEventsEvent;
|
|
38
|
+
protected spvVaultContractSubscription: SubscriptionStarknetEventsEvent;
|
|
33
39
|
protected initFunctionName: ExtractAbiFunctionNames<EscrowManagerAbiType>;
|
|
34
40
|
protected initEntryPointSelector: bigint;
|
|
35
41
|
protected stopped: boolean;
|
|
36
42
|
protected pollIntervalSeconds: number;
|
|
37
43
|
private timeout;
|
|
38
44
|
constructor(chainInterface: StarknetChainInterface, starknetSwapContract: StarknetSwapContract, starknetSpvVaultContract: StarknetSpvVaultContract, pollIntervalSeconds?: number);
|
|
45
|
+
private getEventFingerprint;
|
|
46
|
+
private addProcessedEvent;
|
|
47
|
+
private isEventProcessed;
|
|
39
48
|
findInitSwapData(call: StarknetTraceCall, escrowHash: BigNumberish, claimHandler: IClaimHandler<any, any>): StarknetSwapData;
|
|
40
49
|
/**
|
|
41
50
|
* Returns async getter for fetching on-demand initialize event swap data
|
|
@@ -62,7 +71,7 @@ export declare class StarknetChainEventsBrowser implements ChainEvents<StarknetS
|
|
|
62
71
|
* @param currentBlockTimestamp
|
|
63
72
|
* @protected
|
|
64
73
|
*/
|
|
65
|
-
protected processEvents(events: (StarknetAbiEvent<EscrowManagerAbiType, "escrow_manager::events::Initialize" | "escrow_manager::events::Refund" | "escrow_manager::events::Claim"> | StarknetAbiEvent<SpvVaultContractAbiType, "spv_swap_vault::events::Opened" | "spv_swap_vault::events::Deposited" | "spv_swap_vault::events::Fronted" | "spv_swap_vault::events::Claimed" | "spv_swap_vault::events::Closed">)[], currentBlockNumber: number, currentBlockTimestamp
|
|
74
|
+
protected processEvents(events: (StarknetAbiEvent<EscrowManagerAbiType, "escrow_manager::events::Initialize" | "escrow_manager::events::Refund" | "escrow_manager::events::Claim"> | StarknetAbiEvent<SpvVaultContractAbiType, "spv_swap_vault::events::Opened" | "spv_swap_vault::events::Deposited" | "spv_swap_vault::events::Fronted" | "spv_swap_vault::events::Claimed" | "spv_swap_vault::events::Closed">)[], currentBlockNumber: number, currentBlockTimestamp?: number): Promise<void>;
|
|
66
75
|
protected checkEventsEcrowManager(currentBlock: {
|
|
67
76
|
timestamp: number;
|
|
68
77
|
block_number: number;
|
|
@@ -78,6 +87,8 @@ export declare class StarknetChainEventsBrowser implements ChainEvents<StarknetS
|
|
|
78
87
|
* @protected
|
|
79
88
|
*/
|
|
80
89
|
protected setupPoll(lastState?: StarknetEventListenerState[], saveLatestProcessedBlockNumber?: (newState: StarknetEventListenerState[]) => Promise<void>): Promise<void>;
|
|
90
|
+
protected wsStarted: boolean;
|
|
91
|
+
protected setupWebsocket(): Promise<void>;
|
|
81
92
|
init(): Promise<void>;
|
|
82
93
|
stop(): Promise<void>;
|
|
83
94
|
registerListener(cbk: EventListener<StarknetSwapData>): void;
|
|
@@ -4,6 +4,9 @@ exports.StarknetChainEventsBrowser = void 0;
|
|
|
4
4
|
const base_1 = require("@atomiqlabs/base");
|
|
5
5
|
const Utils_1 = require("../../utils/Utils");
|
|
6
6
|
const starknet_1 = require("starknet");
|
|
7
|
+
const sha2_1 = require("@noble/hashes/sha2");
|
|
8
|
+
const buffer_1 = require("buffer");
|
|
9
|
+
const PROCESSED_EVENTS_BACKLOG = 5000;
|
|
7
10
|
const LOGS_SLIDING_WINDOW = 60;
|
|
8
11
|
/**
|
|
9
12
|
* Starknet on-chain event handler for front-end systems without access to fs, uses WS or long-polling to subscribe, might lose
|
|
@@ -12,15 +15,37 @@ const LOGS_SLIDING_WINDOW = 60;
|
|
|
12
15
|
*/
|
|
13
16
|
class StarknetChainEventsBrowser {
|
|
14
17
|
constructor(chainInterface, starknetSwapContract, starknetSpvVaultContract, pollIntervalSeconds = 5) {
|
|
18
|
+
this.eventsProcessing = {};
|
|
19
|
+
this.processedEvents = new Set();
|
|
15
20
|
this.listeners = [];
|
|
16
21
|
this.logger = (0, Utils_1.getLogger)("StarknetChainEventsBrowser: ");
|
|
17
22
|
this.initFunctionName = "initialize";
|
|
18
23
|
this.initEntryPointSelector = BigInt(starknet_1.hash.starknetKeccak(this.initFunctionName));
|
|
24
|
+
this.wsStarted = false;
|
|
25
|
+
this.Chain = chainInterface;
|
|
26
|
+
this.wsChannel = chainInterface.wsChannel;
|
|
19
27
|
this.provider = chainInterface.provider;
|
|
20
28
|
this.starknetSwapContract = starknetSwapContract;
|
|
21
29
|
this.starknetSpvVaultContract = starknetSpvVaultContract;
|
|
22
30
|
this.pollIntervalSeconds = pollIntervalSeconds;
|
|
23
31
|
}
|
|
32
|
+
getEventFingerprint(event) {
|
|
33
|
+
const eventData = buffer_1.Buffer.concat([
|
|
34
|
+
...event.keys.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 64)),
|
|
35
|
+
...event.data.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 64))
|
|
36
|
+
]);
|
|
37
|
+
const fingerprint = buffer_1.Buffer.from((0, sha2_1.sha256)(eventData));
|
|
38
|
+
return event.txHash + ":" + fingerprint.toString("hex");
|
|
39
|
+
}
|
|
40
|
+
addProcessedEvent(event) {
|
|
41
|
+
this.processedEvents.add(this.getEventFingerprint(event));
|
|
42
|
+
if (this.processedEvents.size > PROCESSED_EVENTS_BACKLOG)
|
|
43
|
+
this.processedEvents.delete(this.processedEvents.keys().next().value);
|
|
44
|
+
}
|
|
45
|
+
isEventProcessed(eventOrFingerprint) {
|
|
46
|
+
const eventFingerprint = typeof (eventOrFingerprint) === "string" ? eventOrFingerprint : this.getEventFingerprint(eventOrFingerprint);
|
|
47
|
+
return this.processedEvents.has(eventFingerprint);
|
|
48
|
+
}
|
|
24
49
|
findInitSwapData(call, escrowHash, claimHandler) {
|
|
25
50
|
if (BigInt(call.contract_address) === BigInt(this.starknetSwapContract.contract.address) &&
|
|
26
51
|
BigInt(call.entry_point_selector) === this.initEntryPointSelector) {
|
|
@@ -165,11 +190,15 @@ class StarknetChainEventsBrowser {
|
|
|
165
190
|
if (blockNumber === currentBlockNumber)
|
|
166
191
|
return currentBlockTimestamp;
|
|
167
192
|
const blockNumberString = blockNumber.toString();
|
|
168
|
-
blockTimestampsCache[blockNumberString] ?? (blockTimestampsCache[blockNumberString] =
|
|
193
|
+
blockTimestampsCache[blockNumberString] ?? (blockTimestampsCache[blockNumberString] = await this.Chain.Blocks.getBlockTime(blockNumber));
|
|
169
194
|
return blockTimestampsCache[blockNumberString];
|
|
170
195
|
};
|
|
171
|
-
const parsedEvents = [];
|
|
172
196
|
for (let event of events) {
|
|
197
|
+
const eventIdentifier = this.getEventFingerprint(event);
|
|
198
|
+
if (this.isEventProcessed(eventIdentifier)) {
|
|
199
|
+
this.logger.debug("processEvents(): skipping already processed event: " + eventIdentifier);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
173
202
|
let parsedEvent;
|
|
174
203
|
switch (event.name) {
|
|
175
204
|
case "escrow_manager::events::Claim":
|
|
@@ -197,21 +226,37 @@ class StarknetChainEventsBrowser {
|
|
|
197
226
|
parsedEvent = this.parseSpvCloseEvent(event);
|
|
198
227
|
break;
|
|
199
228
|
}
|
|
200
|
-
if (
|
|
229
|
+
if (this.eventsProcessing[eventIdentifier] != null) {
|
|
230
|
+
this.logger.debug("processEvents(): awaiting event that is currently processing: " + eventIdentifier);
|
|
231
|
+
await this.eventsProcessing[eventIdentifier];
|
|
201
232
|
continue;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
timestamp
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
233
|
+
}
|
|
234
|
+
const promise = (async () => {
|
|
235
|
+
if (parsedEvent == null)
|
|
236
|
+
return;
|
|
237
|
+
//We are not trusting pre-confs for events, so this shall never happen
|
|
238
|
+
if (event.blockNumber == null)
|
|
239
|
+
throw new Error("Event block number cannot be null!");
|
|
240
|
+
const timestamp = await getBlockTimestamp(event.blockNumber);
|
|
241
|
+
parsedEvent.meta = {
|
|
242
|
+
blockTime: timestamp,
|
|
243
|
+
txId: event.txHash,
|
|
244
|
+
timestamp //Maybe deprecated
|
|
245
|
+
};
|
|
246
|
+
for (let listener of this.listeners) {
|
|
247
|
+
await listener([parsedEvent]);
|
|
248
|
+
}
|
|
249
|
+
this.addProcessedEvent(event);
|
|
250
|
+
})();
|
|
251
|
+
this.eventsProcessing[eventIdentifier] = promise;
|
|
252
|
+
try {
|
|
253
|
+
await promise;
|
|
254
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
255
|
+
}
|
|
256
|
+
catch (e) {
|
|
257
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
258
|
+
throw e;
|
|
259
|
+
}
|
|
215
260
|
}
|
|
216
261
|
}
|
|
217
262
|
async checkEventsEcrowManager(currentBlock, lastTxHash, lastBlockNumber) {
|
|
@@ -274,7 +319,7 @@ class StarknetChainEventsBrowser {
|
|
|
274
319
|
}
|
|
275
320
|
async checkEvents(lastState) {
|
|
276
321
|
lastState ?? (lastState = []);
|
|
277
|
-
const currentBlock = await this.
|
|
322
|
+
const currentBlock = await this.Chain.Blocks.getBlock(starknet_1.BlockTag.LATEST);
|
|
278
323
|
const [lastEscrowTxHash, lastEscrowHeight] = await this.checkEventsEcrowManager(currentBlock, lastState?.[0]?.lastTxHash, lastState?.[0]?.lastBlockNumber);
|
|
279
324
|
const [lastSpvVaultTxHash, lastSpvVaultHeight] = await this.checkEventsSpvVaults(currentBlock, lastState?.[1]?.lastTxHash, lastState?.[1]?.lastBlockNumber);
|
|
280
325
|
return [
|
|
@@ -288,7 +333,6 @@ class StarknetChainEventsBrowser {
|
|
|
288
333
|
* @protected
|
|
289
334
|
*/
|
|
290
335
|
async setupPoll(lastState, saveLatestProcessedBlockNumber) {
|
|
291
|
-
this.stopped = false;
|
|
292
336
|
let func;
|
|
293
337
|
func = async () => {
|
|
294
338
|
await this.checkEvents(lastState).then(newState => {
|
|
@@ -304,14 +348,60 @@ class StarknetChainEventsBrowser {
|
|
|
304
348
|
};
|
|
305
349
|
await func();
|
|
306
350
|
}
|
|
307
|
-
|
|
308
|
-
this.
|
|
309
|
-
|
|
351
|
+
async setupWebsocket() {
|
|
352
|
+
this.wsStarted = true;
|
|
353
|
+
this.wsChannel.on("open", () => {
|
|
354
|
+
this.logger.info("setupWebsocket(): Websocket connection opened!");
|
|
355
|
+
});
|
|
356
|
+
this.wsChannel.on("close", () => {
|
|
357
|
+
this.logger.warn("setupWebsocket(): Websocket connection closed!");
|
|
358
|
+
});
|
|
359
|
+
this.wsChannel.on("error", (err) => {
|
|
360
|
+
this.logger.error("setupWebsocket(): Websocket connection error: ", err);
|
|
361
|
+
});
|
|
362
|
+
await this.wsChannel.waitForConnection();
|
|
363
|
+
this.logger.info("setupWebsocket(): Websocket connection awaited successfully!");
|
|
364
|
+
const [escrowContractSubscription, spvVaultContractSubscription] = await Promise.all([
|
|
365
|
+
this.wsChannel.subscribeEvents({
|
|
366
|
+
fromAddress: this.starknetSwapContract.contract.address,
|
|
367
|
+
keys: this.starknetSwapContract.Events.toFilter(["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"], []),
|
|
368
|
+
finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
|
|
369
|
+
}),
|
|
370
|
+
this.wsChannel.subscribeEvents({
|
|
371
|
+
fromAddress: this.starknetSpvVaultContract.contract.address,
|
|
372
|
+
keys: this.starknetSpvVaultContract.Events.toFilter(["spv_swap_vault::events::Opened", "spv_swap_vault::events::Deposited", "spv_swap_vault::events::Closed", "spv_swap_vault::events::Fronted", "spv_swap_vault::events::Claimed"], []),
|
|
373
|
+
finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
|
|
374
|
+
})
|
|
375
|
+
]);
|
|
376
|
+
escrowContractSubscription.on((event) => {
|
|
377
|
+
const parsedEvents = this.starknetSwapContract.Events.toStarknetAbiEvents([event]);
|
|
378
|
+
this.processEvents(parsedEvents, event.block_number);
|
|
379
|
+
});
|
|
380
|
+
this.escrowContractSubscription = escrowContractSubscription;
|
|
381
|
+
spvVaultContractSubscription.on((event) => {
|
|
382
|
+
const parsedEvents = this.starknetSpvVaultContract.Events.toStarknetAbiEvents([event]);
|
|
383
|
+
this.processEvents(parsedEvents, event.block_number);
|
|
384
|
+
});
|
|
385
|
+
this.spvVaultContractSubscription = spvVaultContractSubscription;
|
|
386
|
+
}
|
|
387
|
+
async init() {
|
|
388
|
+
if (this.wsChannel != null) {
|
|
389
|
+
await this.setupWebsocket();
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
await this.setupPoll();
|
|
393
|
+
}
|
|
394
|
+
this.stopped = false;
|
|
310
395
|
}
|
|
311
396
|
async stop() {
|
|
312
397
|
this.stopped = true;
|
|
313
398
|
if (this.timeout != null)
|
|
314
399
|
clearTimeout(this.timeout);
|
|
400
|
+
if (this.wsStarted) {
|
|
401
|
+
await this.escrowContractSubscription.unsubscribe();
|
|
402
|
+
await this.spvVaultContractSubscription.unsubscribe();
|
|
403
|
+
this.wsStarted = false;
|
|
404
|
+
}
|
|
315
405
|
}
|
|
316
406
|
registerListener(cbk) {
|
|
317
407
|
this.listeners.push(cbk);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-starknet",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.6",
|
|
4
4
|
"description": "Starknet specific base implementation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"@scure/btc-signer": "^1.6.0",
|
|
34
34
|
"abi-wan-kanabi": "2.2.4",
|
|
35
35
|
"buffer": "6.0.3",
|
|
36
|
-
"promise-queue-ts": "^1.0.0"
|
|
36
|
+
"promise-queue-ts": "^1.0.0",
|
|
37
|
+
"ws": "^8.18.3"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
39
40
|
"starknet": "^8.5.0"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {constants, Provider} from "starknet";
|
|
1
|
+
import {constants, Provider, WebSocketChannel} from "starknet";
|
|
2
2
|
import {StarknetFees} from "./chain/modules/StarknetFees";
|
|
3
3
|
import {StarknetChainInterface, StarknetConfig, StarknetRetryPolicy} from "./chain/StarknetChainInterface";
|
|
4
4
|
import {StarknetBtcRelay} from "./btcrelay/StarknetBtcRelay";
|
|
@@ -41,6 +41,7 @@ export const StarknetAssets: StarknetAssetsType = {
|
|
|
41
41
|
|
|
42
42
|
export type StarknetOptions = {
|
|
43
43
|
rpcUrl: string | Provider,
|
|
44
|
+
wsUrl?: string | WebSocketChannel,
|
|
44
45
|
retryPolicy?: StarknetRetryPolicy,
|
|
45
46
|
chainId?: constants.StarknetChainId,
|
|
46
47
|
|
|
@@ -69,13 +70,17 @@ export function initializeStarknet(
|
|
|
69
70
|
const provider = typeof(options.rpcUrl)==="string" ?
|
|
70
71
|
new RpcProviderWithRetries({nodeUrl: options.rpcUrl}) :
|
|
71
72
|
options.rpcUrl;
|
|
73
|
+
let wsChannel: WebSocketChannel;
|
|
74
|
+
if(options.wsUrl!=null) wsChannel = typeof(options.wsUrl)==="string" ?
|
|
75
|
+
new WebSocketChannel({nodeUrl: options.wsUrl, websocket: typeof window !== "undefined" && typeof window.WebSocket !== "undefined" ? window.WebSocket : require("ws")}) :
|
|
76
|
+
options.wsUrl;
|
|
72
77
|
|
|
73
78
|
const Fees = options.fees ?? new StarknetFees(provider);
|
|
74
79
|
|
|
75
80
|
const chainId = options.chainId ??
|
|
76
81
|
(network===BitcoinNetwork.MAINNET ? constants.StarknetChainId.SN_MAIN : constants.StarknetChainId.SN_SEPOLIA);
|
|
77
82
|
|
|
78
|
-
const chainInterface = new StarknetChainInterface(chainId, provider, options.retryPolicy, Fees, options.starknetConfig);
|
|
83
|
+
const chainInterface = new StarknetChainInterface(chainId, provider, wsChannel, options.retryPolicy, Fees, options.starknetConfig);
|
|
79
84
|
|
|
80
85
|
const btcRelay = new StarknetBtcRelay(
|
|
81
86
|
chainInterface, bitcoinRpc, network, options.btcRelayContract
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Provider, constants, stark, ec, Account, provider, wallet} from "starknet";
|
|
1
|
+
import {Provider, constants, stark, ec, Account, provider, wallet, WebSocketChannel} from "starknet";
|
|
2
2
|
import {getLogger, toHex} from "../../utils/Utils";
|
|
3
3
|
import {StarknetTransactions, StarknetTx} from "./modules/StarknetTransactions";
|
|
4
4
|
import {StarknetFees} from "./modules/StarknetFees";
|
|
@@ -32,6 +32,7 @@ export class StarknetChainInterface implements ChainInterface<StarknetTx, Starkn
|
|
|
32
32
|
|
|
33
33
|
readonly chainId = "STARKNET";
|
|
34
34
|
|
|
35
|
+
readonly wsChannel?: WebSocketChannel;
|
|
35
36
|
readonly provider: Provider;
|
|
36
37
|
readonly retryPolicy: StarknetRetryPolicy;
|
|
37
38
|
|
|
@@ -52,6 +53,7 @@ export class StarknetChainInterface implements ChainInterface<StarknetTx, Starkn
|
|
|
52
53
|
constructor(
|
|
53
54
|
chainId: constants.StarknetChainId,
|
|
54
55
|
provider: Provider,
|
|
56
|
+
wsChannel?: WebSocketChannel,
|
|
55
57
|
retryPolicy?: StarknetRetryPolicy,
|
|
56
58
|
feeEstimator: StarknetFees = new StarknetFees(provider),
|
|
57
59
|
options?: StarknetConfig
|
|
@@ -64,6 +66,7 @@ export class StarknetChainInterface implements ChainInterface<StarknetTx, Starkn
|
|
|
64
66
|
this.config.getLogChunkSize ??= 100;
|
|
65
67
|
this.config.maxGetLogKeys ??= 64;
|
|
66
68
|
this.config.maxParallelCalls ??= 10;
|
|
69
|
+
this.wsChannel = wsChannel;
|
|
67
70
|
|
|
68
71
|
this.Fees = feeEstimator;
|
|
69
72
|
this.Tokens = new StarknetTokens(this);
|
|
@@ -27,7 +27,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
|
|
|
27
27
|
this.abi = abi;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
public toStarknetAbiEvents<T extends ExtractAbiEventNames<TAbi>>(blockEvents: StarknetEvent[]): StarknetAbiEvent<TAbi, T>[] {
|
|
31
31
|
const abiEvents = events.getAbiEvents(this.abi);
|
|
32
32
|
const abiStructs = CallData.getAbiStruct(this.abi);
|
|
33
33
|
const abiEnums = CallData.getAbiEnum(this.abi);
|
|
@@ -49,7 +49,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
public toFilter<T extends ExtractAbiEventNames<TAbi>>(
|
|
53
53
|
events: T[],
|
|
54
54
|
keys: (string | string[])[],
|
|
55
55
|
): string[][] {
|
|
@@ -59,6 +59,7 @@ export class StarknetChainEvents extends StarknetChainEventsBrowser {
|
|
|
59
59
|
|
|
60
60
|
async init(): Promise<void> {
|
|
61
61
|
const lastEventsState = await this.getLastEventData();
|
|
62
|
+
if(this.wsChannel!=null) await this.setupWebsocket();
|
|
62
63
|
await this.setupPoll(
|
|
63
64
|
lastEventsState,
|
|
64
65
|
(newState: StarknetEventListenerState[]) => this.saveLastEventData(newState)
|
|
@@ -17,7 +17,15 @@ import {
|
|
|
17
17
|
toHex
|
|
18
18
|
} from "../../utils/Utils";
|
|
19
19
|
import {StarknetSwapContract} from "../swaps/StarknetSwapContract";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
BigNumberish,
|
|
22
|
+
BlockTag,
|
|
23
|
+
hash,
|
|
24
|
+
Provider,
|
|
25
|
+
SubscriptionStarknetEventsEvent,
|
|
26
|
+
TransactionFinalityStatus,
|
|
27
|
+
WebSocketChannel
|
|
28
|
+
} from "starknet";
|
|
21
29
|
import {StarknetAbiEvent} from "../contract/modules/StarknetContractEvents";
|
|
22
30
|
import {EscrowManagerAbiType} from "../swaps/EscrowManagerAbi";
|
|
23
31
|
import {ExtractAbiFunctionNames} from "abi-wan-kanabi/dist/kanabi";
|
|
@@ -25,7 +33,10 @@ import {IClaimHandler} from "../swaps/handlers/claim/ClaimHandlers";
|
|
|
25
33
|
import {StarknetSpvVaultContract} from "../spv_swap/StarknetSpvVaultContract";
|
|
26
34
|
import {StarknetChainInterface} from "../chain/StarknetChainInterface";
|
|
27
35
|
import {SpvVaultContractAbiType} from "../spv_swap/SpvVaultContractAbi";
|
|
36
|
+
import {sha256} from "@noble/hashes/sha2";
|
|
37
|
+
import {Buffer} from "buffer";
|
|
28
38
|
|
|
39
|
+
const PROCESSED_EVENTS_BACKLOG = 5000;
|
|
29
40
|
const LOGS_SLIDING_WINDOW = 60;
|
|
30
41
|
|
|
31
42
|
export type StarknetTraceCall = {
|
|
@@ -44,12 +55,22 @@ export type StarknetEventListenerState = {lastBlockNumber: number, lastTxHash?:
|
|
|
44
55
|
*/
|
|
45
56
|
export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData> {
|
|
46
57
|
|
|
58
|
+
private eventsProcessing: {
|
|
59
|
+
[eventFingerprint: string]: Promise<void>
|
|
60
|
+
} = {};
|
|
61
|
+
private processedEvents: Set<string> = new Set();
|
|
62
|
+
|
|
63
|
+
protected readonly Chain: StarknetChainInterface;
|
|
47
64
|
protected readonly listeners: EventListener<StarknetSwapData>[] = [];
|
|
65
|
+
protected readonly wsChannel?: WebSocketChannel;
|
|
48
66
|
protected readonly provider: Provider;
|
|
49
67
|
protected readonly starknetSwapContract: StarknetSwapContract;
|
|
50
68
|
protected readonly starknetSpvVaultContract: StarknetSpvVaultContract;
|
|
51
69
|
protected readonly logger = getLogger("StarknetChainEventsBrowser: ");
|
|
52
70
|
|
|
71
|
+
protected escrowContractSubscription: SubscriptionStarknetEventsEvent;
|
|
72
|
+
protected spvVaultContractSubscription: SubscriptionStarknetEventsEvent;
|
|
73
|
+
|
|
53
74
|
protected initFunctionName: ExtractAbiFunctionNames<EscrowManagerAbiType> = "initialize";
|
|
54
75
|
protected initEntryPointSelector = BigInt(hash.starknetKeccak(this.initFunctionName));
|
|
55
76
|
|
|
@@ -64,12 +85,34 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
64
85
|
starknetSpvVaultContract: StarknetSpvVaultContract,
|
|
65
86
|
pollIntervalSeconds: number = 5
|
|
66
87
|
) {
|
|
88
|
+
this.Chain = chainInterface;
|
|
89
|
+
this.wsChannel = chainInterface.wsChannel;
|
|
67
90
|
this.provider = chainInterface.provider;
|
|
68
91
|
this.starknetSwapContract = starknetSwapContract;
|
|
69
92
|
this.starknetSpvVaultContract = starknetSpvVaultContract;
|
|
70
93
|
this.pollIntervalSeconds = pollIntervalSeconds;
|
|
71
94
|
}
|
|
72
95
|
|
|
96
|
+
private getEventFingerprint(event: {keys: string[], data: string[], txHash: string}): string {
|
|
97
|
+
const eventData = Buffer.concat([
|
|
98
|
+
...event.keys.map(value => bigNumberishToBuffer(value, 64)),
|
|
99
|
+
...event.data.map(value => bigNumberishToBuffer(value, 64))
|
|
100
|
+
]);
|
|
101
|
+
const fingerprint = Buffer.from(sha256(eventData));
|
|
102
|
+
|
|
103
|
+
return event.txHash+":"+fingerprint.toString("hex");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private addProcessedEvent(event: {keys: string[], data: string[], txHash: string}) {
|
|
107
|
+
this.processedEvents.add(this.getEventFingerprint(event));
|
|
108
|
+
if(this.processedEvents.size > PROCESSED_EVENTS_BACKLOG) this.processedEvents.delete(this.processedEvents.keys().next().value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private isEventProcessed(eventOrFingerprint: {keys: string[], data: string[], txHash: string} | string): boolean {
|
|
112
|
+
const eventFingerprint: string = typeof(eventOrFingerprint)==="string" ? eventOrFingerprint : this.getEventFingerprint(eventOrFingerprint);
|
|
113
|
+
return this.processedEvents.has(eventFingerprint);
|
|
114
|
+
}
|
|
115
|
+
|
|
73
116
|
findInitSwapData(call: StarknetTraceCall, escrowHash: BigNumberish, claimHandler: IClaimHandler<any, any>): StarknetSwapData {
|
|
74
117
|
if(
|
|
75
118
|
BigInt(call.contract_address)===BigInt(this.starknetSwapContract.contract.address) &&
|
|
@@ -253,19 +296,24 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
253
296
|
"spv_swap_vault::events::Opened" | "spv_swap_vault::events::Deposited" | "spv_swap_vault::events::Fronted" | "spv_swap_vault::events::Claimed" | "spv_swap_vault::events::Closed"
|
|
254
297
|
>)[],
|
|
255
298
|
currentBlockNumber: number,
|
|
256
|
-
currentBlockTimestamp
|
|
299
|
+
currentBlockTimestamp?: number
|
|
257
300
|
) {
|
|
258
301
|
const blockTimestampsCache: {[blockNumber: string]: number} = {};
|
|
259
302
|
const getBlockTimestamp: (blockNumber: number) => Promise<number> = async (blockNumber: number)=> {
|
|
260
303
|
if(blockNumber===currentBlockNumber) return currentBlockTimestamp;
|
|
261
304
|
const blockNumberString = blockNumber.toString();
|
|
262
|
-
blockTimestampsCache[blockNumberString] ??=
|
|
305
|
+
blockTimestampsCache[blockNumberString] ??= await this.Chain.Blocks.getBlockTime(blockNumber);
|
|
263
306
|
return blockTimestampsCache[blockNumberString];
|
|
264
307
|
}
|
|
265
308
|
|
|
266
|
-
const parsedEvents: ChainEvent<StarknetSwapData>[] = [];
|
|
267
|
-
|
|
268
309
|
for(let event of events) {
|
|
310
|
+
const eventIdentifier = this.getEventFingerprint(event);
|
|
311
|
+
|
|
312
|
+
if(this.isEventProcessed(eventIdentifier)) {
|
|
313
|
+
this.logger.debug("processEvents(): skipping already processed event: "+eventIdentifier);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
|
|
269
317
|
let parsedEvent: ChainEvent<StarknetSwapData>;
|
|
270
318
|
switch(event.name) {
|
|
271
319
|
case "escrow_manager::events::Claim":
|
|
@@ -293,20 +341,37 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
293
341
|
parsedEvent = this.parseSpvCloseEvent(event as any);
|
|
294
342
|
break;
|
|
295
343
|
}
|
|
296
|
-
if(parsedEvent==null) continue;
|
|
297
|
-
//We are not trusting pre-confs for events, so this shall never happen
|
|
298
|
-
if(event.blockNumber==null) throw new Error("Event block number cannot be null!");
|
|
299
|
-
const timestamp = await getBlockTimestamp(event.blockNumber);
|
|
300
|
-
parsedEvent.meta = {
|
|
301
|
-
blockTime: timestamp,
|
|
302
|
-
txId: event.txHash,
|
|
303
|
-
timestamp //Maybe deprecated
|
|
304
|
-
} as any;
|
|
305
|
-
parsedEvents.push(parsedEvent);
|
|
306
|
-
}
|
|
307
344
|
|
|
308
|
-
|
|
309
|
-
|
|
345
|
+
if(this.eventsProcessing[eventIdentifier]!=null) {
|
|
346
|
+
this.logger.debug("processEvents(): awaiting event that is currently processing: "+eventIdentifier);
|
|
347
|
+
await this.eventsProcessing[eventIdentifier];
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const promise = (async() => {
|
|
352
|
+
if(parsedEvent==null) return;
|
|
353
|
+
//We are not trusting pre-confs for events, so this shall never happen
|
|
354
|
+
if(event.blockNumber==null) throw new Error("Event block number cannot be null!");
|
|
355
|
+
const timestamp = await getBlockTimestamp(event.blockNumber);
|
|
356
|
+
parsedEvent.meta = {
|
|
357
|
+
blockTime: timestamp,
|
|
358
|
+
txId: event.txHash,
|
|
359
|
+
timestamp //Maybe deprecated
|
|
360
|
+
} as any;
|
|
361
|
+
for(let listener of this.listeners) {
|
|
362
|
+
await listener([parsedEvent]);
|
|
363
|
+
}
|
|
364
|
+
this.addProcessedEvent(event);
|
|
365
|
+
})();
|
|
366
|
+
|
|
367
|
+
this.eventsProcessing[eventIdentifier] = promise;
|
|
368
|
+
try {
|
|
369
|
+
await promise;
|
|
370
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
371
|
+
} catch (e) {
|
|
372
|
+
delete this.eventsProcessing[eventIdentifier];
|
|
373
|
+
throw e;
|
|
374
|
+
}
|
|
310
375
|
}
|
|
311
376
|
}
|
|
312
377
|
|
|
@@ -379,7 +444,7 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
379
444
|
protected async checkEvents(lastState: StarknetEventListenerState[]): Promise<StarknetEventListenerState[]> {
|
|
380
445
|
lastState ??= [];
|
|
381
446
|
|
|
382
|
-
const currentBlock = await this.
|
|
447
|
+
const currentBlock = await this.Chain.Blocks.getBlock(BlockTag.LATEST);
|
|
383
448
|
|
|
384
449
|
const [lastEscrowTxHash, lastEscrowHeight] = await this.checkEventsEcrowManager(currentBlock as any, lastState?.[0]?.lastTxHash, lastState?.[0]?.lastBlockNumber);
|
|
385
450
|
const [lastSpvVaultTxHash, lastSpvVaultHeight] = await this.checkEventsSpvVaults(currentBlock as any, lastState?.[1]?.lastTxHash, lastState?.[1]?.lastBlockNumber);
|
|
@@ -399,7 +464,6 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
399
464
|
lastState?: StarknetEventListenerState[],
|
|
400
465
|
saveLatestProcessedBlockNumber?: (newState: StarknetEventListenerState[]) => Promise<void>
|
|
401
466
|
) {
|
|
402
|
-
this.stopped = false;
|
|
403
467
|
let func;
|
|
404
468
|
func = async () => {
|
|
405
469
|
await this.checkEvents(lastState).then(newState => {
|
|
@@ -414,14 +478,79 @@ export class StarknetChainEventsBrowser implements ChainEvents<StarknetSwapData>
|
|
|
414
478
|
await func();
|
|
415
479
|
}
|
|
416
480
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
481
|
+
protected wsStarted: boolean = false;
|
|
482
|
+
|
|
483
|
+
protected async setupWebsocket() {
|
|
484
|
+
this.wsStarted = true;
|
|
485
|
+
|
|
486
|
+
this.wsChannel.on("open", () => {
|
|
487
|
+
this.logger.info("setupWebsocket(): Websocket connection opened!");
|
|
488
|
+
});
|
|
489
|
+
this.wsChannel.on("close", () => {
|
|
490
|
+
this.logger.warn("setupWebsocket(): Websocket connection closed!");
|
|
491
|
+
});
|
|
492
|
+
this.wsChannel.on("error", (err) => {
|
|
493
|
+
this.logger.error("setupWebsocket(): Websocket connection error: ", err);
|
|
494
|
+
});
|
|
495
|
+
await this.wsChannel.waitForConnection();
|
|
496
|
+
this.logger.info("setupWebsocket(): Websocket connection awaited successfully!");
|
|
497
|
+
|
|
498
|
+
const [
|
|
499
|
+
escrowContractSubscription,
|
|
500
|
+
spvVaultContractSubscription
|
|
501
|
+
] = await Promise.all([
|
|
502
|
+
this.wsChannel.subscribeEvents({
|
|
503
|
+
fromAddress: this.starknetSwapContract.contract.address,
|
|
504
|
+
keys: this.starknetSwapContract.Events.toFilter(
|
|
505
|
+
["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"],
|
|
506
|
+
[]
|
|
507
|
+
),
|
|
508
|
+
finalityStatus: TransactionFinalityStatus.ACCEPTED_ON_L2
|
|
509
|
+
}),
|
|
510
|
+
this.wsChannel.subscribeEvents({
|
|
511
|
+
fromAddress: this.starknetSpvVaultContract.contract.address,
|
|
512
|
+
keys: this.starknetSpvVaultContract.Events.toFilter(
|
|
513
|
+
["spv_swap_vault::events::Opened", "spv_swap_vault::events::Deposited", "spv_swap_vault::events::Closed", "spv_swap_vault::events::Fronted", "spv_swap_vault::events::Claimed"],
|
|
514
|
+
[]
|
|
515
|
+
),
|
|
516
|
+
finalityStatus: TransactionFinalityStatus.ACCEPTED_ON_L2
|
|
517
|
+
})
|
|
518
|
+
]);
|
|
519
|
+
|
|
520
|
+
escrowContractSubscription.on((event) => {
|
|
521
|
+
const parsedEvents = this.starknetSwapContract.Events.toStarknetAbiEvents<
|
|
522
|
+
"escrow_manager::events::Initialize" | "escrow_manager::events::Claim" | "escrow_manager::events::Refund"
|
|
523
|
+
>([event]);
|
|
524
|
+
this.processEvents(parsedEvents, event.block_number);
|
|
525
|
+
});
|
|
526
|
+
this.escrowContractSubscription = escrowContractSubscription;
|
|
527
|
+
|
|
528
|
+
spvVaultContractSubscription.on((event) => {
|
|
529
|
+
const parsedEvents = this.starknetSpvVaultContract.Events.toStarknetAbiEvents<
|
|
530
|
+
"spv_swap_vault::events::Opened" | "spv_swap_vault::events::Deposited" | "spv_swap_vault::events::Closed" | "spv_swap_vault::events::Fronted" | "spv_swap_vault::events::Claimed"
|
|
531
|
+
>([event]);
|
|
532
|
+
this.processEvents(parsedEvents, event.block_number);
|
|
533
|
+
});
|
|
534
|
+
this.spvVaultContractSubscription = spvVaultContractSubscription;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async init(): Promise<void> {
|
|
538
|
+
if(this.wsChannel!=null) {
|
|
539
|
+
await this.setupWebsocket();
|
|
540
|
+
} else {
|
|
541
|
+
await this.setupPoll();
|
|
542
|
+
}
|
|
543
|
+
this.stopped = false;
|
|
420
544
|
}
|
|
421
545
|
|
|
422
546
|
async stop(): Promise<void> {
|
|
423
547
|
this.stopped = true;
|
|
424
548
|
if(this.timeout!=null) clearTimeout(this.timeout);
|
|
549
|
+
if(this.wsStarted) {
|
|
550
|
+
await this.escrowContractSubscription.unsubscribe();
|
|
551
|
+
await this.spvVaultContractSubscription.unsubscribe();
|
|
552
|
+
this.wsStarted = false;
|
|
553
|
+
}
|
|
425
554
|
}
|
|
426
555
|
|
|
427
556
|
registerListener(cbk: EventListener<StarknetSwapData>): void {
|