@atomiqlabs/chain-starknet 8.0.13 → 8.1.10

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.
Files changed (119) hide show
  1. package/dist/index.d.ts +18 -18
  2. package/dist/index.js +42 -42
  3. package/dist/starknet/StarknetChainType.d.ts +19 -19
  4. package/dist/starknet/StarknetChainType.js +2 -2
  5. package/dist/starknet/StarknetInitializer.d.ts +66 -63
  6. package/dist/starknet/StarknetInitializer.js +101 -101
  7. package/dist/starknet/btcrelay/BtcRelayAbi.d.ts +250 -250
  8. package/dist/starknet/btcrelay/BtcRelayAbi.js +341 -341
  9. package/dist/starknet/btcrelay/StarknetBtcRelay.d.ts +196 -196
  10. package/dist/starknet/btcrelay/StarknetBtcRelay.js +419 -411
  11. package/dist/starknet/btcrelay/headers/StarknetBtcHeader.d.ts +70 -70
  12. package/dist/starknet/btcrelay/headers/StarknetBtcHeader.js +115 -115
  13. package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.d.ts +91 -91
  14. package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.js +155 -155
  15. package/dist/starknet/chain/StarknetAction.d.ts +19 -19
  16. package/dist/starknet/chain/StarknetAction.js +74 -74
  17. package/dist/starknet/chain/StarknetChainInterface.d.ts +142 -143
  18. package/dist/starknet/chain/StarknetChainInterface.js +198 -199
  19. package/dist/starknet/chain/StarknetModule.d.ts +8 -8
  20. package/dist/starknet/chain/StarknetModule.js +12 -12
  21. package/dist/starknet/chain/modules/ERC20Abi.d.ts +755 -755
  22. package/dist/starknet/chain/modules/ERC20Abi.js +1032 -1032
  23. package/dist/starknet/chain/modules/StarknetAccounts.d.ts +6 -6
  24. package/dist/starknet/chain/modules/StarknetAccounts.js +26 -26
  25. package/dist/starknet/chain/modules/StarknetAddresses.d.ts +10 -10
  26. package/dist/starknet/chain/modules/StarknetAddresses.js +27 -27
  27. package/dist/starknet/chain/modules/StarknetBlocks.d.ts +27 -27
  28. package/dist/starknet/chain/modules/StarknetBlocks.js +82 -82
  29. package/dist/starknet/chain/modules/StarknetEvents.d.ts +47 -47
  30. package/dist/starknet/chain/modules/StarknetEvents.js +90 -90
  31. package/dist/starknet/chain/modules/StarknetFees.d.ts +118 -104
  32. package/dist/starknet/chain/modules/StarknetFees.js +150 -146
  33. package/dist/starknet/chain/modules/StarknetSignatures.d.ts +29 -29
  34. package/dist/starknet/chain/modules/StarknetSignatures.js +72 -72
  35. package/dist/starknet/chain/modules/StarknetTokens.d.ts +66 -66
  36. package/dist/starknet/chain/modules/StarknetTokens.js +99 -99
  37. package/dist/starknet/chain/modules/StarknetTransactions.d.ts +122 -115
  38. package/dist/starknet/chain/modules/StarknetTransactions.js +633 -612
  39. package/dist/starknet/contract/StarknetContractBase.d.ts +14 -13
  40. package/dist/starknet/contract/StarknetContractBase.js +21 -20
  41. package/dist/starknet/contract/StarknetContractModule.d.ts +8 -8
  42. package/dist/starknet/contract/StarknetContractModule.js +11 -11
  43. package/dist/starknet/contract/modules/StarknetContractEvents.d.ts +56 -57
  44. package/dist/starknet/contract/modules/StarknetContractEvents.js +111 -111
  45. package/dist/starknet/events/StarknetChainEvents.d.ts +21 -21
  46. package/dist/starknet/events/StarknetChainEvents.js +61 -61
  47. package/dist/starknet/events/StarknetChainEventsBrowser.d.ts +178 -190
  48. package/dist/starknet/events/StarknetChainEventsBrowser.js +523 -582
  49. package/dist/starknet/provider/RpcProviderWithRetries.d.ts +49 -53
  50. package/dist/starknet/provider/RpcProviderWithRetries.js +94 -94
  51. package/dist/starknet/provider/WebSocketChannelWithRetries.d.ts +21 -21
  52. package/dist/starknet/provider/WebSocketChannelWithRetries.js +46 -46
  53. package/dist/starknet/spv_swap/SpvVaultContractAbi.d.ts +488 -488
  54. package/dist/starknet/spv_swap/SpvVaultContractAbi.js +656 -656
  55. package/dist/starknet/spv_swap/StarknetSpvVaultContract.d.ts +225 -219
  56. package/dist/starknet/spv_swap/StarknetSpvVaultContract.js +663 -621
  57. package/dist/starknet/spv_swap/StarknetSpvVaultData.d.ts +108 -108
  58. package/dist/starknet/spv_swap/StarknetSpvVaultData.js +190 -190
  59. package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.d.ts +56 -56
  60. package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.js +103 -103
  61. package/dist/starknet/swaps/EscrowManagerAbi.d.ts +431 -431
  62. package/dist/starknet/swaps/EscrowManagerAbi.js +583 -583
  63. package/dist/starknet/swaps/StarknetSwapContract.d.ts +309 -278
  64. package/dist/starknet/swaps/StarknetSwapContract.js +755 -579
  65. package/dist/starknet/swaps/StarknetSwapData.d.ts +234 -234
  66. package/dist/starknet/swaps/StarknetSwapData.js +474 -474
  67. package/dist/starknet/swaps/StarknetSwapModule.d.ts +10 -10
  68. package/dist/starknet/swaps/StarknetSwapModule.js +12 -12
  69. package/dist/starknet/swaps/handlers/IHandler.d.ts +13 -13
  70. package/dist/starknet/swaps/handlers/IHandler.js +2 -2
  71. package/dist/starknet/swaps/handlers/claim/ClaimHandlers.d.ts +13 -13
  72. package/dist/starknet/swaps/handlers/claim/ClaimHandlers.js +13 -13
  73. package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.d.ts +21 -21
  74. package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.js +44 -44
  75. package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
  76. package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +48 -48
  77. package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
  78. package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +40 -40
  79. package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +20 -20
  80. package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +30 -30
  81. package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +42 -45
  82. package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +50 -54
  83. package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
  84. package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.js +27 -27
  85. package/dist/starknet/swaps/modules/StarknetLpVault.d.ts +67 -67
  86. package/dist/starknet/swaps/modules/StarknetLpVault.js +122 -122
  87. package/dist/starknet/swaps/modules/StarknetSwapClaim.d.ts +52 -52
  88. package/dist/starknet/swaps/modules/StarknetSwapClaim.js +99 -99
  89. package/dist/starknet/swaps/modules/StarknetSwapInit.d.ts +94 -94
  90. package/dist/starknet/swaps/modules/StarknetSwapInit.js +239 -239
  91. package/dist/starknet/swaps/modules/StarknetSwapRefund.d.ts +60 -60
  92. package/dist/starknet/swaps/modules/StarknetSwapRefund.js +126 -126
  93. package/dist/starknet/wallet/StarknetBrowserSigner.d.ts +11 -11
  94. package/dist/starknet/wallet/StarknetBrowserSigner.js +17 -17
  95. package/dist/starknet/wallet/StarknetPersistentSigner.d.ts +76 -76
  96. package/dist/starknet/wallet/StarknetPersistentSigner.js +291 -291
  97. package/dist/starknet/wallet/StarknetSigner.d.ts +72 -72
  98. package/dist/starknet/wallet/StarknetSigner.js +114 -114
  99. package/dist/starknet/wallet/accounts/StarknetKeypairWallet.d.ts +18 -18
  100. package/dist/starknet/wallet/accounts/StarknetKeypairWallet.js +45 -45
  101. package/dist/utils/Utils.d.ts +77 -77
  102. package/dist/utils/Utils.js +304 -303
  103. package/package.json +2 -2
  104. package/src/starknet/StarknetInitializer.ts +6 -3
  105. package/src/starknet/btcrelay/StarknetBtcRelay.ts +19 -6
  106. package/src/starknet/btcrelay/headers/StarknetBtcHeader.ts +7 -7
  107. package/src/starknet/btcrelay/headers/StarknetBtcStoredHeader.ts +6 -6
  108. package/src/starknet/chain/StarknetAction.ts +1 -0
  109. package/src/starknet/chain/StarknetChainInterface.ts +0 -2
  110. package/src/starknet/chain/modules/StarknetFees.ts +15 -2
  111. package/src/starknet/chain/modules/StarknetTransactions.ts +24 -0
  112. package/src/starknet/contract/StarknetContractBase.ts +7 -4
  113. package/src/starknet/contract/StarknetContractModule.ts +1 -1
  114. package/src/starknet/contract/modules/StarknetContractEvents.ts +7 -7
  115. package/src/starknet/events/StarknetChainEventsBrowser.ts +2 -64
  116. package/src/starknet/provider/RpcProviderWithRetries.ts +1 -1
  117. package/src/starknet/spv_swap/StarknetSpvVaultContract.ts +84 -18
  118. package/src/starknet/swaps/StarknetSwapContract.ts +242 -6
  119. package/src/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +0 -4
