@atomiqlabs/chain-solana 10.0.0-dev.3 → 11.0.0
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/dist/index.d.ts +29 -29
- package/dist/index.js +45 -45
- package/dist/solana/SolanaChainType.d.ts +10 -10
- package/dist/solana/SolanaChainType.js +2 -2
- package/dist/solana/SolanaChains.d.ts +20 -20
- package/dist/solana/SolanaChains.js +25 -25
- package/dist/solana/SolanaInitializer.d.ts +18 -18
- package/dist/solana/SolanaInitializer.js +63 -63
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
- package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
- package/dist/solana/btcrelay/program/programIdl.json +671 -671
- package/dist/solana/chain/SolanaAction.d.ts +26 -26
- package/dist/solana/chain/SolanaAction.js +86 -86
- package/dist/solana/chain/SolanaChainInterface.d.ts +58 -58
- package/dist/solana/chain/SolanaChainInterface.js +112 -112
- 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 +28 -28
- package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
- package/dist/solana/chain/modules/SolanaEvents.d.ts +25 -25
- package/dist/solana/chain/modules/SolanaEvents.js +58 -58
- package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
- package/dist/solana/chain/modules/SolanaFees.js +379 -379
- package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
- package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
- package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
- package/dist/solana/chain/modules/SolanaSlots.js +68 -68
- package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
- package/dist/solana/chain/modules/SolanaTokens.js +248 -248
- package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
- package/dist/solana/chain/modules/SolanaTransactions.js +332 -332
- package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
- package/dist/solana/events/SolanaChainEvents.js +256 -256
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -85
- package/dist/solana/events/SolanaChainEventsBrowser.js +194 -194
- package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
- package/dist/solana/program/SolanaProgramBase.js +43 -43
- 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 +59 -59
- package/dist/solana/program/modules/SolanaProgramEvents.js +103 -103
- package/dist/solana/swaps/SolanaSwapData.d.ts +59 -59
- package/dist/solana/swaps/SolanaSwapData.js +267 -267
- package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
- package/dist/solana/swaps/SolanaSwapModule.js +11 -11
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +202 -202
- package/dist/solana/swaps/SolanaSwapProgram.js +470 -463
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
- package/dist/solana/swaps/SwapTypeEnum.js +42 -42
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
- package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
- package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
- package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
- package/dist/solana/swaps/modules/SwapClaim.js +291 -291
- package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
- package/dist/solana/swaps/modules/SwapInit.js +519 -519
- package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
- package/dist/solana/swaps/modules/SwapRefund.js +252 -252
- 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/wallet/SolanaKeypairWallet.d.ts +9 -9
- package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
- package/dist/solana/wallet/SolanaSigner.d.ts +10 -10
- package/dist/solana/wallet/SolanaSigner.js +16 -16
- package/dist/utils/Utils.d.ts +53 -53
- package/dist/utils/Utils.js +170 -170
- package/package.json +41 -41
- package/src/index.ts +36 -36
- package/src/solana/SolanaChainType.ts +25 -25
- package/src/solana/SolanaChains.ts +23 -23
- package/src/solana/SolanaInitializer.ts +102 -102
- package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -588
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
- package/src/solana/btcrelay/program/programIdl.json +670 -670
- package/src/solana/chain/SolanaAction.ts +108 -108
- package/src/solana/chain/SolanaChainInterface.ts +174 -174
- package/src/solana/chain/SolanaModule.ts +20 -20
- package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
- package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
- package/src/solana/chain/modules/SolanaEvents.ts +56 -56
- package/src/solana/chain/modules/SolanaFees.ts +450 -450
- package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
- package/src/solana/chain/modules/SolanaSlots.ts +82 -82
- package/src/solana/chain/modules/SolanaTokens.ts +307 -307
- package/src/solana/chain/modules/SolanaTransactions.ts +370 -370
- package/src/solana/events/SolanaChainEvents.ts +299 -299
- package/src/solana/events/SolanaChainEventsBrowser.ts +256 -256
- package/src/solana/program/SolanaProgramBase.ts +79 -79
- package/src/solana/program/SolanaProgramModule.ts +15 -15
- package/src/solana/program/modules/SolanaProgramEvents.ts +140 -140
- package/src/solana/swaps/SolanaSwapData.ts +379 -379
- package/src/solana/swaps/SolanaSwapModule.ts +16 -16
- package/src/solana/swaps/SolanaSwapProgram.ts +697 -692
- package/src/solana/swaps/SwapTypeEnum.ts +29 -29
- package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
- package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
- package/src/solana/swaps/modules/SwapClaim.ts +389 -389
- package/src/solana/swaps/modules/SwapInit.ts +663 -663
- package/src/solana/swaps/modules/SwapRefund.ts +312 -312
- package/src/solana/swaps/programIdl.json +944 -944
- package/src/solana/swaps/programTypes.ts +1885 -1885
- package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
- package/src/solana/wallet/SolanaSigner.ts +23 -23
- package/src/utils/Utils.ts +180 -180
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SolanaChainEvents = void 0;
|
|
4
|
-
const fs = require("fs/promises");
|
|
5
|
-
const SolanaChainEventsBrowser_1 = require("./SolanaChainEventsBrowser");
|
|
6
|
-
const BLOCKHEIGHT_FILENAME = "/blockheight.txt";
|
|
7
|
-
const LOG_FETCH_INTERVAL = 5 * 1000;
|
|
8
|
-
const LOG_FETCH_LIMIT = 500;
|
|
9
|
-
const PROCESSED_SIGNATURES_BACKLOG = 100;
|
|
10
|
-
/**
|
|
11
|
-
* Event handler for backend Node.js systems with access to fs, uses HTTP polling in combination with WS to not miss
|
|
12
|
-
* any events
|
|
13
|
-
*/
|
|
14
|
-
class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrowser {
|
|
15
|
-
constructor(directory, connection, solanaSwapProgram, logFetchInterval, logFetchLimit) {
|
|
16
|
-
super(connection, solanaSwapProgram);
|
|
17
|
-
this.signaturesProcessing = {};
|
|
18
|
-
this.processedSignatures = [];
|
|
19
|
-
this.processedSignaturesIndex = 0;
|
|
20
|
-
this.directory = directory;
|
|
21
|
-
this.logFetchInterval = logFetchInterval || LOG_FETCH_INTERVAL;
|
|
22
|
-
this.logFetchLimit = logFetchLimit || LOG_FETCH_LIMIT;
|
|
23
|
-
}
|
|
24
|
-
addProcessedSignature(signature) {
|
|
25
|
-
this.processedSignatures[this.processedSignaturesIndex] = signature;
|
|
26
|
-
this.processedSignaturesIndex += 1;
|
|
27
|
-
if (this.processedSignaturesIndex >= PROCESSED_SIGNATURES_BACKLOG)
|
|
28
|
-
this.processedSignaturesIndex = 0;
|
|
29
|
-
}
|
|
30
|
-
isSignatureProcessed(signature) {
|
|
31
|
-
return this.processedSignatures.includes(signature);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Retrieves last signature & slot from filesystem
|
|
35
|
-
*
|
|
36
|
-
* @private
|
|
37
|
-
*/
|
|
38
|
-
async getLastSignature() {
|
|
39
|
-
try {
|
|
40
|
-
const txt = (await fs.readFile(this.directory + BLOCKHEIGHT_FILENAME)).toString();
|
|
41
|
-
const arr = txt.split(";");
|
|
42
|
-
if (arr.length < 2)
|
|
43
|
-
return {
|
|
44
|
-
signature: txt,
|
|
45
|
-
slot: 0
|
|
46
|
-
};
|
|
47
|
-
return {
|
|
48
|
-
signature: arr[0],
|
|
49
|
-
slot: parseInt(arr[1])
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
catch (e) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Saves last signature & slot to the filesystem
|
|
58
|
-
*
|
|
59
|
-
* @private
|
|
60
|
-
*/
|
|
61
|
-
saveLastSignature(lastSignature, slot) {
|
|
62
|
-
return fs.writeFile(this.directory + BLOCKHEIGHT_FILENAME, lastSignature + ";" + slot);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Parses EventObject from the transaction
|
|
66
|
-
*
|
|
67
|
-
* @param transaction
|
|
68
|
-
* @private
|
|
69
|
-
* @returns {EventObject} parsed event object
|
|
70
|
-
*/
|
|
71
|
-
getEventObjectFromTransaction(transaction) {
|
|
72
|
-
if (transaction.meta.err != null)
|
|
73
|
-
return null;
|
|
74
|
-
const instructions = this.solanaSwapProgram.Events.decodeInstructions(transaction.transaction.message);
|
|
75
|
-
const events = this.solanaSwapProgram.Events.parseLogs(transaction.meta.logMessages);
|
|
76
|
-
return {
|
|
77
|
-
instructions,
|
|
78
|
-
events,
|
|
79
|
-
blockTime: transaction.blockTime,
|
|
80
|
-
signature: transaction.transaction.signatures[0]
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Fetches transaction from the RPC, parses it to even object & processes it through event handler
|
|
85
|
-
*
|
|
86
|
-
* @param signature
|
|
87
|
-
* @private
|
|
88
|
-
* @returns {boolean} whether the operation was successful
|
|
89
|
-
*/
|
|
90
|
-
async fetchTxAndProcessEvent(signature) {
|
|
91
|
-
try {
|
|
92
|
-
const transaction = await this.connection.getParsedTransaction(signature, {
|
|
93
|
-
commitment: "confirmed",
|
|
94
|
-
maxSupportedTransactionVersion:
|
|
95
|
-
});
|
|
96
|
-
if (transaction == null)
|
|
97
|
-
return false;
|
|
98
|
-
const eventObject = this.getEventObjectFromTransaction(transaction);
|
|
99
|
-
if (eventObject == null)
|
|
100
|
-
return true;
|
|
101
|
-
await this.processEvent(eventObject);
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: " + signature, e);
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Returns websocket event handler for specific event type
|
|
111
|
-
*
|
|
112
|
-
* @param name
|
|
113
|
-
* @protected
|
|
114
|
-
* @returns event handler to be passed to program's addEventListener function
|
|
115
|
-
*/
|
|
116
|
-
getWsEventHandler(name) {
|
|
117
|
-
return (data, slotNumber, signature) => {
|
|
118
|
-
if (this.signaturesProcessing[signature] != null)
|
|
119
|
-
return;
|
|
120
|
-
if (this.isSignatureProcessed(signature))
|
|
121
|
-
return;
|
|
122
|
-
this.logger.debug("getWsEventHandler(" + name + "): Process signature: ", signature);
|
|
123
|
-
this.signaturesProcessing[signature] = this.processEvent({
|
|
124
|
-
events: [{ name, data: data }],
|
|
125
|
-
instructions: null,
|
|
126
|
-
blockTime: Math.floor(Date.now() / 1000),
|
|
127
|
-
signature
|
|
128
|
-
}).then(() => true).catch(e => {
|
|
129
|
-
this.logger.error("getWsEventHandler(" + name + "): Error processing signature: " + signature, e);
|
|
130
|
-
return false;
|
|
131
|
-
});
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Gets all the new signatures from the last processed signature
|
|
136
|
-
*
|
|
137
|
-
* @param lastProcessedSignature
|
|
138
|
-
* @private
|
|
139
|
-
*/
|
|
140
|
-
async getNewSignatures(lastProcessedSignature) {
|
|
141
|
-
let signatures = [];
|
|
142
|
-
let fetched = null;
|
|
143
|
-
while (fetched == null || fetched.length === this.logFetchLimit) {
|
|
144
|
-
if (signatures.length === 0) {
|
|
145
|
-
fetched = await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
146
|
-
until: lastProcessedSignature.signature,
|
|
147
|
-
limit: this.logFetchLimit
|
|
148
|
-
}, "confirmed");
|
|
149
|
-
//Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
|
|
150
|
-
if (fetched.length > 0 && fetched[0].slot < lastProcessedSignature.slot) {
|
|
151
|
-
this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
fetched = await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
157
|
-
before: signatures[signatures.length - 1].signature,
|
|
158
|
-
until: lastProcessedSignature.signature,
|
|
159
|
-
limit: this.logFetchLimit
|
|
160
|
-
}, "confirmed");
|
|
161
|
-
}
|
|
162
|
-
signatures = signatures.concat(fetched);
|
|
163
|
-
}
|
|
164
|
-
return signatures;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Gets single latest known signature
|
|
168
|
-
*
|
|
169
|
-
* @private
|
|
170
|
-
*/
|
|
171
|
-
async getFirstSignature() {
|
|
172
|
-
return await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
173
|
-
limit: 1
|
|
174
|
-
}, "confirmed");
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Processes signatures, fetches transactions & processes event through event handlers
|
|
178
|
-
*
|
|
179
|
-
* @param signatures
|
|
180
|
-
* @private
|
|
181
|
-
* @returns {Promise<{signature: string, slot: number}>} latest processed transaction signature and slot height
|
|
182
|
-
*/
|
|
183
|
-
async processSignatures(signatures) {
|
|
184
|
-
let lastSuccessfulSignature = null;
|
|
185
|
-
try {
|
|
186
|
-
for (let i = signatures.length - 1; i >= 0; i--) {
|
|
187
|
-
const txSignature = signatures[i];
|
|
188
|
-
//Check if signature is already being processed by the
|
|
189
|
-
const signaturePromise = this.signaturesProcessing[txSignature.signature];
|
|
190
|
-
if (signaturePromise != null) {
|
|
191
|
-
const result = await signaturePromise;
|
|
192
|
-
delete this.signaturesProcessing[txSignature.signature];
|
|
193
|
-
if (result) {
|
|
194
|
-
lastSuccessfulSignature = txSignature;
|
|
195
|
-
this.addProcessedSignature(txSignature.signature);
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
this.logger.debug("processSignatures(): Process signature: ", txSignature);
|
|
200
|
-
const processPromise = this.fetchTxAndProcessEvent(txSignature.signature);
|
|
201
|
-
this.signaturesProcessing[txSignature.signature] = processPromise;
|
|
202
|
-
const result = await processPromise;
|
|
203
|
-
if (!result)
|
|
204
|
-
throw new Error("Failed to process signature: " + txSignature);
|
|
205
|
-
lastSuccessfulSignature = txSignature;
|
|
206
|
-
this.addProcessedSignature(txSignature.signature);
|
|
207
|
-
delete this.signaturesProcessing[txSignature.signature];
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
catch (e) {
|
|
211
|
-
this.logger.error("processSignatures(): Failed processing signatures: ", e);
|
|
212
|
-
}
|
|
213
|
-
return lastSuccessfulSignature;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Polls for new events & processes them
|
|
217
|
-
*
|
|
218
|
-
* @private
|
|
219
|
-
*/
|
|
220
|
-
async checkEvents() {
|
|
221
|
-
const lastSignature = await this.getLastSignature();
|
|
222
|
-
let signatures = lastSignature == null ? await this.getFirstSignature() : await this.getNewSignatures(lastSignature);
|
|
223
|
-
let lastSuccessfulSignature = await this.processSignatures(signatures);
|
|
224
|
-
if (lastSuccessfulSignature != null) {
|
|
225
|
-
await this.saveLastSignature(lastSuccessfulSignature.signature, lastSuccessfulSignature.slot);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
async setupHttpPolling() {
|
|
229
|
-
this.stopped = false;
|
|
230
|
-
let func;
|
|
231
|
-
func = async () => {
|
|
232
|
-
await this.checkEvents().catch(e => {
|
|
233
|
-
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
234
|
-
});
|
|
235
|
-
if (this.stopped)
|
|
236
|
-
return;
|
|
237
|
-
this.timeout = setTimeout(func, this.logFetchInterval);
|
|
238
|
-
};
|
|
239
|
-
await func();
|
|
240
|
-
}
|
|
241
|
-
async init() {
|
|
242
|
-
try {
|
|
243
|
-
await fs.mkdir(this.directory);
|
|
244
|
-
}
|
|
245
|
-
catch (e) { }
|
|
246
|
-
await this.setupHttpPolling();
|
|
247
|
-
this.setupWebsocket();
|
|
248
|
-
}
|
|
249
|
-
stop() {
|
|
250
|
-
this.stopped = true;
|
|
251
|
-
if (this.timeout != null)
|
|
252
|
-
clearTimeout(this.timeout);
|
|
253
|
-
return super.stop();
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
exports.SolanaChainEvents = SolanaChainEvents;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SolanaChainEvents = void 0;
|
|
4
|
+
const fs = require("fs/promises");
|
|
5
|
+
const SolanaChainEventsBrowser_1 = require("./SolanaChainEventsBrowser");
|
|
6
|
+
const BLOCKHEIGHT_FILENAME = "/blockheight.txt";
|
|
7
|
+
const LOG_FETCH_INTERVAL = 5 * 1000;
|
|
8
|
+
const LOG_FETCH_LIMIT = 500;
|
|
9
|
+
const PROCESSED_SIGNATURES_BACKLOG = 100;
|
|
10
|
+
/**
|
|
11
|
+
* Event handler for backend Node.js systems with access to fs, uses HTTP polling in combination with WS to not miss
|
|
12
|
+
* any events
|
|
13
|
+
*/
|
|
14
|
+
class SolanaChainEvents extends SolanaChainEventsBrowser_1.SolanaChainEventsBrowser {
|
|
15
|
+
constructor(directory, connection, solanaSwapProgram, logFetchInterval, logFetchLimit) {
|
|
16
|
+
super(connection, solanaSwapProgram);
|
|
17
|
+
this.signaturesProcessing = {};
|
|
18
|
+
this.processedSignatures = [];
|
|
19
|
+
this.processedSignaturesIndex = 0;
|
|
20
|
+
this.directory = directory;
|
|
21
|
+
this.logFetchInterval = logFetchInterval || LOG_FETCH_INTERVAL;
|
|
22
|
+
this.logFetchLimit = logFetchLimit || LOG_FETCH_LIMIT;
|
|
23
|
+
}
|
|
24
|
+
addProcessedSignature(signature) {
|
|
25
|
+
this.processedSignatures[this.processedSignaturesIndex] = signature;
|
|
26
|
+
this.processedSignaturesIndex += 1;
|
|
27
|
+
if (this.processedSignaturesIndex >= PROCESSED_SIGNATURES_BACKLOG)
|
|
28
|
+
this.processedSignaturesIndex = 0;
|
|
29
|
+
}
|
|
30
|
+
isSignatureProcessed(signature) {
|
|
31
|
+
return this.processedSignatures.includes(signature);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves last signature & slot from filesystem
|
|
35
|
+
*
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
async getLastSignature() {
|
|
39
|
+
try {
|
|
40
|
+
const txt = (await fs.readFile(this.directory + BLOCKHEIGHT_FILENAME)).toString();
|
|
41
|
+
const arr = txt.split(";");
|
|
42
|
+
if (arr.length < 2)
|
|
43
|
+
return {
|
|
44
|
+
signature: txt,
|
|
45
|
+
slot: 0
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
signature: arr[0],
|
|
49
|
+
slot: parseInt(arr[1])
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Saves last signature & slot to the filesystem
|
|
58
|
+
*
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
saveLastSignature(lastSignature, slot) {
|
|
62
|
+
return fs.writeFile(this.directory + BLOCKHEIGHT_FILENAME, lastSignature + ";" + slot);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parses EventObject from the transaction
|
|
66
|
+
*
|
|
67
|
+
* @param transaction
|
|
68
|
+
* @private
|
|
69
|
+
* @returns {EventObject} parsed event object
|
|
70
|
+
*/
|
|
71
|
+
getEventObjectFromTransaction(transaction) {
|
|
72
|
+
if (transaction.meta.err != null)
|
|
73
|
+
return null;
|
|
74
|
+
const instructions = this.solanaSwapProgram.Events.decodeInstructions(transaction.transaction.message);
|
|
75
|
+
const events = this.solanaSwapProgram.Events.parseLogs(transaction.meta.logMessages);
|
|
76
|
+
return {
|
|
77
|
+
instructions,
|
|
78
|
+
events,
|
|
79
|
+
blockTime: transaction.blockTime,
|
|
80
|
+
signature: transaction.transaction.signatures[0]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Fetches transaction from the RPC, parses it to even object & processes it through event handler
|
|
85
|
+
*
|
|
86
|
+
* @param signature
|
|
87
|
+
* @private
|
|
88
|
+
* @returns {boolean} whether the operation was successful
|
|
89
|
+
*/
|
|
90
|
+
async fetchTxAndProcessEvent(signature) {
|
|
91
|
+
try {
|
|
92
|
+
const transaction = await this.connection.getParsedTransaction(signature, {
|
|
93
|
+
commitment: "confirmed",
|
|
94
|
+
maxSupportedTransactionVersion: 1
|
|
95
|
+
});
|
|
96
|
+
if (transaction == null)
|
|
97
|
+
return false;
|
|
98
|
+
const eventObject = this.getEventObjectFromTransaction(transaction);
|
|
99
|
+
if (eventObject == null)
|
|
100
|
+
return true;
|
|
101
|
+
await this.processEvent(eventObject);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
this.logger.error("fetchTxAndProcessEvent(): Error fetching transaction and processing event, signature: " + signature, e);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns websocket event handler for specific event type
|
|
111
|
+
*
|
|
112
|
+
* @param name
|
|
113
|
+
* @protected
|
|
114
|
+
* @returns event handler to be passed to program's addEventListener function
|
|
115
|
+
*/
|
|
116
|
+
getWsEventHandler(name) {
|
|
117
|
+
return (data, slotNumber, signature) => {
|
|
118
|
+
if (this.signaturesProcessing[signature] != null)
|
|
119
|
+
return;
|
|
120
|
+
if (this.isSignatureProcessed(signature))
|
|
121
|
+
return;
|
|
122
|
+
this.logger.debug("getWsEventHandler(" + name + "): Process signature: ", signature);
|
|
123
|
+
this.signaturesProcessing[signature] = this.processEvent({
|
|
124
|
+
events: [{ name, data: data }],
|
|
125
|
+
instructions: null,
|
|
126
|
+
blockTime: Math.floor(Date.now() / 1000),
|
|
127
|
+
signature
|
|
128
|
+
}).then(() => true).catch(e => {
|
|
129
|
+
this.logger.error("getWsEventHandler(" + name + "): Error processing signature: " + signature, e);
|
|
130
|
+
return false;
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Gets all the new signatures from the last processed signature
|
|
136
|
+
*
|
|
137
|
+
* @param lastProcessedSignature
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
async getNewSignatures(lastProcessedSignature) {
|
|
141
|
+
let signatures = [];
|
|
142
|
+
let fetched = null;
|
|
143
|
+
while (fetched == null || fetched.length === this.logFetchLimit) {
|
|
144
|
+
if (signatures.length === 0) {
|
|
145
|
+
fetched = await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
146
|
+
until: lastProcessedSignature.signature,
|
|
147
|
+
limit: this.logFetchLimit
|
|
148
|
+
}, "confirmed");
|
|
149
|
+
//Check if newest returned signature (index 0) is older than the latest signature's slot, this is a sanity check
|
|
150
|
+
if (fetched.length > 0 && fetched[0].slot < lastProcessedSignature.slot) {
|
|
151
|
+
this.logger.debug("getNewSignatures(): Sanity check triggered, returned signature slot height is older than latest!");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
fetched = await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
157
|
+
before: signatures[signatures.length - 1].signature,
|
|
158
|
+
until: lastProcessedSignature.signature,
|
|
159
|
+
limit: this.logFetchLimit
|
|
160
|
+
}, "confirmed");
|
|
161
|
+
}
|
|
162
|
+
signatures = signatures.concat(fetched);
|
|
163
|
+
}
|
|
164
|
+
return signatures;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Gets single latest known signature
|
|
168
|
+
*
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
async getFirstSignature() {
|
|
172
|
+
return await this.connection.getSignaturesForAddress(this.solanaSwapProgram.program.programId, {
|
|
173
|
+
limit: 1
|
|
174
|
+
}, "confirmed");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Processes signatures, fetches transactions & processes event through event handlers
|
|
178
|
+
*
|
|
179
|
+
* @param signatures
|
|
180
|
+
* @private
|
|
181
|
+
* @returns {Promise<{signature: string, slot: number}>} latest processed transaction signature and slot height
|
|
182
|
+
*/
|
|
183
|
+
async processSignatures(signatures) {
|
|
184
|
+
let lastSuccessfulSignature = null;
|
|
185
|
+
try {
|
|
186
|
+
for (let i = signatures.length - 1; i >= 0; i--) {
|
|
187
|
+
const txSignature = signatures[i];
|
|
188
|
+
//Check if signature is already being processed by the
|
|
189
|
+
const signaturePromise = this.signaturesProcessing[txSignature.signature];
|
|
190
|
+
if (signaturePromise != null) {
|
|
191
|
+
const result = await signaturePromise;
|
|
192
|
+
delete this.signaturesProcessing[txSignature.signature];
|
|
193
|
+
if (result) {
|
|
194
|
+
lastSuccessfulSignature = txSignature;
|
|
195
|
+
this.addProcessedSignature(txSignature.signature);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.logger.debug("processSignatures(): Process signature: ", txSignature);
|
|
200
|
+
const processPromise = this.fetchTxAndProcessEvent(txSignature.signature);
|
|
201
|
+
this.signaturesProcessing[txSignature.signature] = processPromise;
|
|
202
|
+
const result = await processPromise;
|
|
203
|
+
if (!result)
|
|
204
|
+
throw new Error("Failed to process signature: " + txSignature);
|
|
205
|
+
lastSuccessfulSignature = txSignature;
|
|
206
|
+
this.addProcessedSignature(txSignature.signature);
|
|
207
|
+
delete this.signaturesProcessing[txSignature.signature];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
this.logger.error("processSignatures(): Failed processing signatures: ", e);
|
|
212
|
+
}
|
|
213
|
+
return lastSuccessfulSignature;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Polls for new events & processes them
|
|
217
|
+
*
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
async checkEvents() {
|
|
221
|
+
const lastSignature = await this.getLastSignature();
|
|
222
|
+
let signatures = lastSignature == null ? await this.getFirstSignature() : await this.getNewSignatures(lastSignature);
|
|
223
|
+
let lastSuccessfulSignature = await this.processSignatures(signatures);
|
|
224
|
+
if (lastSuccessfulSignature != null) {
|
|
225
|
+
await this.saveLastSignature(lastSuccessfulSignature.signature, lastSuccessfulSignature.slot);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async setupHttpPolling() {
|
|
229
|
+
this.stopped = false;
|
|
230
|
+
let func;
|
|
231
|
+
func = async () => {
|
|
232
|
+
await this.checkEvents().catch(e => {
|
|
233
|
+
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
234
|
+
});
|
|
235
|
+
if (this.stopped)
|
|
236
|
+
return;
|
|
237
|
+
this.timeout = setTimeout(func, this.logFetchInterval);
|
|
238
|
+
};
|
|
239
|
+
await func();
|
|
240
|
+
}
|
|
241
|
+
async init() {
|
|
242
|
+
try {
|
|
243
|
+
await fs.mkdir(this.directory);
|
|
244
|
+
}
|
|
245
|
+
catch (e) { }
|
|
246
|
+
await this.setupHttpPolling();
|
|
247
|
+
this.setupWebsocket();
|
|
248
|
+
}
|
|
249
|
+
stop() {
|
|
250
|
+
this.stopped = true;
|
|
251
|
+
if (this.timeout != null)
|
|
252
|
+
clearTimeout(this.timeout);
|
|
253
|
+
return super.stop();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
exports.SolanaChainEvents = SolanaChainEvents;
|