@atomiqlabs/chain-solana 13.5.13 → 13.5.14

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 (131) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +73 -73
  3. package/dist/index.d.ts +81 -81
  4. package/dist/index.js +102 -102
  5. package/dist/node/index.d.ts +9 -9
  6. package/dist/node/index.js +13 -13
  7. package/dist/solana/SolanaChainType.d.ts +15 -15
  8. package/dist/solana/SolanaChainType.js +2 -2
  9. package/dist/solana/SolanaChains.d.ts +12 -12
  10. package/dist/solana/SolanaChains.js +45 -45
  11. package/dist/solana/SolanaInitializer.d.ts +94 -94
  12. package/dist/solana/SolanaInitializer.js +174 -174
  13. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +222 -222
  14. package/dist/solana/btcrelay/SolanaBtcRelay.js +455 -455
  15. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +84 -84
  16. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +70 -70
  17. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +92 -92
  18. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +109 -109
  19. package/dist/solana/btcrelay/program/programIdl.json +671 -671
  20. package/dist/solana/chain/SolanaAction.d.ts +26 -26
  21. package/dist/solana/chain/SolanaAction.js +87 -87
  22. package/dist/solana/chain/SolanaChainInterface.d.ts +224 -224
  23. package/dist/solana/chain/SolanaChainInterface.js +275 -275
  24. package/dist/solana/chain/SolanaModule.d.ts +14 -14
  25. package/dist/solana/chain/SolanaModule.js +13 -13
  26. package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
  27. package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
  28. package/dist/solana/chain/modules/SolanaBlocks.d.ts +32 -32
  29. package/dist/solana/chain/modules/SolanaBlocks.js +78 -78
  30. package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
  31. package/dist/solana/chain/modules/SolanaEvents.js +238 -238
  32. package/dist/solana/chain/modules/SolanaFees.d.ts +189 -189
  33. package/dist/solana/chain/modules/SolanaFees.js +434 -434
  34. package/dist/solana/chain/modules/SolanaSignatures.d.ts +24 -24
  35. package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
  36. package/dist/solana/chain/modules/SolanaSlots.d.ts +33 -33
  37. package/dist/solana/chain/modules/SolanaSlots.js +72 -72
  38. package/dist/solana/chain/modules/SolanaTokens.d.ts +123 -123
  39. package/dist/solana/chain/modules/SolanaTokens.js +242 -242
  40. package/dist/solana/chain/modules/SolanaTransactions.d.ts +149 -149
  41. package/dist/solana/chain/modules/SolanaTransactions.js +445 -445
  42. package/dist/solana/connection/ConnectionWithRetries.d.ts +35 -35
  43. package/dist/solana/connection/ConnectionWithRetries.js +86 -71
  44. package/dist/solana/events/SolanaChainEvents.d.ts +45 -45
  45. package/dist/solana/events/SolanaChainEvents.js +108 -108
  46. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +205 -205
  47. package/dist/solana/events/SolanaChainEventsBrowser.js +404 -404
  48. package/dist/solana/program/SolanaProgramBase.d.ts +73 -73
  49. package/dist/solana/program/SolanaProgramBase.js +54 -54
  50. package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
  51. package/dist/solana/program/SolanaProgramModule.js +11 -11
  52. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
  53. package/dist/solana/program/modules/SolanaProgramEvents.js +117 -117
  54. package/dist/solana/swaps/SolanaSwapData.d.ts +333 -333
  55. package/dist/solana/swaps/SolanaSwapData.js +535 -535
  56. package/dist/solana/swaps/SolanaSwapModule.d.ts +11 -11
  57. package/dist/solana/swaps/SolanaSwapModule.js +12 -12
  58. package/dist/solana/swaps/SolanaSwapProgram.d.ts +376 -376
  59. package/dist/solana/swaps/SolanaSwapProgram.js +769 -769
  60. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
  61. package/dist/solana/swaps/SwapTypeEnum.js +43 -43
  62. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +95 -95
  63. package/dist/solana/swaps/modules/SolanaDataAccount.js +232 -232
  64. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +69 -69
  65. package/dist/solana/swaps/modules/SolanaLpVault.js +171 -171
  66. package/dist/solana/swaps/modules/SwapClaim.d.ts +126 -126
  67. package/dist/solana/swaps/modules/SwapClaim.js +294 -294
  68. package/dist/solana/swaps/modules/SwapInit.d.ts +213 -213
  69. package/dist/solana/swaps/modules/SwapInit.js +658 -658
  70. package/dist/solana/swaps/modules/SwapRefund.d.ts +87 -87
  71. package/dist/solana/swaps/modules/SwapRefund.js +293 -293
  72. package/dist/solana/swaps/programIdl.json +945 -945
  73. package/dist/solana/swaps/programTypes.d.ts +943 -943
  74. package/dist/solana/swaps/programTypes.js +945 -945
  75. package/dist/solana/swaps/v1/programIdl.json +945 -945
  76. package/dist/solana/swaps/v1/programTypes.d.ts +943 -943
  77. package/dist/solana/swaps/v1/programTypes.js +945 -945
  78. package/dist/solana/swaps/v2/programIdl.json +952 -952
  79. package/dist/solana/swaps/v2/programTypes.d.ts +950 -950
  80. package/dist/solana/swaps/v2/programTypes.js +952 -952
  81. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +29 -29
  82. package/dist/solana/wallet/SolanaKeypairWallet.js +50 -50
  83. package/dist/solana/wallet/SolanaSigner.d.ts +30 -30
  84. package/dist/solana/wallet/SolanaSigner.js +30 -30
  85. package/dist/utils/Utils.d.ts +58 -58
  86. package/dist/utils/Utils.js +170 -170
  87. package/node/index.d.ts +1 -1
  88. package/node/index.js +3 -3
  89. package/package.json +46 -46
  90. package/src/index.ts +87 -87
  91. package/src/node/index.ts +9 -9
  92. package/src/solana/SolanaChainType.ts +32 -32
  93. package/src/solana/SolanaChains.ts +46 -46
  94. package/src/solana/SolanaInitializer.ts +278 -278
  95. package/src/solana/btcrelay/SolanaBtcRelay.ts +615 -615
  96. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +116 -116
  97. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +148 -148
  98. package/src/solana/btcrelay/program/programIdl.json +670 -670
  99. package/src/solana/chain/SolanaAction.ts +109 -109
  100. package/src/solana/chain/SolanaChainInterface.ts +404 -404
  101. package/src/solana/chain/SolanaModule.ts +20 -20
  102. package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
  103. package/src/solana/chain/modules/SolanaBlocks.ts +89 -89
  104. package/src/solana/chain/modules/SolanaEvents.ts +271 -271
  105. package/src/solana/chain/modules/SolanaFees.ts +522 -522
  106. package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
  107. package/src/solana/chain/modules/SolanaSlots.ts +85 -85
  108. package/src/solana/chain/modules/SolanaTokens.ts +300 -300
  109. package/src/solana/chain/modules/SolanaTransactions.ts +503 -503
  110. package/src/solana/connection/ConnectionWithRetries.ts +113 -96
  111. package/src/solana/events/SolanaChainEvents.ts +127 -127
  112. package/src/solana/events/SolanaChainEventsBrowser.ts +495 -495
  113. package/src/solana/program/SolanaProgramBase.ts +119 -119
  114. package/src/solana/program/SolanaProgramModule.ts +15 -15
  115. package/src/solana/program/modules/SolanaProgramEvents.ts +157 -157
  116. package/src/solana/swaps/SolanaSwapData.ts +735 -735
  117. package/src/solana/swaps/SolanaSwapModule.ts +19 -19
  118. package/src/solana/swaps/SolanaSwapProgram.ts +1074 -1074
  119. package/src/solana/swaps/SwapTypeEnum.ts +30 -30
  120. package/src/solana/swaps/modules/SolanaDataAccount.ts +302 -302
  121. package/src/solana/swaps/modules/SolanaLpVault.ts +208 -208
  122. package/src/solana/swaps/modules/SwapClaim.ts +387 -387
  123. package/src/solana/swaps/modules/SwapInit.ts +785 -785
  124. package/src/solana/swaps/modules/SwapRefund.ts +353 -353
  125. package/src/solana/swaps/v1/programIdl.json +944 -944
  126. package/src/solana/swaps/v1/programTypes.ts +1885 -1885
  127. package/src/solana/swaps/v2/programIdl.json +951 -951
  128. package/src/solana/swaps/v2/programTypes.ts +1899 -1899
  129. package/src/solana/wallet/SolanaKeypairWallet.ts +56 -56
  130. package/src/solana/wallet/SolanaSigner.ts +43 -43
  131. package/src/utils/Utils.ts +194 -194
