@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.
- package/LICENSE +201 -201
- package/README.md +73 -73
- package/dist/index.d.ts +81 -81
- package/dist/index.js +102 -102
- package/dist/node/index.d.ts +9 -9
- package/dist/node/index.js +13 -13
- package/dist/solana/SolanaChainType.d.ts +15 -15
- package/dist/solana/SolanaChainType.js +2 -2
- package/dist/solana/SolanaChains.d.ts +12 -12
- package/dist/solana/SolanaChains.js +45 -45
- package/dist/solana/SolanaInitializer.d.ts +94 -94
- package/dist/solana/SolanaInitializer.js +174 -174
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +222 -222
- package/dist/solana/btcrelay/SolanaBtcRelay.js +455 -455
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +84 -84
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +70 -70
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +92 -92
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +109 -109
- package/dist/solana/btcrelay/program/programIdl.json +671 -671
- package/dist/solana/chain/SolanaAction.d.ts +26 -26
- package/dist/solana/chain/SolanaAction.js +87 -87
- package/dist/solana/chain/SolanaChainInterface.d.ts +224 -224
- package/dist/solana/chain/SolanaChainInterface.js +275 -275
- package/dist/solana/chain/SolanaModule.d.ts +14 -14
- package/dist/solana/chain/SolanaModule.js +13 -13
- package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
- package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
- package/dist/solana/chain/modules/SolanaBlocks.d.ts +32 -32
- package/dist/solana/chain/modules/SolanaBlocks.js +78 -78
- package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
- package/dist/solana/chain/modules/SolanaEvents.js +238 -238
- package/dist/solana/chain/modules/SolanaFees.d.ts +189 -189
- package/dist/solana/chain/modules/SolanaFees.js +434 -434
- package/dist/solana/chain/modules/SolanaSignatures.d.ts +24 -24
- package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
- package/dist/solana/chain/modules/SolanaSlots.d.ts +33 -33
- package/dist/solana/chain/modules/SolanaSlots.js +72 -72
- package/dist/solana/chain/modules/SolanaTokens.d.ts +123 -123
- package/dist/solana/chain/modules/SolanaTokens.js +242 -242
- package/dist/solana/chain/modules/SolanaTransactions.d.ts +149 -149
- package/dist/solana/chain/modules/SolanaTransactions.js +445 -445
- package/dist/solana/connection/ConnectionWithRetries.d.ts +35 -35
- package/dist/solana/connection/ConnectionWithRetries.js +86 -71
- package/dist/solana/events/SolanaChainEvents.d.ts +45 -45
- package/dist/solana/events/SolanaChainEvents.js +108 -108
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +205 -205
- package/dist/solana/events/SolanaChainEventsBrowser.js +404 -404
- package/dist/solana/program/SolanaProgramBase.d.ts +73 -73
- package/dist/solana/program/SolanaProgramBase.js +54 -54
- package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
- package/dist/solana/program/SolanaProgramModule.js +11 -11
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
- package/dist/solana/program/modules/SolanaProgramEvents.js +117 -117
- package/dist/solana/swaps/SolanaSwapData.d.ts +333 -333
- package/dist/solana/swaps/SolanaSwapData.js +535 -535
- package/dist/solana/swaps/SolanaSwapModule.d.ts +11 -11
- package/dist/solana/swaps/SolanaSwapModule.js +12 -12
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +376 -376
- package/dist/solana/swaps/SolanaSwapProgram.js +769 -769
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
- package/dist/solana/swaps/SwapTypeEnum.js +43 -43
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +95 -95
- package/dist/solana/swaps/modules/SolanaDataAccount.js +232 -232
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +69 -69
- package/dist/solana/swaps/modules/SolanaLpVault.js +171 -171
- package/dist/solana/swaps/modules/SwapClaim.d.ts +126 -126
- package/dist/solana/swaps/modules/SwapClaim.js +294 -294
- package/dist/solana/swaps/modules/SwapInit.d.ts +213 -213
- package/dist/solana/swaps/modules/SwapInit.js +658 -658
- package/dist/solana/swaps/modules/SwapRefund.d.ts +87 -87
- package/dist/solana/swaps/modules/SwapRefund.js +293 -293
- package/dist/solana/swaps/programIdl.json +945 -945
- package/dist/solana/swaps/programTypes.d.ts +943 -943
- package/dist/solana/swaps/programTypes.js +945 -945
- package/dist/solana/swaps/v1/programIdl.json +945 -945
- package/dist/solana/swaps/v1/programTypes.d.ts +943 -943
- package/dist/solana/swaps/v1/programTypes.js +945 -945
- package/dist/solana/swaps/v2/programIdl.json +952 -952
- package/dist/solana/swaps/v2/programTypes.d.ts +950 -950
- package/dist/solana/swaps/v2/programTypes.js +952 -952
- package/dist/solana/wallet/SolanaKeypairWallet.d.ts +29 -29
- package/dist/solana/wallet/SolanaKeypairWallet.js +50 -50
- package/dist/solana/wallet/SolanaSigner.d.ts +30 -30
- package/dist/solana/wallet/SolanaSigner.js +30 -30
- package/dist/utils/Utils.d.ts +58 -58
- package/dist/utils/Utils.js +170 -170
- package/node/index.d.ts +1 -1
- package/node/index.js +3 -3
- package/package.json +46 -46
- package/src/index.ts +87 -87
- package/src/node/index.ts +9 -9
- package/src/solana/SolanaChainType.ts +32 -32
- package/src/solana/SolanaChains.ts +46 -46
- package/src/solana/SolanaInitializer.ts +278 -278
- package/src/solana/btcrelay/SolanaBtcRelay.ts +615 -615
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +116 -116
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +148 -148
- package/src/solana/btcrelay/program/programIdl.json +670 -670
- package/src/solana/chain/SolanaAction.ts +109 -109
- package/src/solana/chain/SolanaChainInterface.ts +404 -404
- package/src/solana/chain/SolanaModule.ts +20 -20
- package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
- package/src/solana/chain/modules/SolanaBlocks.ts +89 -89
- package/src/solana/chain/modules/SolanaEvents.ts +271 -271
- package/src/solana/chain/modules/SolanaFees.ts +522 -522
- package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
- package/src/solana/chain/modules/SolanaSlots.ts +85 -85
- package/src/solana/chain/modules/SolanaTokens.ts +300 -300
- package/src/solana/chain/modules/SolanaTransactions.ts +503 -503
- package/src/solana/connection/ConnectionWithRetries.ts +113 -96
- package/src/solana/events/SolanaChainEvents.ts +127 -127
- package/src/solana/events/SolanaChainEventsBrowser.ts +495 -495
- package/src/solana/program/SolanaProgramBase.ts +119 -119
- package/src/solana/program/SolanaProgramModule.ts +15 -15
- package/src/solana/program/modules/SolanaProgramEvents.ts +157 -157
- package/src/solana/swaps/SolanaSwapData.ts +735 -735
- package/src/solana/swaps/SolanaSwapModule.ts +19 -19
- package/src/solana/swaps/SolanaSwapProgram.ts +1074 -1074
- package/src/solana/swaps/SwapTypeEnum.ts +30 -30
- package/src/solana/swaps/modules/SolanaDataAccount.ts +302 -302
- package/src/solana/swaps/modules/SolanaLpVault.ts +208 -208
- package/src/solana/swaps/modules/SwapClaim.ts +387 -387
- package/src/solana/swaps/modules/SwapInit.ts +785 -785
- package/src/solana/swaps/modules/SwapRefund.ts +353 -353
- package/src/solana/swaps/v1/programIdl.json +944 -944
- package/src/solana/swaps/v1/programTypes.ts +1885 -1885
- package/src/solana/swaps/v2/programIdl.json +951 -951
- package/src/solana/swaps/v2/programTypes.ts +1899 -1899
- package/src/solana/wallet/SolanaKeypairWallet.ts +56 -56
- package/src/solana/wallet/SolanaSigner.ts +43 -43
- 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;
|