@@ -1,582 +1,523 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StarknetChainEventsBrowser = void 0;
4
- const base_1 = require("@atomiqlabs/base");
5
- const StarknetSwapData_1 = require("../swaps/StarknetSwapData");
6
- const Utils_1 = require("../../utils/Utils");
7
- const starknet_1 = require("starknet");
8
- const sha2_1 = require("@noble/hashes/sha2");
9
- const buffer_1 = require("buffer");
10
- const PROCESSED_EVENTS_BACKLOG = 5000;
11
- const LOGS_SLIDING_WINDOW = 60;
12
- function parseInitFunctionCalldata(calldata, claimHandler) {
13
- const escrow = StarknetSwapData_1.StarknetSwapData.fromSerializedFeltArray(calldata, claimHandler);
14
- if (calldata.length < 1)
15
- throw new Error("Calldata invalid length");
16
- const signatureLen = Number((0, Utils_1.toBigInt)(calldata.shift()));
17
- if (calldata.length < signatureLen + 2)
18
- throw new Error("Calldata invalid length");
19
- const signature = calldata.splice(0, signatureLen);
20
- const timeout = (0, Utils_1.toBigInt)(calldata.shift());
21
- const extraDataLen = Number((0, Utils_1.toBigInt)(calldata.shift()));
22
- if (calldata.length < extraDataLen)
23
- throw new Error("Calldata invalid length");
24
- const extraData = calldata.splice(0, extraDataLen);
25
- if (calldata.length !== 0)
26
- throw new Error("Calldata not read fully!");
27
- return { escrow, signature, timeout, extraData };
28
- }
29
- /**
30
- * Starknet on-chain event handler for front-end systems without access to fs, uses WS or long-polling to subscribe, might lose
31
- * out on some events if the network is unreliable, front-end systems should take this into consideration and not
32
- * rely purely on events
33
- *
34
- * @category Events
35
- */
36
- class StarknetChainEventsBrowser {
37
- constructor(chainInterface, starknetSwapContract, starknetSpvVaultContract, pollIntervalSeconds = 5) {
38
- this.eventsProcessing = {};
39
- this.processedEvents = new Set();
40
- this.listeners = [];
41
- this.logger = (0, Utils_1.getLogger)("StarknetChainEventsBrowser: ");
42
- this.initFunctionName = "initialize";
43
- this.initEntryPointSelector = BigInt(starknet_1.hash.starknetKeccak(this.initFunctionName));
44
- this.stopped = true;
45
- this.wsStarted = false;
46
- this.Chain = chainInterface;
47
- this.wsChannel = chainInterface.wsChannel;
48
- this.provider = chainInterface.provider;
49
- this.starknetSwapContract = starknetSwapContract;
50
- this.starknetSpvVaultContract = starknetSpvVaultContract;
51
- this.pollIntervalSeconds = pollIntervalSeconds;
52
- }
53
- /**
54
- *
55
- * @param event
56
- * @private
57
- */
58
- getEventFingerprint(event) {
59
- const eventData = buffer_1.Buffer.concat([
60
- ...event.keys.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 32)),
61
- ...event.data.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 32))
62
- ]);
63
- const fingerprint = buffer_1.Buffer.from((0, sha2_1.sha256)(eventData));
64
- return event.txHash + ":" + fingerprint.toString("hex");
65
- }
66
- /**
67
- *
68
- * @param event
69
- * @private
70
- */
71
- addProcessedEvent(event) {
72
- this.processedEvents.add(this.getEventFingerprint(event));
73
- if (this.processedEvents.size > PROCESSED_EVENTS_BACKLOG)
74
- this.processedEvents.delete(this.processedEvents.keys().next().value);
75
- }
76
- /**
77
- *
78
- * @param eventOrFingerprint
79
- * @private
80
- */
81
- isEventProcessed(eventOrFingerprint) {
82
- const eventFingerprint = typeof (eventOrFingerprint) === "string" ? eventOrFingerprint : this.getEventFingerprint(eventOrFingerprint);
83
- return this.processedEvents.has(eventFingerprint);
84
- }
85
- /**
86
- *
87
- * @param call
88
- * @param escrowHash
89
- * @param claimHandler
90
- * @private
91
- */
92
- findInitSwapData(call, escrowHash, claimHandler) {
93
- if (BigInt(call.contract_address) === BigInt(this.starknetSwapContract.contract.address) &&
94
- BigInt(call.entry_point_selector) === this.initEntryPointSelector) {
95
- //Found, check correct escrow hash
96
- const { escrow, extraData } = parseInitFunctionCalldata(call.calldata, claimHandler);
97
- if ("0x" + escrow.getEscrowHash() === (0, Utils_1.toHex)(escrowHash)) {
98
- if (extraData.length !== 0) {
99
- escrow.setExtraData((0, Utils_1.bytes31SpanToBuffer)(extraData, 42).toString("hex"));
100
- }
101
- return escrow;
102
- }
103
- }
104
- for (let _call of call.calls) {
105
- const found = this.findInitSwapData(_call, escrowHash, claimHandler);
106
- if (found != null)
107
- return found;
108
- }
109
- return null;
110
- }
111
- /**
112
- * Returns async getter for fetching on-demand initialize event swap data
113
- *
114
- * @param event
115
- * @param claimHandler
116
- * @private
117
- * @returns {() => Promise<StarknetSwapData>} getter to be passed to InitializeEvent constructor
118
- */
119
- getSwapDataGetter(event, claimHandler) {
120
- return async () => {
121
- let trace;
122
- try {
123
- trace = await this.provider.getTransactionTrace(event.txHash);
124
- }
125
- catch (e) {
126
- this.logger.warn("getSwapDataGetter(): getter: starknet_traceTransaction not supported by the RPC: ", e);
127
- const blockTraces = await this.provider.getBlockTransactionsTraces(event.blockHash);
128
- const foundTrace = blockTraces.find(val => (0, Utils_1.toHex)(val.transaction_hash) === (0, Utils_1.toHex)(event.txHash));
129
- if (foundTrace == null)
130
- throw new Error(`Cannot find ${event.txHash} in the block traces, block: ${event.blockHash}`);
131
- trace = foundTrace.trace_root;
132
- }
133
- if (trace == null)
134
- return null;
135
- if (trace.execute_invocation.revert_reason != null)
136
- return null;
137
- return this.findInitSwapData(trace.execute_invocation, event.params.escrow_hash, claimHandler);
138
- };
139
- }
140
- /**
141
- *
142
- * @param event
143
- * @private
144
- */
145
- parseInitializeEvent(event) {
146
- const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
147
- const escrowHash = escrowHashBuffer.toString("hex");
148
- const claimHandlerHex = (0, Utils_1.toHex)(event.params.claim_handler);
149
- const claimHandler = this.starknetSwapContract.claimHandlersByAddress[claimHandlerHex];
150
- if (claimHandler == null) {
151
- this.logger.warn("parseInitializeEvent(" + escrowHash + "): Unknown claim handler with claim: " + claimHandlerHex);
152
- return null;
153
- }
154
- const swapType = claimHandler.getType();
155
- this.logger.debug("InitializeEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) + " escrowHash: " + escrowHash);
156
- return new base_1.InitializeEvent(escrowHash, swapType, (0, Utils_1.onceAsync)(this.getSwapDataGetter(event, claimHandler)));
157
- }
158
- /**
159
- *
160
- * @param event
161
- * @private
162
- */
163
- parseRefundEvent(event) {
164
- const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
165
- const escrowHash = escrowHashBuffer.toString("hex");
166
- this.logger.debug("RefundEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) + " escrowHash: " + escrowHash);
167
- return new base_1.RefundEvent(escrowHash);
168
- }
169
- /**
170
- *
171
- * @param event
172
- * @private
173
- */
174
- parseClaimEvent(event) {
175
- const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
176
- const escrowHash = escrowHashBuffer.toString("hex");
177
- const claimHandlerHex = (0, Utils_1.toHex)(event.params.claim_handler);
178
- const claimHandler = this.starknetSwapContract.claimHandlersByAddress[claimHandlerHex];
179
- if (claimHandler == null) {
180
- this.logger.warn("parseClaimEvent(" + escrowHash + "): Unknown claim handler with claim: " + claimHandlerHex);
181
- return null;
182
- }
183
- const witnessResult = claimHandler.parseWitnessResult(event.params.witness_result);
184
- this.logger.debug("ClaimEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) +
185
- " witnessResult: " + witnessResult + " escrowHash: " + escrowHash);
186
- return new base_1.ClaimEvent(escrowHash, witnessResult);
187
- }
188
- /**
189
- *
190
- * @param event
191
- * @private
192
- */
193
- parseSpvOpenEvent(event) {
194
- const owner = (0, Utils_1.toHex)(event.params.owner);
195
- const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
196
- const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
197
- const vout = Number((0, Utils_1.toBigInt)(event.params.vout));
198
- this.logger.debug("SpvOpenEvent owner: " + owner + " vaultId: " + vaultId + " utxo: " + btcTxId + ":" + vout);
199
- return new base_1.SpvVaultOpenEvent(owner, vaultId, btcTxId, vout);
200
- }
201
- /**
202
- *
203
- * @param event
204
- * @private
205
- */
206
- parseSpvDepositEvent(event) {
207
- const owner = (0, Utils_1.toHex)(event.params.owner);
208
- const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
209
- const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
210
- const depositCount = Number((0, Utils_1.toBigInt)(event.params.deposit_count));
211
- this.logger.debug("SpvDepositEvent owner: " + owner + " vaultId: " + vaultId + " depositCount: " + depositCount + " amounts: ", amounts);
212
- return new base_1.SpvVaultDepositEvent(owner, vaultId, amounts, depositCount);
213
- }
214
- /**
215
- *
216
- * @param event
217
- * @private
218
- */
219
- parseSpvFrontEvent(event) {
220
- const owner = (0, Utils_1.toHex)(event.params.owner);
221
- const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
222
- const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
223
- const recipient = (0, Utils_1.toHex)(event.params.recipient);
224
- const executionHash = (0, Utils_1.toHex)(event.params.execution_hash);
225
- const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
226
- const frontingAddress = (0, Utils_1.toHex)(event.params.caller);
227
- this.logger.debug("SpvFrontEvent owner: " + owner + " vaultId: " + vaultId + " btcTxId: " + btcTxId +
228
- " recipient: " + recipient + " frontedBy: " + frontingAddress + " amounts: ", amounts);
229
- return new base_1.SpvVaultFrontEvent(owner, vaultId, btcTxId, recipient, executionHash, amounts, frontingAddress);
230
- }
231
- /**
232
- *
233
- * @param event
234
- * @private
235
- */
236
- parseSpvClaimEvent(event) {
237
- const owner = (0, Utils_1.toHex)(event.params.owner);
238
- const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
239
- const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
240
- const recipient = (0, Utils_1.toHex)(event.params.recipient);
241
- const executionHash = (0, Utils_1.toHex)(event.params.execution_hash);
242
- const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
243
- const caller = (0, Utils_1.toHex)(event.params.caller);
244
- const frontingAddress = (0, Utils_1.toHex)(event.params.fronting_address);
245
- const withdrawCount = Number((0, Utils_1.toBigInt)(event.params.withdraw_count));
246
- this.logger.debug("SpvClaimEvent owner: " + owner + " vaultId: " + vaultId + " btcTxId: " + btcTxId + " withdrawCount: " + withdrawCount +
247
- " recipient: " + recipient + " frontedBy: " + frontingAddress + " claimedBy: " + caller + " amounts: ", amounts);
248
- return new base_1.SpvVaultClaimEvent(owner, vaultId, btcTxId, recipient, executionHash, amounts, caller, frontingAddress, withdrawCount);
249
- }
250
- /**
251
- *
252
- * @param event
253
- * @private
254
- */
255
- parseSpvCloseEvent(event) {
256
- const owner = (0, Utils_1.toHex)(event.params.owner);
257
- const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
258
- const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
259
- const error = (0, Utils_1.bigNumberishToBuffer)(event.params.error).toString();
260
- return new base_1.SpvVaultCloseEvent(owner, vaultId, btcTxId, error);
261
- }
262
- /**
263
- * Processes event as received from the chain, parses it & calls event listeners
264
- *
265
- * @param events
266
- * @param currentBlockNumber
267
- * @param currentBlockTimestamp
268
- * @private
269
- */
270
- async processEvents(events, currentBlockNumber, currentBlockTimestamp) {
271
- const blockTimestampsCache = {};
272
- const getBlockTimestamp = async (blockNumber) => {
273
- //Use current timestamp for events without block height (probably pre-confirmed)
274
- if (blockNumber == null)
275
- return Math.floor(Date.now() / 1000);
276
- if (currentBlockTimestamp != null && blockNumber === currentBlockNumber)
277
- return currentBlockTimestamp;
278
- const blockNumberString = blockNumber.toString();
279
- blockTimestampsCache[blockNumberString] ?? (blockTimestampsCache[blockNumberString] = await this.Chain.Blocks.getBlockTime(blockNumber));
280
- return blockTimestampsCache[blockNumberString];
281
- };
282
- for (let event of events) {
283
- const eventIdentifier = this.getEventFingerprint(event);
284
- if (this.isEventProcessed(eventIdentifier)) {
285
- this.logger.debug("processEvents(): skipping already processed event: " + eventIdentifier);
286
- continue;
287
- }
288
- let parsedEvent;
289
- switch (event.name) {
290
- case "escrow_manager::events::Claim":
291
- parsedEvent = this.parseClaimEvent(event);
292
- break;
293
- case "escrow_manager::events::Refund":
294
- parsedEvent = this.parseRefundEvent(event);
295
- break;
296
- case "escrow_manager::events::Initialize":
297
- parsedEvent = this.parseInitializeEvent(event);
298
- break;
299
- case "spv_swap_vault::events::Opened":
300
- parsedEvent = this.parseSpvOpenEvent(event);
301
- break;
302
- case "spv_swap_vault::events::Deposited":
303
- parsedEvent = this.parseSpvDepositEvent(event);
304
- break;
305
- case "spv_swap_vault::events::Fronted":
306
- parsedEvent = this.parseSpvFrontEvent(event);
307
- break;
308
- case "spv_swap_vault::events::Claimed":
309
- parsedEvent = this.parseSpvClaimEvent(event);
310
- break;
311
- case "spv_swap_vault::events::Closed":
312
- parsedEvent = this.parseSpvCloseEvent(event);
313
- break;
314
- }
315
- if (this.eventsProcessing[eventIdentifier] != null) {
316
- this.logger.debug("processEvents(): awaiting event that is currently processing: " + eventIdentifier);
317
- await this.eventsProcessing[eventIdentifier];
318
- continue;
319
- }
320
- const promise = (async () => {
321
- if (parsedEvent == null)
322
- return;
323
- //We are not trusting pre-confs for events, so this shall never happen
324
- if (event.blockNumber == null)
325
- throw new Error("Event block number cannot be null!");
326
- const timestamp = await getBlockTimestamp(event.blockNumber);
327
- parsedEvent.meta = {
328
- blockTime: timestamp,
329
- txId: event.txHash,
330
- timestamp //Maybe deprecated
331
- };
332
- const eventsArr = [parsedEvent];
333
- for (let listener of this.listeners) {
334
- await listener(eventsArr);
335
- }
336
- this.addProcessedEvent(event);
337
- })();
338
- this.eventsProcessing[eventIdentifier] = promise;
339
- try {
340
- await promise;
341
- delete this.eventsProcessing[eventIdentifier];
342
- }
343
- catch (e) {
344
- delete this.eventsProcessing[eventIdentifier];
345
- throw e;
346
- }
347
- }
348
- }
349
- /**
350
- *
351
- * @param currentBlock
352
- * @param lastTxHash
353
- * @param lastBlockNumber
354
- * @private
355
- */
356
- async checkEventsEcrowManager(currentBlock, lastTxHash, lastBlockNumber) {
357
- const currentBlockNumber = currentBlock.block_number;
358
- lastBlockNumber ?? (lastBlockNumber = currentBlockNumber);
359
- if (currentBlockNumber < lastBlockNumber) {
360
- this.logger.warn(`checkEventsEscrowManager(): Sanity check triggered - not processing events, currentBlock: ${currentBlockNumber}, lastBlock: ${lastBlockNumber}`);
361
- return { lastTxHash, lastBlockNumber };
362
- }
363
- // this.logger.debug("checkEvents(EscrowManager): Requesting logs: "+logStartHeight+"...pending");
364
- let events = await this.starknetSwapContract.Events.getContractBlockEvents(["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"], [], lastBlockNumber, null);
365
- if (lastTxHash != null) {
366
- const latestProcessedEventIndex = (0, Utils_1.findLastIndex)(events, val => val.txHash === lastTxHash);
367
- if (latestProcessedEventIndex !== -1) {
368
- events.splice(0, latestProcessedEventIndex + 1);
369
- this.logger.debug("checkEvents(EscrowManager): Splicing processed events, resulting size: " + events.length);
370
- }
371
- }
372
- if (events.length > 0) {
373
- await this.processEvents(events, currentBlock?.block_number, currentBlock?.timestamp);
374
- const lastProcessed = events[events.length - 1];
375
- lastTxHash = lastProcessed.txHash;
376
- const lastProcessedWithBlockHeightIndex = (0, Utils_1.findLastIndex)(events, val => val.blockNumber != null);
377
- if (lastProcessedWithBlockHeightIndex !== -1) {
378
- const lastProcessedWithBlockHeight = events[lastProcessedWithBlockHeightIndex];
379
- if (lastProcessedWithBlockHeight.blockNumber > lastBlockNumber)
380
- lastBlockNumber = lastProcessedWithBlockHeight.blockNumber;
381
- }
382
- }
383
- else if (currentBlockNumber - lastBlockNumber > LOGS_SLIDING_WINDOW) {
384
- lastTxHash = undefined;
385
- lastBlockNumber = currentBlockNumber - LOGS_SLIDING_WINDOW;
386
- }
387
- return { lastTxHash, lastBlockNumber };
388
- }
389
- async checkEventsSpvVaults(currentBlock, lastTxHash, lastBlockNumber) {
390
- const currentBlockNumber = currentBlock.block_number;
391
- lastBlockNumber ?? (lastBlockNumber = currentBlockNumber);
392
- if (currentBlockNumber < lastBlockNumber) {
393
- this.logger.warn(`checkEventsSpvVaults(): Sanity check triggered - not processing events, currentBlock: ${currentBlockNumber}, lastBlock: ${lastBlockNumber}`);
394
- return { lastTxHash, lastBlockNumber };
395
- }
396
- // this.logger.debug("checkEvents(SpvVaults): Requesting logs: "+logStartHeight+"...pending");
397
- let events = await this.starknetSpvVaultContract.Events.getContractBlockEvents(["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"], [], lastBlockNumber, null);
398
- if (lastTxHash != null) {
399
- const latestProcessedEventIndex = (0, Utils_1.findLastIndex)(events, val => val.txHash === lastTxHash);
400
- if (latestProcessedEventIndex !== -1) {
401
- events.splice(0, latestProcessedEventIndex + 1);
402
- this.logger.debug("checkEvents(SpvVaults): Splicing processed events, resulting size: " + events.length);
403
- }
404
- }
405
- if (events.length > 0) {
406
- await this.processEvents(events, currentBlock?.block_number, currentBlock?.timestamp);
407
- const lastProcessed = events[events.length - 1];
408
- lastTxHash = lastProcessed.txHash;
409
- const lastProcessedWithBlockHeightIndex = (0, Utils_1.findLastIndex)(events, val => val.blockNumber != null);
410
- if (lastProcessedWithBlockHeightIndex !== -1) {
411
- const lastProcessedWithBlockHeight = events[lastProcessedWithBlockHeightIndex];
412
- if (lastProcessedWithBlockHeight.blockNumber > lastBlockNumber)
413
- lastBlockNumber = lastProcessedWithBlockHeight.blockNumber;
414
- }
415
- }
416
- else if (currentBlockNumber - lastBlockNumber > LOGS_SLIDING_WINDOW) {
417
- lastTxHash = undefined;
418
- lastBlockNumber = currentBlockNumber - LOGS_SLIDING_WINDOW;
419
- }
420
- return { lastTxHash, lastBlockNumber };
421
- }
422
- /**
423
- * @inheritDoc
424
- */
425
- async poll(lastState) {
426
- lastState ?? (lastState = []);
427
- const currentBlock = await this.Chain.Blocks.getBlock(starknet_1.BlockTag.LATEST);
428
- const resultEscrow = await this.checkEventsEcrowManager(currentBlock, lastState?.[0]?.lastTxHash, lastState?.[0]?.lastBlockNumber);
429
- const resultSpvVault = await this.checkEventsSpvVaults(currentBlock, lastState?.[1]?.lastTxHash, lastState?.[1]?.lastBlockNumber);
430
- return [
431
- resultEscrow,
432
- resultSpvVault
433
- ];
434
- }
435
- /**
436
- * Sets up event handlers listening for swap events over websocket
437
- *
438
- * @protected
439
- */
440
- async setupPoll(lastState, saveLatestProcessedBlockNumber) {
441
- let func;
442
- func = async () => {
443
- await this.poll(lastState).then(newState => {
444
- lastState = newState;
445
- if (saveLatestProcessedBlockNumber != null)
446
- return saveLatestProcessedBlockNumber(newState);
447
- }).catch(e => {
448
- this.logger.error("setupPoll(): Failed to fetch starknet log: ", e);
449
- });
450
- if (this.stopped)
451
- return;
452
- this.timeout = setTimeout(func, this.pollIntervalSeconds * 1000);
453
- };
454
- await func();
455
- }
456
- /**
457
- *
458
- * @private
459
- */
460
- async subscribeWsEscrowEvents() {
461
- let subscription;
462
- do {
463
- try {
464
- subscription = await this.wsChannel.subscribeEvents({
465
- fromAddress: this.starknetSwapContract.contract.address,
466
- keys: this.starknetSwapContract.Events.toFilter(["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"], []),
467
- finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
468
- });
469
- }
470
- catch (e) {
471
- this.logger.error("subscribeWsEscrowEvents(): Failed to subscribe to escrow events, retrying in 10 seconds...");
472
- await new Promise(resolve => setTimeout(resolve, 10 * 1000));
473
- }
474
- } while (subscription == null);
475
- subscription.on((event) => {
476
- const parsedEvents = this.starknetSwapContract.Events.toStarknetAbiEvents([event]);
477
- this.processEvents(parsedEvents, event.block_number).catch(e => {
478
- console.error(`WS: EscrowContract: Failed to process event ${parsedEvents[0].txHash}:${parsedEvents[0].name}: `, e);
479
- });
480
- });
481
- this.escrowContractSubscription = subscription;
482
- this.logger.debug("subscribeWsEscrowEvents(): Successfully subscribed to escrow contract WS events");
483
- }
484
- /**
485
- *
486
- * @private
487
- */
488
- async subscribeWsSpvVaultEvents() {
489
- let subscription;
490
- do {
491
- try {
492
- subscription = await this.wsChannel.subscribeEvents({
493
- fromAddress: this.starknetSpvVaultContract.contract.address,
494
- 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"], []),
495
- finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
496
- });
497
- }
498
- catch (e) {
499
- this.logger.error("subscribeWsSpvVaultEvents(): Failed to subscribe to spv vault events, retrying in 10 seconds...");
500
- await new Promise(resolve => setTimeout(resolve, 10 * 1000));
501
- }
502
- } while (subscription == null);
503
- subscription.on((event) => {
504
- const parsedEvents = this.starknetSpvVaultContract.Events.toStarknetAbiEvents([event]);
505
- this.processEvents(parsedEvents, event.block_number).catch(e => {
506
- console.error(`WS: SpvVaultContract: Failed to process event ${parsedEvents[0].txHash}:${parsedEvents[0].name}: `, e);
507
- });
508
- });
509
- this.spvVaultContractSubscription = subscription;
510
- this.logger.debug("subscribeWsSpvVaultEvents(): Successfully subscribed to spv vault contract WS events");
511
- }
512
- /**
513
- *
514
- * @protected
515
- */
516
- async setupWebsocket() {
517
- if (this.wsChannel == null)
518
- throw new Error("Tried to setup websocket subscription on a provider without WS");
519
- this.wsStarted = true;
520
- this.wsChannel.on("open", () => {
521
- this.logger.info("setupWebsocket(): Websocket connection opened!");
522
- });
523
- this.wsChannel.on("close", () => {
524
- this.logger.warn("setupWebsocket(): Websocket connection closed!");
525
- });
526
- this.wsChannel.on("error", (err) => {
527
- this.logger.error("setupWebsocket(): Websocket connection error: ", err);
528
- });
529
- //We don't await these, since they might block indefinitely
530
- this.subscribeWsEscrowEvents();
531
- this.subscribeWsSpvVaultEvents();
532
- }
533
- /**
534
- * @inheritDoc
535
- */
536
- async init(noAutomaticPoll) {
537
- if (noAutomaticPoll)
538
- return;
539
- this.stopped = false;
540
- if (this.wsChannel != null) {
541
- this.logger.debug("init(): WS channel detected, setting up websocket-based subscription!");
542
- await this.setupWebsocket();
543
- }
544
- else {
545
- this.logger.debug("init(): Setting up HTTP polling events subscription!");
546
- await this.setupPoll();
547
- }
548
- }
549
- /**
550
- * Stops all event subscriptions and timers
551
- */
552
- async stop() {
553
- this.stopped = true;
554
- if (this.timeout != null)
555
- clearTimeout(this.timeout);
556
- if (this.wsStarted) {
557
- if (this.escrowContractSubscription != null)
558
- await this.escrowContractSubscription.unsubscribe();
559
- if (this.spvVaultContractSubscription != null)
560
- await this.spvVaultContractSubscription.unsubscribe();
561
- this.wsStarted = false;
562
- }
563
- }
564
- /**
565
- * @inheritDoc
566
- */
567
- registerListener(cbk) {
568
- this.listeners.push(cbk);
569
- }
570
- /**
571
- * @inheritDoc
572
- */
573
- unregisterListener(cbk) {
574
- const index = this.listeners.indexOf(cbk);
575
- if (index >= 0) {
576
- this.listeners.splice(index, 1);
577
- return true;
578
- }
579
- return false;
580
- }
581
- }
582
- exports.StarknetChainEventsBrowser = StarknetChainEventsBrowser;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StarknetChainEventsBrowser = void 0;
4
+ const base_1 = require("@atomiqlabs/base");
5
+ const Utils_1 = require("../../utils/Utils");
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;
10
+ const LOGS_SLIDING_WINDOW = 60;
11
+ /**
12
+ * Starknet on-chain event handler for front-end systems without access to fs, uses WS or long-polling to subscribe, might lose
13
+ * out on some events if the network is unreliable, front-end systems should take this into consideration and not
14
+ * rely purely on events
15
+ *
16
+ * @category Events
17
+ */
18
+ class StarknetChainEventsBrowser {
19
+ constructor(chainInterface, starknetSwapContract, starknetSpvVaultContract, pollIntervalSeconds = 5) {
20
+ this.eventsProcessing = {};
21
+ this.processedEvents = new Set();
22
+ this.listeners = [];
23
+ this.logger = (0, Utils_1.getLogger)("StarknetChainEventsBrowser: ");
24
+ this.stopped = true;
25
+ this.wsStarted = false;
26
+ this.Chain = chainInterface;
27
+ this.wsChannel = chainInterface.wsChannel;
28
+ this.provider = chainInterface.provider;
29
+ this.starknetSwapContract = starknetSwapContract;
30
+ this.starknetSpvVaultContract = starknetSpvVaultContract;
31
+ this.pollIntervalSeconds = pollIntervalSeconds;
32
+ }
33
+ /**
34
+ *
35
+ * @param event
36
+ * @private
37
+ */
38
+ getEventFingerprint(event) {
39
+ const eventData = buffer_1.Buffer.concat([
40
+ ...event.keys.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 32)),
41
+ ...event.data.map(value => (0, Utils_1.bigNumberishToBuffer)(value, 32))
42
+ ]);
43
+ const fingerprint = buffer_1.Buffer.from((0, sha2_1.sha256)(eventData));
44
+ return event.txHash + ":" + fingerprint.toString("hex");
45
+ }
46
+ /**
47
+ *
48
+ * @param event
49
+ * @private
50
+ */
51
+ addProcessedEvent(event) {
52
+ this.processedEvents.add(this.getEventFingerprint(event));
53
+ if (this.processedEvents.size > PROCESSED_EVENTS_BACKLOG)
54
+ this.processedEvents.delete(this.processedEvents.keys().next().value);
55
+ }
56
+ /**
57
+ *
58
+ * @param eventOrFingerprint
59
+ * @private
60
+ */
61
+ isEventProcessed(eventOrFingerprint) {
62
+ const eventFingerprint = typeof (eventOrFingerprint) === "string" ? eventOrFingerprint : this.getEventFingerprint(eventOrFingerprint);
63
+ return this.processedEvents.has(eventFingerprint);
64
+ }
65
+ /**
66
+ * Returns async getter for fetching on-demand initialize event swap data
67
+ *
68
+ * @param event
69
+ * @param claimHandler
70
+ * @private
71
+ * @returns {() => Promise<StarknetSwapData>} getter to be passed to InitializeEvent constructor
72
+ */
73
+ getSwapDataGetter(event, claimHandler) {
74
+ return async () => {
75
+ const trace = await this.Chain.Transactions.traceTransaction(event.txHash, event.blockHash);
76
+ if (trace == null)
77
+ return null;
78
+ return this.starknetSwapContract.findInitSwapData(trace, event.params.escrow_hash, claimHandler);
79
+ };
80
+ }
81
+ /**
82
+ *
83
+ * @param event
84
+ * @private
85
+ */
86
+ parseInitializeEvent(event) {
87
+ const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
88
+ const escrowHash = escrowHashBuffer.toString("hex");
89
+ const claimHandlerHex = (0, Utils_1.toHex)(event.params.claim_handler);
90
+ const claimHandler = this.starknetSwapContract.claimHandlersByAddress[claimHandlerHex];
91
+ if (claimHandler == null) {
92
+ this.logger.warn("parseInitializeEvent(" + escrowHash + "): Unknown claim handler with claim: " + claimHandlerHex);
93
+ return null;
94
+ }
95
+ const swapType = claimHandler.getType();
96
+ this.logger.debug("InitializeEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) + " escrowHash: " + escrowHash);
97
+ return new base_1.InitializeEvent(escrowHash, swapType, (0, Utils_1.onceAsync)(this.getSwapDataGetter(event, claimHandler)));
98
+ }
99
+ /**
100
+ *
101
+ * @param event
102
+ * @private
103
+ */
104
+ parseRefundEvent(event) {
105
+ const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
106
+ const escrowHash = escrowHashBuffer.toString("hex");
107
+ this.logger.debug("RefundEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) + " escrowHash: " + escrowHash);
108
+ return new base_1.RefundEvent(escrowHash);
109
+ }
110
+ /**
111
+ *
112
+ * @param event
113
+ * @private
114
+ */
115
+ parseClaimEvent(event) {
116
+ const escrowHashBuffer = (0, Utils_1.bigNumberishToBuffer)(event.params.escrow_hash, 32);
117
+ const escrowHash = escrowHashBuffer.toString("hex");
118
+ const claimHandlerHex = (0, Utils_1.toHex)(event.params.claim_handler);
119
+ const claimHandler = this.starknetSwapContract.claimHandlersByAddress[claimHandlerHex];
120
+ if (claimHandler == null) {
121
+ this.logger.warn("parseClaimEvent(" + escrowHash + "): Unknown claim handler with claim: " + claimHandlerHex);
122
+ return null;
123
+ }
124
+ const witnessResult = claimHandler.parseWitnessResult(event.params.witness_result);
125
+ this.logger.debug("ClaimEvent claimHash: " + (0, Utils_1.toHex)(event.params.claim_data) +
126
+ " witnessResult: " + witnessResult + " escrowHash: " + escrowHash);
127
+ return new base_1.ClaimEvent(escrowHash, witnessResult);
128
+ }
129
+ /**
130
+ *
131
+ * @param event
132
+ * @private
133
+ */
134
+ parseSpvOpenEvent(event) {
135
+ const owner = (0, Utils_1.toHex)(event.params.owner);
136
+ const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
137
+ const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
138
+ const vout = Number((0, Utils_1.toBigInt)(event.params.vout));
139
+ this.logger.debug("SpvOpenEvent owner: " + owner + " vaultId: " + vaultId + " utxo: " + btcTxId + ":" + vout);
140
+ return new base_1.SpvVaultOpenEvent(owner, vaultId, btcTxId, vout);
141
+ }
142
+ /**
143
+ *
144
+ * @param event
145
+ * @private
146
+ */
147
+ parseSpvDepositEvent(event) {
148
+ const owner = (0, Utils_1.toHex)(event.params.owner);
149
+ const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
150
+ const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
151
+ const depositCount = Number((0, Utils_1.toBigInt)(event.params.deposit_count));
152
+ this.logger.debug("SpvDepositEvent owner: " + owner + " vaultId: " + vaultId + " depositCount: " + depositCount + " amounts: ", amounts);
153
+ return new base_1.SpvVaultDepositEvent(owner, vaultId, amounts, depositCount);
154
+ }
155
+ /**
156
+ *
157
+ * @param event
158
+ * @private
159
+ */
160
+ parseSpvFrontEvent(event) {
161
+ const owner = (0, Utils_1.toHex)(event.params.owner);
162
+ const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
163
+ const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
164
+ const recipient = (0, Utils_1.toHex)(event.params.recipient);
165
+ const executionHash = (0, Utils_1.toHex)(event.params.execution_hash);
166
+ const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
167
+ const frontingAddress = (0, Utils_1.toHex)(event.params.caller);
168
+ this.logger.debug("SpvFrontEvent owner: " + owner + " vaultId: " + vaultId + " btcTxId: " + btcTxId +
169
+ " recipient: " + recipient + " frontedBy: " + frontingAddress + " amounts: ", amounts);
170
+ return new base_1.SpvVaultFrontEvent(owner, vaultId, btcTxId, recipient, executionHash, amounts, frontingAddress);
171
+ }
172
+ /**
173
+ *
174
+ * @param event
175
+ * @private
176
+ */
177
+ parseSpvClaimEvent(event) {
178
+ const owner = (0, Utils_1.toHex)(event.params.owner);
179
+ const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
180
+ const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
181
+ const recipient = (0, Utils_1.toHex)(event.params.recipient);
182
+ const executionHash = (0, Utils_1.toHex)(event.params.execution_hash);
183
+ const amounts = [(0, Utils_1.toBigInt)(event.params.amounts["0"]), (0, Utils_1.toBigInt)(event.params.amounts["1"])];
184
+ const caller = (0, Utils_1.toHex)(event.params.caller);
185
+ const frontingAddress = (0, Utils_1.toHex)(event.params.fronting_address);
186
+ const withdrawCount = Number((0, Utils_1.toBigInt)(event.params.withdraw_count));
187
+ this.logger.debug("SpvClaimEvent owner: " + owner + " vaultId: " + vaultId + " btcTxId: " + btcTxId + " withdrawCount: " + withdrawCount +
188
+ " recipient: " + recipient + " frontedBy: " + frontingAddress + " claimedBy: " + caller + " amounts: ", amounts);
189
+ return new base_1.SpvVaultClaimEvent(owner, vaultId, btcTxId, recipient, executionHash, amounts, caller, frontingAddress, withdrawCount);
190
+ }
191
+ /**
192
+ *
193
+ * @param event
194
+ * @private
195
+ */
196
+ parseSpvCloseEvent(event) {
197
+ const owner = (0, Utils_1.toHex)(event.params.owner);
198
+ const vaultId = (0, Utils_1.toBigInt)(event.params.vault_id);
199
+ const btcTxId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
200
+ const error = (0, Utils_1.bigNumberishToBuffer)(event.params.error).toString();
201
+ return new base_1.SpvVaultCloseEvent(owner, vaultId, btcTxId, error);
202
+ }
203
+ /**
204
+ * Processes event as received from the chain, parses it & calls event listeners
205
+ *
206
+ * @param events
207
+ * @param currentBlockNumber
208
+ * @param currentBlockTimestamp
209
+ * @private
210
+ */
211
+ async processEvents(events, currentBlockNumber, currentBlockTimestamp) {
212
+ const blockTimestampsCache = {};
213
+ const getBlockTimestamp = async (blockNumber) => {
214
+ //Use current timestamp for events without block height (probably pre-confirmed)
215
+ if (blockNumber == null)
216
+ return Math.floor(Date.now() / 1000);
217
+ if (currentBlockTimestamp != null && blockNumber === currentBlockNumber)
218
+ return currentBlockTimestamp;
219
+ const blockNumberString = blockNumber.toString();
220
+ blockTimestampsCache[blockNumberString] ?? (blockTimestampsCache[blockNumberString] = await this.Chain.Blocks.getBlockTime(blockNumber));
221
+ return blockTimestampsCache[blockNumberString];
222
+ };
223
+ for (let event of events) {
224
+ const eventIdentifier = this.getEventFingerprint(event);
225
+ if (this.isEventProcessed(eventIdentifier)) {
226
+ this.logger.debug("processEvents(): skipping already processed event: " + eventIdentifier);
227
+ continue;
228
+ }
229
+ let parsedEvent;
230
+ switch (event.name) {
231
+ case "escrow_manager::events::Claim":
232
+ parsedEvent = this.parseClaimEvent(event);
233
+ break;
234
+ case "escrow_manager::events::Refund":
235
+ parsedEvent = this.parseRefundEvent(event);
236
+ break;
237
+ case "escrow_manager::events::Initialize":
238
+ parsedEvent = this.parseInitializeEvent(event);
239
+ break;
240
+ case "spv_swap_vault::events::Opened":
241
+ parsedEvent = this.parseSpvOpenEvent(event);
242
+ break;
243
+ case "spv_swap_vault::events::Deposited":
244
+ parsedEvent = this.parseSpvDepositEvent(event);
245
+ break;
246
+ case "spv_swap_vault::events::Fronted":
247
+ parsedEvent = this.parseSpvFrontEvent(event);
248
+ break;
249
+ case "spv_swap_vault::events::Claimed":
250
+ parsedEvent = this.parseSpvClaimEvent(event);
251
+ break;
252
+ case "spv_swap_vault::events::Closed":
253
+ parsedEvent = this.parseSpvCloseEvent(event);
254
+ break;
255
+ }
256
+ if (this.eventsProcessing[eventIdentifier] != null) {
257
+ this.logger.debug("processEvents(): awaiting event that is currently processing: " + eventIdentifier);
258
+ await this.eventsProcessing[eventIdentifier];
259
+ continue;
260
+ }
261
+ const promise = (async () => {
262
+ if (parsedEvent == null)
263
+ return;
264
+ //We are not trusting pre-confs for events, so this shall never happen
265
+ if (event.blockNumber == null)
266
+ throw new Error("Event block number cannot be null!");
267
+ const timestamp = await getBlockTimestamp(event.blockNumber);
268
+ parsedEvent.meta = {
269
+ blockTime: timestamp,
270
+ txId: event.txHash,
271
+ timestamp //Maybe deprecated
272
+ };
273
+ const eventsArr = [parsedEvent];
274
+ for (let listener of this.listeners) {
275
+ await listener(eventsArr);
276
+ }
277
+ this.addProcessedEvent(event);
278
+ })();
279
+ this.eventsProcessing[eventIdentifier] = promise;
280
+ try {
281
+ await promise;
282
+ delete this.eventsProcessing[eventIdentifier];
283
+ }
284
+ catch (e) {
285
+ delete this.eventsProcessing[eventIdentifier];
286
+ throw e;
287
+ }
288
+ }
289
+ }
290
+ /**
291
+ *
292
+ * @param currentBlock
293
+ * @param lastTxHash
294
+ * @param lastBlockNumber
295
+ * @private
296
+ */
297
+ async checkEventsEcrowManager(currentBlock, lastTxHash, lastBlockNumber) {
298
+ const currentBlockNumber = currentBlock.block_number;
299
+ lastBlockNumber ?? (lastBlockNumber = currentBlockNumber);
300
+ if (currentBlockNumber < lastBlockNumber) {
301
+ this.logger.warn(`checkEventsEscrowManager(): Sanity check triggered - not processing events, currentBlock: ${currentBlockNumber}, lastBlock: ${lastBlockNumber}`);
302
+ return { lastTxHash, lastBlockNumber };
303
+ }
304
+ // this.logger.debug("checkEvents(EscrowManager): Requesting logs: "+logStartHeight+"...pending");
305
+ let events = await this.starknetSwapContract.Events.getContractBlockEvents(["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"], [], lastBlockNumber, null);
306
+ if (lastTxHash != null) {
307
+ const latestProcessedEventIndex = (0, Utils_1.findLastIndex)(events, val => val.txHash === lastTxHash);
308
+ if (latestProcessedEventIndex !== -1) {
309
+ events.splice(0, latestProcessedEventIndex + 1);
310
+ this.logger.debug("checkEvents(EscrowManager): Splicing processed events, resulting size: " + events.length);
311
+ }
312
+ }
313
+ if (events.length > 0) {
314
+ await this.processEvents(events, currentBlock?.block_number, currentBlock?.timestamp);
315
+ const lastProcessed = events[events.length - 1];
316
+ lastTxHash = lastProcessed.txHash;
317
+ const lastProcessedWithBlockHeightIndex = (0, Utils_1.findLastIndex)(events, val => val.blockNumber != null);
318
+ if (lastProcessedWithBlockHeightIndex !== -1) {
319
+ const lastProcessedWithBlockHeight = events[lastProcessedWithBlockHeightIndex];
320
+ if (lastProcessedWithBlockHeight.blockNumber > lastBlockNumber)
321
+ lastBlockNumber = lastProcessedWithBlockHeight.blockNumber;
322
+ }
323
+ }
324
+ else if (currentBlockNumber - lastBlockNumber > LOGS_SLIDING_WINDOW) {
325
+ lastTxHash = undefined;
326
+ lastBlockNumber = currentBlockNumber - LOGS_SLIDING_WINDOW;
327
+ }
328
+ return { lastTxHash, lastBlockNumber };
329
+ }
330
+ async checkEventsSpvVaults(currentBlock, lastTxHash, lastBlockNumber) {
331
+ const currentBlockNumber = currentBlock.block_number;
332
+ lastBlockNumber ?? (lastBlockNumber = currentBlockNumber);
333
+ if (currentBlockNumber < lastBlockNumber) {
334
+ this.logger.warn(`checkEventsSpvVaults(): Sanity check triggered - not processing events, currentBlock: ${currentBlockNumber}, lastBlock: ${lastBlockNumber}`);
335
+ return { lastTxHash, lastBlockNumber };
336
+ }
337
+ // this.logger.debug("checkEvents(SpvVaults): Requesting logs: "+logStartHeight+"...pending");
338
+ let events = await this.starknetSpvVaultContract.Events.getContractBlockEvents(["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"], [], lastBlockNumber, null);
339
+ if (lastTxHash != null) {
340
+ const latestProcessedEventIndex = (0, Utils_1.findLastIndex)(events, val => val.txHash === lastTxHash);
341
+ if (latestProcessedEventIndex !== -1) {
342
+ events.splice(0, latestProcessedEventIndex + 1);
343
+ this.logger.debug("checkEvents(SpvVaults): Splicing processed events, resulting size: " + events.length);
344
+ }
345
+ }
346
+ if (events.length > 0) {
347
+ await this.processEvents(events, currentBlock?.block_number, currentBlock?.timestamp);
348
+ const lastProcessed = events[events.length - 1];
349
+ lastTxHash = lastProcessed.txHash;
350
+ const lastProcessedWithBlockHeightIndex = (0, Utils_1.findLastIndex)(events, val => val.blockNumber != null);
351
+ if (lastProcessedWithBlockHeightIndex !== -1) {
352
+ const lastProcessedWithBlockHeight = events[lastProcessedWithBlockHeightIndex];
353
+ if (lastProcessedWithBlockHeight.blockNumber > lastBlockNumber)
354
+ lastBlockNumber = lastProcessedWithBlockHeight.blockNumber;
355
+ }
356
+ }
357
+ else if (currentBlockNumber - lastBlockNumber > LOGS_SLIDING_WINDOW) {
358
+ lastTxHash = undefined;
359
+ lastBlockNumber = currentBlockNumber - LOGS_SLIDING_WINDOW;
360
+ }
361
+ return { lastTxHash, lastBlockNumber };
362
+ }
363
+ /**
364
+ * @inheritDoc
365
+ */
366
+ async poll(lastState) {
367
+ lastState ?? (lastState = []);
368
+ const currentBlock = await this.Chain.Blocks.getBlock(starknet_1.BlockTag.LATEST);
369
+ const resultEscrow = await this.checkEventsEcrowManager(currentBlock, lastState?.[0]?.lastTxHash, lastState?.[0]?.lastBlockNumber);
370
+ const resultSpvVault = await this.checkEventsSpvVaults(currentBlock, lastState?.[1]?.lastTxHash, lastState?.[1]?.lastBlockNumber);
371
+ return [
372
+ resultEscrow,
373
+ resultSpvVault
374
+ ];
375
+ }
376
+ /**
377
+ * Sets up event handlers listening for swap events over websocket
378
+ *
379
+ * @protected
380
+ */
381
+ async setupPoll(lastState, saveLatestProcessedBlockNumber) {
382
+ let func;
383
+ func = async () => {
384
+ await this.poll(lastState).then(newState => {
385
+ lastState = newState;
386
+ if (saveLatestProcessedBlockNumber != null)
387
+ return saveLatestProcessedBlockNumber(newState);
388
+ }).catch(e => {
389
+ this.logger.error("setupPoll(): Failed to fetch starknet log: ", e);
390
+ });
391
+ if (this.stopped)
392
+ return;
393
+ this.timeout = setTimeout(func, this.pollIntervalSeconds * 1000);
394
+ };
395
+ await func();
396
+ }
397
+ /**
398
+ *
399
+ * @private
400
+ */
401
+ async subscribeWsEscrowEvents() {
402
+ let subscription;
403
+ do {
404
+ try {
405
+ subscription = await this.wsChannel.subscribeEvents({
406
+ fromAddress: this.starknetSwapContract.contract.address,
407
+ keys: this.starknetSwapContract.Events.toFilter(["escrow_manager::events::Initialize", "escrow_manager::events::Claim", "escrow_manager::events::Refund"], []),
408
+ finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
409
+ });
410
+ }
411
+ catch (e) {
412
+ this.logger.error("subscribeWsEscrowEvents(): Failed to subscribe to escrow events, retrying in 10 seconds...");
413
+ await new Promise(resolve => setTimeout(resolve, 10 * 1000));
414
+ }
415
+ } while (subscription == null);
416
+ subscription.on((event) => {
417
+ const parsedEvents = this.starknetSwapContract.Events.toStarknetAbiEvents([event]);
418
+ this.processEvents(parsedEvents, event.block_number).catch(e => {
419
+ console.error(`WS: EscrowContract: Failed to process event ${parsedEvents[0].txHash}:${parsedEvents[0].name}: `, e);
420
+ });
421
+ });
422
+ this.escrowContractSubscription = subscription;
423
+ this.logger.debug("subscribeWsEscrowEvents(): Successfully subscribed to escrow contract WS events");
424
+ }
425
+ /**
426
+ *
427
+ * @private
428
+ */
429
+ async subscribeWsSpvVaultEvents() {
430
+ let subscription;
431
+ do {
432
+ try {
433
+ subscription = await this.wsChannel.subscribeEvents({
434
+ fromAddress: this.starknetSpvVaultContract.contract.address,
435
+ 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"], []),
436
+ finalityStatus: starknet_1.TransactionFinalityStatus.ACCEPTED_ON_L2
437
+ });
438
+ }
439
+ catch (e) {
440
+ this.logger.error("subscribeWsSpvVaultEvents(): Failed to subscribe to spv vault events, retrying in 10 seconds...");
441
+ await new Promise(resolve => setTimeout(resolve, 10 * 1000));
442
+ }
443
+ } while (subscription == null);
444
+ subscription.on((event) => {
445
+ const parsedEvents = this.starknetSpvVaultContract.Events.toStarknetAbiEvents([event]);
446
+ this.processEvents(parsedEvents, event.block_number).catch(e => {
447
+ console.error(`WS: SpvVaultContract: Failed to process event ${parsedEvents[0].txHash}:${parsedEvents[0].name}: `, e);
448
+ });
449
+ });
450
+ this.spvVaultContractSubscription = subscription;
451
+ this.logger.debug("subscribeWsSpvVaultEvents(): Successfully subscribed to spv vault contract WS events");
452
+ }
453
+ /**
454
+ *
455
+ * @protected
456
+ */
457
+ async setupWebsocket() {
458
+ if (this.wsChannel == null)
459
+ throw new Error("Tried to setup websocket subscription on a provider without WS");
460
+ this.wsStarted = true;
461
+ this.wsChannel.on("open", () => {
462
+ this.logger.info("setupWebsocket(): Websocket connection opened!");
463
+ });
464
+ this.wsChannel.on("close", () => {
465
+ this.logger.warn("setupWebsocket(): Websocket connection closed!");
466
+ });
467
+ this.wsChannel.on("error", (err) => {
468
+ this.logger.error("setupWebsocket(): Websocket connection error: ", err);
469
+ });
470
+ //We don't await these, since they might block indefinitely
471
+ this.subscribeWsEscrowEvents();
472
+ this.subscribeWsSpvVaultEvents();
473
+ }
474
+ /**
475
+ * @inheritDoc
476
+ */
477
+ async init(noAutomaticPoll) {
478
+ if (noAutomaticPoll)
479
+ return;
480
+ this.stopped = false;
481
+ if (this.wsChannel != null) {
482
+ this.logger.debug("init(): WS channel detected, setting up websocket-based subscription!");
483
+ await this.setupWebsocket();
484
+ }
485
+ else {
486
+ this.logger.debug("init(): Setting up HTTP polling events subscription!");
487
+ await this.setupPoll();
488
+ }
489
+ }
490
+ /**
491
+ * Stops all event subscriptions and timers
492
+ */
493
+ async stop() {
494
+ this.stopped = true;
495
+ if (this.timeout != null)
496
+ clearTimeout(this.timeout);
497
+ if (this.wsStarted) {
498
+ if (this.escrowContractSubscription != null)
499
+ await this.escrowContractSubscription.unsubscribe();
500
+ if (this.spvVaultContractSubscription != null)
501
+ await this.spvVaultContractSubscription.unsubscribe();
502
+ this.wsStarted = false;
503
+ }
504
+ }
505
+ /**
506
+ * @inheritDoc
507
+ */
508
+ registerListener(cbk) {
509
+ this.listeners.push(cbk);
510
+ }
511
+ /**
512
+ * @inheritDoc
513
+ */
514
+ unregisterListener(cbk) {
515
+ const index = this.listeners.indexOf(cbk);
516
+ if (index >= 0) {
517
+ this.listeners.splice(index, 1);
518
+ return true;
519
+ }
520
+ return false;
521
+ }
522
+ }
523
+ exports.StarknetChainEventsBrowser = StarknetChainEventsBrowser;