@@ -1,404 +1,404 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SolanaChainEventsBrowser = void 0;
4
- const base_1 = require("@atomiqlabs/base");
5
- const SolanaSwapData_1 = require("../swaps/SolanaSwapData");
6
- const SolanaSwapProgram_1 = require("../swaps/SolanaSwapProgram");
7
- const Utils_1 = require("../../utils/Utils");
8
- const SwapTypeEnum_1 = require("../swaps/SwapTypeEnum");
9
- const buffer_1 = require("buffer");
10
- const LOG_FETCH_LIMIT = 500;
11
- const PROCESSED_SIGNATURES_BACKLOG = 500;
12
- function toNewEventListenerState(obj) {
13
- if (obj == null)
14
- return undefined;
15
- if (obj.slot != null || obj.signature != null)
16
- return { "v1": obj };
17
- return obj;
18
- }
19
- /**
20
- * Solana on-chain event handler for front-end systems without access to fs, uses pure WS to subscribe, might lose
21
- * out on some events if the network is unreliable, front-end systems should take this into consideration and not
22
- * rely purely on events
23
- *
24
- * @category Events
25
- */
26
- class SolanaChainEventsBrowser {
27
- constructor(connection, contractVersions, logFetchLimit) {
28
- /**
29
- * @internal
30
- */
31
- this.listeners = [];
32
- /**
33
- * @internal
34
- */
35
- this.eventListeners = {};
36
- /**
37
- * @internal
38
- */
39
- this.logger = (0, Utils_1.getLogger)("SolanaChainEventsBrowser: ");
40
- this.signaturesProcessing = {};
41
- this.processedSignatures = [];
42
- this.processedSignaturesIndex = 0;
43
- this.connection = connection;
44
- if (contractVersions instanceof SolanaSwapProgram_1.SolanaSwapProgram) {
45
- this.contractVersions = { [contractVersions.version]: { swapContract: contractVersions } };
46
- }
47
- else {
48
- this.contractVersions = contractVersions;
49
- }
50
- this.logFetchLimit = logFetchLimit ?? LOG_FETCH_LIMIT;
51
- }
52
- addProcessedSignature(signature, version) {
53
- this.processedSignatures[this.processedSignaturesIndex] = signature + "-" + version;
54
- this.processedSignaturesIndex += 1;
55
- if (this.processedSignaturesIndex >= PROCESSED_SIGNATURES_BACKLOG)
56
- this.processedSignaturesIndex = 0;
57
- }
58
- isSignatureProcessed(signature, version) {
59
- return this.processedSignatures.includes(signature + "-" + version);
60
- }
61
- /**
62
- * Parses EventObject from the transaction
63
- *
64
- * @param transaction
65
- * @param version
66
- * @private
67
- * @returns {EventObject} parsed event object
68
- */
69
- getEventObjectFromTransaction(transaction, version) {
70
- const signature = transaction.transaction.signatures[0];
71
- if (transaction.meta == null)
72
- throw new Error(`Transaction 'meta' not found for Solana tx: ${signature}`);
73
- if (transaction.meta.err != null || transaction.meta.logMessages == null)
74
- return null;
75
- const instructions = this.contractVersions[version].swapContract._Events.decodeInstructions(transaction.transaction.message);
76
- const events = this.contractVersions[version].swapContract._Events.parseLogs(transaction.meta.logMessages);
77
- return {
78
- instructions,
79
- events,
80
- blockTime: transaction.blockTime,
81
- signature: signature
82
- };
83
- }
84
- /**
85
- * Fetches transaction from the RPC, parses it to even object & processes it through event handler
86
- *
87
- * @param signature
88
- * @param version
89
- * @private
90
- * @returns {boolean} whether the operation was successful
91
- */
92
- async fetchTxAndProcessEvent(signature, version) {
93
- try {
94
- const transaction = await this.connection.getParsedTransaction(signature, {
95
- commitment: "confirmed",
96
- maxSupportedTransactionVersion: 1
97
- });
98
- if (transaction == null)
99
- return false;
100
- const eventObject = this.getEventObjectFromTransaction(transaction, version);
101
- if (eventObject == null)
102
- return true;
103
- await this.processEvent(eventObject, version);
104
- return true;
105
- }
106
- catch (e) {
107
- this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: " + signature, e);
108
- return false;
109
- }
110
- }
111
- /**
112
- * Fetches and parses transaction instructions
113
- *
114
- * @private
115
- * @returns {Promise<(InstructionWithAccounts<SwapProgram> | null)[] | null>} array of parsed instructions
116
- */
117
- async getTransactionInstructions(signature, version) {
118
- const transaction = await (0, Utils_1.tryWithRetries)(async () => {
119
- const res = await this.connection.getParsedTransaction(signature, {
120
- commitment: "confirmed",
121
- maxSupportedTransactionVersion: 0
122
- });
123
- if (res == null)
124
- throw new Error("Transaction not found!");
125
- return res;
126
- });
127
- if (transaction.meta == null)
128
- throw new Error("Transaction 'meta' not found!");
129
- if (transaction.meta.err != null)
130
- return null;
131
- return this.contractVersions[version].swapContract._Events.decodeInstructions(transaction.transaction.message);
132
- }
133
- /**
134
- * Returns async getter for fetching on-demand initialize event swap data
135
- *
136
- * @param eventObject
137
- * @param txoHash
138
- * @param version
139
- * @private
140
- * @returns {() => Promise<SolanaSwapData>} getter to be passed to InitializeEvent constructor
141
- */
142
- getSwapDataGetter(eventObject, txoHash, version) {
143
- return async () => {
144
- if (eventObject.instructions == null) {
145
- const ixs = await this.getTransactionInstructions(eventObject.signature, version);
146
- if (ixs == null)
147
- return null;
148
- eventObject.instructions = ixs;
149
- }
150
- const initIx = eventObject.instructions.find(ix => ix != null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize"));
151
- if (initIx == null)
152
- return null;
153
- return SolanaSwapData_1.SolanaSwapData.fromInstruction(this.contractVersions[version].swapContract.program.programId, version, initIx, txoHash);
154
- };
155
- }
156
- /**
157
- * @internal
158
- */
159
- parseInitializeEvent(data, eventObject, version) {
160
- const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
161
- const txoHash = buffer_1.Buffer.from(data.txoHash).toString("hex");
162
- const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
163
- this.logger.debug("InitializeEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
164
- " txoHash: " + txoHash + " escrowHash: " + escrowHash);
165
- return new base_1.InitializeEvent(escrowHash, SwapTypeEnum_1.SwapTypeEnum.toChainSwapType(data.kind), (0, Utils_1.onceAsync)(this.getSwapDataGetter(eventObject, txoHash, version)), version);
166
- }
167
- /**
168
- * @internal
169
- */
170
- parseRefundEvent(data, version) {
171
- const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
172
- const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
173
- this.logger.debug("RefundEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
174
- " escrowHash: " + escrowHash);
175
- return new base_1.RefundEvent(escrowHash, version);
176
- }
177
- /**
178
- * @internal
179
- */
180
- parseClaimEvent(data, version) {
181
- const secret = buffer_1.Buffer.from(data.secret).toString("hex");
182
- const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
183
- const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
184
- this.logger.debug("ClaimEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
185
- " secret: " + secret + " escrowHash: " + escrowHash);
186
- return new base_1.ClaimEvent(escrowHash, secret, version);
187
- }
188
- /**
189
- * Processes event as received from the chain, parses it & calls event listeners
190
- *
191
- * @param eventObject
192
- * @param version
193
- * @internal
194
- */
195
- async processEvent(eventObject, version) {
196
- let parsedEvents = eventObject.events.map(event => {
197
- let parsedEvent;
198
- switch (event.name) {
199
- case "ClaimEvent":
200
- parsedEvent = this.parseClaimEvent(event.data, version);
201
- break;
202
- case "RefundEvent":
203
- parsedEvent = this.parseRefundEvent(event.data, version);
204
- break;
205
- case "InitializeEvent":
206
- parsedEvent = this.parseInitializeEvent(event.data, eventObject, version);
207
- break;
208
- }
209
- if (parsedEvent == null)
210
- return null;
211
- parsedEvent.meta = {
212
- blockTime: eventObject.blockTime,
213
- timestamp: eventObject.blockTime,
214
- txId: eventObject.signature
215
- };
216
- return parsedEvent;
217
- }).filter(parsedEvent => parsedEvent != null);
218
- for (let listener of this.listeners) {
219
- await listener(parsedEvents);
220
- }
221
- }
222
- /**
223
- * Returns websocket event handler for specific event type
224
- *
225
- * @param name
226
- * @param version
227
- * @internal
228
- * @returns event handler to be passed to program's addEventListener function
229
- */
230
- getWsEventHandler(name, version) {
231
- return (data, slotNumber, signature) => {
232
- if (this.signaturesProcessing[signature + "-" + version] != null)
233
- return;
234
- if (this.isSignatureProcessed(signature, version))
235
- return;
236
- this.logger.debug("getWsEventHandler(" + name + "): Process signature: ", signature);
237
- this.signaturesProcessing[signature + "-" + version] = this.processEvent({
238
- events: [{ name, data: data }],
239
- blockTime: Math.floor(Date.now() / 1000),
240
- signature
241
- }, version).then(() => true).catch(e => {
242
- this.logger.error("getWsEventHandler(" + name + "): Error processing signature: " + signature, e);
243
- return false;
244
- });
245
- };
246
- }
247
- /**
248
- * Sets up event handlers listening for swap events over websocket
249
- *
250
- * @internal
251
- */
252
- setupWebsocket() {
253
- var _a;
254
- for (let version in this.contractVersions) {
255
- const program = this.contractVersions[version].swapContract.program;
256
- const eventListeners = (_a = this.eventListeners)[version] ?? (_a[version] = []);
257
- eventListeners.push(program.addEventListener("InitializeEvent", this.getWsEventHandler("InitializeEvent", version)));
258
- eventListeners.push(program.addEventListener("ClaimEvent", this.getWsEventHandler("ClaimEvent", version)));
259
- eventListeners.push(program.addEventListener("RefundEvent", this.getWsEventHandler("RefundEvent", version)));
260
- }
261
- }
262
- /**
263
- * Gets all the new signatures from the last processed signature
264
- *
265
- * @param lastProcessedSignature
266
- * @param version
267
- * @private
268
- */
269
- async getNewSignatures(lastProcessedSignature, version) {
270
- let signatures = [];
271
- let fetched = null;
272
- while (fetched == null || fetched.length === this.logFetchLimit) {
273
- if (signatures.length === 0) {
274
- fetched = await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
275
- until: lastProcessedSignature.signature,
276
- limit: this.logFetchLimit
277
- }, "confirmed");
278
- //Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
279
- if (fetched.length > 0 && fetched[0].slot < lastProcessedSignature.slot) {
280
- this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
281
- return null;
282
- }
283
- }
284
- else {
285
- fetched = await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
286
- before: signatures[signatures.length - 1].signature,
287
- until: lastProcessedSignature.signature,
288
- limit: this.logFetchLimit
289
- }, "confirmed");
290
- }
291
- signatures = signatures.concat(fetched);
292
- }
293
- return signatures;
294
- }
295
- /**
296
- * Gets single latest known signature
297
- *
298
- * @private
299
- */
300
- async getFirstSignature(version) {
301
- return await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
302
- limit: 1
303
- }, "confirmed");
304
- }
305
- /**
306
- * Processes signatures, fetches transactions & processes event through event handlers
307
- *
308
- * @param signatures
309
- * @param version
310
- * @private
311
- * @returns {Promise<{signature: string, slot: number}>} latest processed transaction signature and slot height
312
- */
313
- async processSignatures(signatures, version) {
314
- let lastSuccessfulSignature = null;
315
- try {
316
- for (let i = signatures.length - 1; i >= 0; i--) {
317
- const txSignature = signatures[i];
318
- //Check if signature is already being processed by the
319
- const signaturePromise = this.signaturesProcessing[txSignature.signature + "-" + version];
320
- if (signaturePromise != null) {
321
- const result = await signaturePromise;
322
- delete this.signaturesProcessing[txSignature.signature + "-" + version];
323
- if (result) {
324
- lastSuccessfulSignature = txSignature;
325
- this.addProcessedSignature(txSignature.signature, version);
326
- continue;
327
- }
328
- }
329
- this.logger.debug("processSignatures(): Process signature: ", txSignature);
330
- const processPromise = this.fetchTxAndProcessEvent(txSignature.signature, version);
331
- this.signaturesProcessing[txSignature.signature + "-" + version] = processPromise;
332
- const result = await processPromise;
333
- if (!result)
334
- throw new Error("Failed to process signature: " + txSignature);
335
- lastSuccessfulSignature = txSignature;
336
- this.addProcessedSignature(txSignature.signature, version);
337
- delete this.signaturesProcessing[txSignature.signature + "-" + version];
338
- }
339
- }
340
- catch (e) {
341
- this.logger.error("processSignatures(): Failed processing signatures: ", e);
342
- }
343
- return lastSuccessfulSignature;
344
- }
345
- /**
346
- * @inheritDoc
347
- */
348
- async poll(lastSignature) {
349
- const _lastSignature = toNewEventListenerState(lastSignature);
350
- const result = {};
351
- for (let version in this.contractVersions) {
352
- const lastSignature = _lastSignature?.[version];
353
- let signatures = lastSignature == null
354
- ? await this.getFirstSignature(version)
355
- : await this.getNewSignatures(lastSignature, version);
356
- if (signatures == null) {
357
- result[version] = lastSignature ?? null;
358
- }
359
- else {
360
- let lastSuccessfulSignature = await this.processSignatures(signatures, version);
361
- result[version] = lastSuccessfulSignature ?? lastSignature ?? null;
362
- }
363
- }
364
- return result;
365
- }
366
- /**
367
- * @inheritDoc
368
- */
369
- init(noAutomaticPoll) {
370
- if (noAutomaticPoll)
371
- return Promise.resolve();
372
- this.setupWebsocket();
373
- return Promise.resolve();
374
- }
375
- /**
376
- * @inheritDoc
377
- */
378
- async stop() {
379
- for (let version in this.eventListeners) {
380
- for (let num of this.eventListeners[version]) {
381
- await this.contractVersions[version].swapContract.program.removeEventListener(num);
382
- }
383
- }
384
- this.eventListeners = {};
385
- }
386
- /**
387
- * @inheritDoc
388
- */
389
- registerListener(cbk) {
390
- this.listeners.push(cbk);
391
- }
392
- /**
393
- * @inheritDoc
394
- */
395
- unregisterListener(cbk) {
396
- const index = this.listeners.indexOf(cbk);
397
- if (index >= 0) {
398
- this.listeners.splice(index, 1);
399
- return true;
400
- }
401
- return false;
402
- }
403
- }
404
- exports.SolanaChainEventsBrowser = SolanaChainEventsBrowser;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaChainEventsBrowser = void 0;
4
+ const base_1 = require("@atomiqlabs/base");
5
+ const SolanaSwapData_1 = require("../swaps/SolanaSwapData");
6
+ const SolanaSwapProgram_1 = require("../swaps/SolanaSwapProgram");
7
+ const Utils_1 = require("../../utils/Utils");
8
+ const SwapTypeEnum_1 = require("../swaps/SwapTypeEnum");
9
+ const buffer_1 = require("buffer");
10
+ const LOG_FETCH_LIMIT = 500;
11
+ const PROCESSED_SIGNATURES_BACKLOG = 500;
12
+ function toNewEventListenerState(obj) {
13
+ if (obj == null)
14
+ return undefined;
15
+ if (obj.slot != null || obj.signature != null)
16
+ return { "v1": obj };
17
+ return obj;
18
+ }
19
+ /**
20
+ * Solana on-chain event handler for front-end systems without access to fs, uses pure WS to subscribe, might lose
21
+ * out on some events if the network is unreliable, front-end systems should take this into consideration and not
22
+ * rely purely on events
23
+ *
24
+ * @category Events
25
+ */
26
+ class SolanaChainEventsBrowser {
27
+ constructor(connection, contractVersions, logFetchLimit) {
28
+ /**
29
+ * @internal
30
+ */
31
+ this.listeners = [];
32
+ /**
33
+ * @internal
34
+ */
35
+ this.eventListeners = {};
36
+ /**
37
+ * @internal
38
+ */
39
+ this.logger = (0, Utils_1.getLogger)("SolanaChainEventsBrowser: ");
40
+ this.signaturesProcessing = {};
41
+ this.processedSignatures = [];
42
+ this.processedSignaturesIndex = 0;
43
+ this.connection = connection;
44
+ if (contractVersions instanceof SolanaSwapProgram_1.SolanaSwapProgram) {
45
+ this.contractVersions = { [contractVersions.version]: { swapContract: contractVersions } };
46
+ }
47
+ else {
48
+ this.contractVersions = contractVersions;
49
+ }
50
+ this.logFetchLimit = logFetchLimit ?? LOG_FETCH_LIMIT;
51
+ }
52
+ addProcessedSignature(signature, version) {
53
+ this.processedSignatures[this.processedSignaturesIndex] = signature + "-" + version;
54
+ this.processedSignaturesIndex += 1;
55
+ if (this.processedSignaturesIndex >= PROCESSED_SIGNATURES_BACKLOG)
56
+ this.processedSignaturesIndex = 0;
57
+ }
58
+ isSignatureProcessed(signature, version) {
59
+ return this.processedSignatures.includes(signature + "-" + version);
60
+ }
61
+ /**
62
+ * Parses EventObject from the transaction
63
+ *
64
+ * @param transaction
65
+ * @param version
66
+ * @private
67
+ * @returns {EventObject} parsed event object
68
+ */
69
+ getEventObjectFromTransaction(transaction, version) {
70
+ const signature = transaction.transaction.signatures[0];
71
+ if (transaction.meta == null)
72
+ throw new Error(`Transaction 'meta' not found for Solana tx: ${signature}`);
73
+ if (transaction.meta.err != null || transaction.meta.logMessages == null)
74
+ return null;
75
+ const instructions = this.contractVersions[version].swapContract._Events.decodeInstructions(transaction.transaction.message);
76
+ const events = this.contractVersions[version].swapContract._Events.parseLogs(transaction.meta.logMessages);
77
+ return {
78
+ instructions,
79
+ events,
80
+ blockTime: transaction.blockTime,
81
+ signature: signature
82
+ };
83
+ }
84
+ /**
85
+ * Fetches transaction from the RPC, parses it to even object & processes it through event handler
86
+ *
87
+ * @param signature
88
+ * @param version
89
+ * @private
90
+ * @returns {boolean} whether the operation was successful
91
+ */
92
+ async fetchTxAndProcessEvent(signature, version) {
93
+ try {
94
+ const transaction = await this.connection.getParsedTransaction(signature, {
95
+ commitment: "confirmed",
96
+ maxSupportedTransactionVersion: 1
97
+ });
98
+ if (transaction == null)
99
+ return false;
100
+ const eventObject = this.getEventObjectFromTransaction(transaction, version);
101
+ if (eventObject == null)
102
+ return true;
103
+ await this.processEvent(eventObject, version);
104
+ return true;
105
+ }
106
+ catch (e) {
107
+ this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: " + signature, e);
108
+ return false;
109
+ }
110
+ }
111
+ /**
112
+ * Fetches and parses transaction instructions
113
+ *
114
+ * @private
115
+ * @returns {Promise<(InstructionWithAccounts<SwapProgram> | null)[] | null>} array of parsed instructions
116
+ */
117
+ async getTransactionInstructions(signature, version) {
118
+ const transaction = await (0, Utils_1.tryWithRetries)(async () => {
119
+ const res = await this.connection.getParsedTransaction(signature, {
120
+ commitment: "confirmed",
121
+ maxSupportedTransactionVersion: 0
122
+ });
123
+ if (res == null)
124
+ throw new Error("Transaction not found!");
125
+ return res;
126
+ });
127
+ if (transaction.meta == null)
128
+ throw new Error("Transaction 'meta' not found!");
129
+ if (transaction.meta.err != null)
130
+ return null;
131
+ return this.contractVersions[version].swapContract._Events.decodeInstructions(transaction.transaction.message);
132
+ }
133
+ /**
134
+ * Returns async getter for fetching on-demand initialize event swap data
135
+ *
136
+ * @param eventObject
137
+ * @param txoHash
138
+ * @param version
139
+ * @private
140
+ * @returns {() => Promise<SolanaSwapData>} getter to be passed to InitializeEvent constructor
141
+ */
142
+ getSwapDataGetter(eventObject, txoHash, version) {
143
+ return async () => {
144
+ if (eventObject.instructions == null) {
145
+ const ixs = await this.getTransactionInstructions(eventObject.signature, version);
146
+ if (ixs == null)
147
+ return null;
148
+ eventObject.instructions = ixs;
149
+ }
150
+ const initIx = eventObject.instructions.find(ix => ix != null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize"));
151
+ if (initIx == null)
152
+ return null;
153
+ return SolanaSwapData_1.SolanaSwapData.fromInstruction(this.contractVersions[version].swapContract.program.programId, version, initIx, txoHash);
154
+ };
155
+ }
156
+ /**
157
+ * @internal
158
+ */
159
+ parseInitializeEvent(data, eventObject, version) {
160
+ const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
161
+ const txoHash = buffer_1.Buffer.from(data.txoHash).toString("hex");
162
+ const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
163
+ this.logger.debug("InitializeEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
164
+ " txoHash: " + txoHash + " escrowHash: " + escrowHash);
165
+ return new base_1.InitializeEvent(escrowHash, SwapTypeEnum_1.SwapTypeEnum.toChainSwapType(data.kind), (0, Utils_1.onceAsync)(this.getSwapDataGetter(eventObject, txoHash, version)), version);
166
+ }
167
+ /**
168
+ * @internal
169
+ */
170
+ parseRefundEvent(data, version) {
171
+ const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
172
+ const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
173
+ this.logger.debug("RefundEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
174
+ " escrowHash: " + escrowHash);
175
+ return new base_1.RefundEvent(escrowHash, version);
176
+ }
177
+ /**
178
+ * @internal
179
+ */
180
+ parseClaimEvent(data, version) {
181
+ const secret = buffer_1.Buffer.from(data.secret).toString("hex");
182
+ const paymentHash = buffer_1.Buffer.from(data.hash).toString("hex");
183
+ const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, data.sequence);
184
+ this.logger.debug("ClaimEvent paymentHash: " + paymentHash + " sequence: " + data.sequence.toString(10) +
185
+ " secret: " + secret + " escrowHash: " + escrowHash);
186
+ return new base_1.ClaimEvent(escrowHash, secret, version);
187
+ }
188
+ /**
189
+ * Processes event as received from the chain, parses it & calls event listeners
190
+ *
191
+ * @param eventObject
192
+ * @param version
193
+ * @internal
194
+ */
195
+ async processEvent(eventObject, version) {
196
+ let parsedEvents = eventObject.events.map(event => {
197
+ let parsedEvent;
198
+ switch (event.name) {
199
+ case "ClaimEvent":
200
+ parsedEvent = this.parseClaimEvent(event.data, version);
201
+ break;
202
+ case "RefundEvent":
203
+ parsedEvent = this.parseRefundEvent(event.data, version);
204
+ break;
205
+ case "InitializeEvent":
206
+ parsedEvent = this.parseInitializeEvent(event.data, eventObject, version);
207
+ break;
208
+ }
209
+ if (parsedEvent == null)
210
+ return null;
211
+ parsedEvent.meta = {
212
+ blockTime: eventObject.blockTime,
213
+ timestamp: eventObject.blockTime,
214
+ txId: eventObject.signature
215
+ };
216
+ return parsedEvent;
217
+ }).filter(parsedEvent => parsedEvent != null);
218
+ for (let listener of this.listeners) {
219
+ await listener(parsedEvents);
220
+ }
221
+ }
222
+ /**
223
+ * Returns websocket event handler for specific event type
224
+ *
225
+ * @param name
226
+ * @param version
227
+ * @internal
228
+ * @returns event handler to be passed to program's addEventListener function
229
+ */
230
+ getWsEventHandler(name, version) {
231
+ return (data, slotNumber, signature) => {
232
+ if (this.signaturesProcessing[signature + "-" + version] != null)
233
+ return;
234
+ if (this.isSignatureProcessed(signature, version))
235
+ return;
236
+ this.logger.debug("getWsEventHandler(" + name + "): Process signature: ", signature);
237
+ this.signaturesProcessing[signature + "-" + version] = this.processEvent({
238
+ events: [{ name, data: data }],
239
+ blockTime: Math.floor(Date.now() / 1000),
240
+ signature
241
+ }, version).then(() => true).catch(e => {
242
+ this.logger.error("getWsEventHandler(" + name + "): Error processing signature: " + signature, e);
243
+ return false;
244
+ });
245
+ };
246
+ }
247
+ /**
248
+ * Sets up event handlers listening for swap events over websocket
249
+ *
250
+ * @internal
251
+ */
252
+ setupWebsocket() {
253
+ var _a;
254
+ for (let version in this.contractVersions) {
255
+ const program = this.contractVersions[version].swapContract.program;
256
+ const eventListeners = (_a = this.eventListeners)[version] ?? (_a[version] = []);
257
+ eventListeners.push(program.addEventListener("InitializeEvent", this.getWsEventHandler("InitializeEvent", version)));
258
+ eventListeners.push(program.addEventListener("ClaimEvent", this.getWsEventHandler("ClaimEvent", version)));
259
+ eventListeners.push(program.addEventListener("RefundEvent", this.getWsEventHandler("RefundEvent", version)));
260
+ }
261
+ }
262
+ /**
263
+ * Gets all the new signatures from the last processed signature
264
+ *
265
+ * @param lastProcessedSignature
266
+ * @param version
267
+ * @private
268
+ */
269
+ async getNewSignatures(lastProcessedSignature, version) {
270
+ let signatures = [];
271
+ let fetched = null;
272
+ while (fetched == null || fetched.length === this.logFetchLimit) {
273
+ if (signatures.length === 0) {
274
+ fetched = await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
275
+ until: lastProcessedSignature.signature,
276
+ limit: this.logFetchLimit
277
+ }, "confirmed");
278
+ //Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
279
+ if (fetched.length > 0 && fetched[0].slot < lastProcessedSignature.slot) {
280
+ this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
281
+ return null;
282
+ }
283
+ }
284
+ else {
285
+ fetched = await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
286
+ before: signatures[signatures.length - 1].signature,
287
+ until: lastProcessedSignature.signature,
288
+ limit: this.logFetchLimit
289
+ }, "confirmed");
290
+ }
291
+ signatures = signatures.concat(fetched);
292
+ }
293
+ return signatures;
294
+ }
295
+ /**
296
+ * Gets single latest known signature
297
+ *
298
+ * @private
299
+ */
300
+ async getFirstSignature(version) {
301
+ return await this.connection.getSignaturesForAddress(this.contractVersions[version].swapContract.program.programId, {
302
+ limit: 1
303
+ }, "confirmed");
304
+ }
305
+ /**
306
+ * Processes signatures, fetches transactions & processes event through event handlers
307
+ *
308
+ * @param signatures
309
+ * @param version
310
+ * @private
311
+ * @returns {Promise<{signature: string, slot: number}>} latest processed transaction signature and slot height
312
+ */
313
+ async processSignatures(signatures, version) {
314
+ let lastSuccessfulSignature = null;
315
+ try {
316
+ for (let i = signatures.length - 1; i >= 0; i--) {
317
+ const txSignature = signatures[i];
318
+ //Check if signature is already being processed by the
319
+ const signaturePromise = this.signaturesProcessing[txSignature.signature + "-" + version];
320
+ if (signaturePromise != null) {
321
+ const result = await signaturePromise;
322
+ delete this.signaturesProcessing[txSignature.signature + "-" + version];
323
+ if (result) {
324
+ lastSuccessfulSignature = txSignature;
325
+ this.addProcessedSignature(txSignature.signature, version);
326
+ continue;
327
+ }
328
+ }
329
+ this.logger.debug("processSignatures(): Process signature: ", txSignature);
330
+ const processPromise = this.fetchTxAndProcessEvent(txSignature.signature, version);
331
+ this.signaturesProcessing[txSignature.signature + "-" + version] = processPromise;
332
+ const result = await processPromise;
333
+ if (!result)
334
+ throw new Error("Failed to process signature: " + txSignature);
335
+ lastSuccessfulSignature = txSignature;
336
+ this.addProcessedSignature(txSignature.signature, version);
337
+ delete this.signaturesProcessing[txSignature.signature + "-" + version];
338
+ }
339
+ }
340
+ catch (e) {
341
+ this.logger.error("processSignatures(): Failed processing signatures: ", e);
342
+ }
343
+ return lastSuccessfulSignature;
344
+ }
345
+ /**
346
+ * @inheritDoc
347
+ */
348
+ async poll(lastSignature) {
349
+ const _lastSignature = toNewEventListenerState(lastSignature);
350
+ const result = {};
351
+ for (let version in this.contractVersions) {
352
+ const lastSignature = _lastSignature?.[version];
353
+ let signatures = lastSignature == null
354
+ ? await this.getFirstSignature(version)
355
+ : await this.getNewSignatures(lastSignature, version);
356
+ if (signatures == null) {
357
+ result[version] = lastSignature ?? null;
358
+ }
359
+ else {
360
+ let lastSuccessfulSignature = await this.processSignatures(signatures, version);
361
+ result[version] = lastSuccessfulSignature ?? lastSignature ?? null;
362
+ }
363
+ }
364
+ return result;
365
+ }
366
+ /**
367
+ * @inheritDoc
368
+ */
369
+ init(noAutomaticPoll) {
370
+ if (noAutomaticPoll)
371
+ return Promise.resolve();
372
+ this.setupWebsocket();
373
+ return Promise.resolve();
374
+ }
375
+ /**
376
+ * @inheritDoc
377
+ */
378
+ async stop() {
379
+ for (let version in this.eventListeners) {
380
+ for (let num of this.eventListeners[version]) {
381
+ await this.contractVersions[version].swapContract.program.removeEventListener(num);
382
+ }
383
+ }
384
+ this.eventListeners = {};
385
+ }
386
+ /**
387
+ * @inheritDoc
388
+ */
389
+ registerListener(cbk) {
390
+ this.listeners.push(cbk);
391
+ }
392
+ /**
393
+ * @inheritDoc
394
+ */
395
+ unregisterListener(cbk) {
396
+ const index = this.listeners.indexOf(cbk);
397
+ if (index >= 0) {
398
+ this.listeners.splice(index, 1);
399
+ return true;
400
+ }
401
+ return false;
402
+ }
403
+ }
404
+ exports.SolanaChainEventsBrowser = SolanaChainEventsBrowser;