@atomiqlabs/chain-solana 13.5.10 → 13.5.12

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.
@@ -50,18 +50,23 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
50
50
  watchdogInterval = setInterval(async () => {
51
51
  const result = await this.sendRawTransaction(rawTx, { skipPreflight: true }).catch(e => this.logger.error("txConfirmationAndResendWatchdog(): transaction re-sent error: ", e));
52
52
  this.logger.debug("txConfirmationAndResendWatchdog(): transaction re-sent: " + result);
53
- const status = await this.getTxIdStatus(signature, finality).catch(e => this.logger.error("txConfirmationAndResendWatchdog(): get tx id status error: ", e));
53
+ let status = await this.getTxIdStatus(signature, finality).catch(e => this.logger.error("txConfirmationAndResendWatchdog(): get tx id status error: ", e));
54
54
  if (status == null || status === "not_found") {
55
- if (await this.connection.isBlockhashValid(tx.recentBlockhash, { commitment: finality }))
55
+ const blockValidity = await this.connection.isBlockhashValid(tx.recentBlockhash, { commitment: finality }).catch(e => this.logger.error("txConfirmationAndResendWatchdog(): blockhash validity check error: ", e));
56
+ if (!blockValidity)
57
+ return;
58
+ if (blockValidity.value)
56
59
  return;
57
60
  try {
58
- //One list try to get the txId status
61
+ //One last try to get the txId status
59
62
  const statusCheck = await this.getTxIdStatus(signature, finality);
60
- if (statusCheck == "not_found")
63
+ if (statusCheck === "not_found")
61
64
  reject(new Error("Transaction expired before confirmation, please try again!"));
65
+ status = statusCheck;
62
66
  }
63
67
  catch (e) {
64
68
  this.logger.error("txConfirmationAndResendWatchdog(): re-check get tx id status error: ", e);
69
+ return;
65
70
  }
66
71
  }
67
72
  if (status === "success") {
@@ -95,14 +100,22 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
95
100
  const signature = bs58.encode(tx.signature);
96
101
  let result;
97
102
  try {
98
- result = await this.connection.confirmTransaction(tx.lastValidBlockHeight == null
99
- ? signature
100
- : {
101
- signature: signature,
102
- blockhash: tx.recentBlockhash,
103
- lastValidBlockHeight: tx.lastValidBlockHeight,
104
- abortSignal
103
+ result = await new Promise((resolve, reject) => {
104
+ let subscriptionId;
105
+ if (abortSignal != null)
106
+ abortSignal.addEventListener("abort", () => {
107
+ if (subscriptionId != null)
108
+ this.connection.removeSignatureListener(subscriptionId).catch(e => {
109
+ this.logger.debug("txConfirmFromWebsocket(): remove WS signature confirm listener error: ", e);
110
+ });
111
+ subscriptionId = undefined;
112
+ reject(abortSignal.reason);
113
+ });
114
+ subscriptionId = this.connection.onSignature(signature, (data) => {
115
+ resolve(data);
116
+ subscriptionId = undefined;
105
117
  }, finality);
118
+ });
106
119
  this.logger.info("txConfirmFromWebsocket(): transaction confirmed from WS, signature: " + signature);
107
120
  }
108
121
  catch (err) {
@@ -122,7 +135,7 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
122
135
  throw err;
123
136
  }
124
137
  }
125
- if (result.value.err != null)
138
+ if (result.err != null)
126
139
  throw new base_1.TransactionRevertedError("Transaction reverted!");
127
140
  return signature;
128
141
  }
@@ -137,10 +150,12 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
137
150
  */
138
151
  async confirmTransaction(tx, abortSignal, finality) {
139
152
  const abortController = new AbortController();
140
- if (abortSignal != null)
153
+ if (abortSignal != null) {
154
+ abortSignal.throwIfAborted();
141
155
  abortSignal.addEventListener("abort", () => {
142
- abortController.abort();
156
+ abortController.abort(abortSignal.reason);
143
157
  });
158
+ }
144
159
  let txSignature;
145
160
  try {
146
161
  txSignature = await Promise.race([
@@ -254,8 +269,12 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
254
269
  const signature = await this.sendSignedTransaction(solTx.tx, options, onBeforePublish);
255
270
  const confirmPromise = this.confirmTransaction(solTx.tx, abortSignal, "confirmed");
256
271
  //Don't await the last promise when !waitForConfirmation
257
- if (i < txs.length - 1 || e + 50 < _txs.length || waitForConfirmation)
272
+ if (i < txs.length - 1 || e + 50 < _txs.length || waitForConfirmation) {
258
273
  await confirmPromise;
274
+ }
275
+ else {
276
+ confirmPromise.catch(err => this.logger.error(`sendAndConfirm(): Error while awaiting confirmation of ${signature}: `, err));
277
+ }
259
278
  signatures.push(signature);
260
279
  }
261
280
  }
@@ -279,25 +298,43 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
279
298
  };
280
299
  this.logger.debug("sendSignedAndConfirm(): sending transactions, count: " + signedTxs.length +
281
300
  " waitForConfirmation: " + waitForConfirmation + " parallel: " + parallel);
301
+ const abortController = new AbortController();
302
+ if (abortSignal != null) {
303
+ abortSignal.throwIfAborted();
304
+ abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
305
+ }
282
306
  const signatures = [];
283
307
  const promises = [];
284
308
  for (let i = 0; i < signedTxs.length; i++) {
285
309
  const signedTx = signedTxs[i];
286
310
  this.logger.debug("sendSignedAndConfirm(): sending transaction " + i + ", total count: " + signedTxs.length);
287
311
  const signature = await this.sendSignedTransaction(signedTx, options, onBeforePublish);
288
- const confirmPromise = this.confirmTransaction(signedTx, abortSignal, "confirmed");
312
+ if (abortSignal != null)
313
+ abortSignal.throwIfAborted();
314
+ const confirmPromise = this.confirmTransaction(signedTx, abortController.signal, "confirmed");
315
+ signatures.push(signature);
289
316
  if (!parallel) {
290
317
  //Don't await the last one when not wait for confirmations
291
- if (i < signedTxs.length - 1 || waitForConfirmation)
318
+ if (i < signedTxs.length - 1 || waitForConfirmation) {
292
319
  await confirmPromise;
320
+ continue;
321
+ }
293
322
  }
294
323
  else {
295
- promises.push(confirmPromise);
324
+ if (waitForConfirmation) {
325
+ promises.push(confirmPromise.catch(err => {
326
+ this.logger.error(`sendSignedAndConfirm(): Error while awaiting confirmation of ${signature}: `, err);
327
+ abortController.abort(err);
328
+ }));
329
+ continue;
330
+ }
296
331
  }
297
- signatures.push(signature);
332
+ confirmPromise.catch(err => this.logger.error(`sendSignedAndConfirm(): Error while awaiting confirmation of ${signature}: `, err));
298
333
  }
334
+ abortController.signal.throwIfAborted();
299
335
  if (parallel && waitForConfirmation)
300
336
  await Promise.all(promises);
337
+ abortController.signal.throwIfAborted();
301
338
  this.logger.info("sendSignedAndConfirm(): sent transactions, count: " + signedTxs.length +
302
339
  " waitForConfirmation: " + waitForConfirmation + " parallel: " + parallel);
303
340
  return signatures;
@@ -359,7 +396,7 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
359
396
  maxSupportedTransactionVersion: 0
360
397
  });
361
398
  if (txReceipt == null) {
362
- const isValid = await this.connection.isBlockhashValid(parsedTx.recentBlockhash, { commitment: "processed" });
399
+ const { value: isValid } = await this.connection.isBlockhashValid(parsedTx.recentBlockhash, { commitment: "processed" });
363
400
  if (!isValid)
364
401
  return "not_found";
365
402
  return "pending";
@@ -139,7 +139,7 @@ class SolanaDataAccount extends SolanaSwapModule_1.SolanaSwapModule {
139
139
  continue;
140
140
  try {
141
141
  const fetchedDataAccount = await this.connection.getAccountInfo(accountKey);
142
- if (fetchedDataAccount == null) {
142
+ if (fetchedDataAccount == null || fetchedDataAccount.lamports === 0 || fetchedDataAccount.data.length === 0) {
143
143
  await this.removeDataAccount(accountKey);
144
144
  continue;
145
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-solana",
3
- "version": "13.5.10",
3
+ "version": "13.5.12",
4
4
  "description": "Solana specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ComputeBudgetInstruction,
3
- ComputeBudgetProgram, Finality, Keypair, RpcResponseAndContext,
3
+ ComputeBudgetProgram, Finality, Keypair,
4
4
  SendOptions, SignatureResult, Signer, Transaction,
5
5
  TransactionExpiredBlockheightExceededError
6
6
  } from "@solana/web3.js";
@@ -81,17 +81,23 @@ export class SolanaTransactions extends SolanaModule {
81
81
  );
82
82
  this.logger.debug("txConfirmationAndResendWatchdog(): transaction re-sent: "+result);
83
83
 
84
- const status = await this.getTxIdStatus(signature, finality).catch(
84
+ let status = await this.getTxIdStatus(signature, finality).catch(
85
85
  e => this.logger.error("txConfirmationAndResendWatchdog(): get tx id status error: ", e)
86
86
  );
87
87
  if(status==null || status==="not_found") {
88
- if(await this.connection.isBlockhashValid(tx.recentBlockhash!, {commitment: finality})) return;
88
+ const blockValidity = await this.connection.isBlockhashValid(tx.recentBlockhash!, {commitment: finality}).catch(
89
+ e => this.logger.error("txConfirmationAndResendWatchdog(): blockhash validity check error: ", e)
90
+ );
91
+ if(!blockValidity) return;
92
+ if(blockValidity.value) return;
89
93
  try {
90
- //One list try to get the txId status
94
+ //One last try to get the txId status
91
95
  const statusCheck = await this.getTxIdStatus(signature, finality);
92
- if(statusCheck=="not_found") reject(new Error("Transaction expired before confirmation, please try again!"));
96
+ if(statusCheck === "not_found") reject(new Error("Transaction expired before confirmation, please try again!"));
97
+ status = statusCheck;
93
98
  } catch (e) {
94
- this.logger.error("txConfirmationAndResendWatchdog(): re-check get tx id status error: ", e)
99
+ this.logger.error("txConfirmationAndResendWatchdog(): re-check get tx id status error: ", e);
100
+ return;
95
101
  }
96
102
  }
97
103
  if(status==="success") {
@@ -127,19 +133,24 @@ export class SolanaTransactions extends SolanaModule {
127
133
  if(tx.signature==null) throw new Error("Cannot wait for confirmation for tx without signature!");
128
134
  const signature = bs58.encode(tx.signature);
129
135
 
130
- let result: RpcResponseAndContext<SignatureResult>;
136
+ let result: SignatureResult;
131
137
  try {
132
- result = await this.connection.confirmTransaction(
133
- tx.lastValidBlockHeight==null
134
- ? signature
135
- : {
136
- signature: signature,
137
- blockhash: tx.recentBlockhash,
138
- lastValidBlockHeight: tx.lastValidBlockHeight,
139
- abortSignal
140
- },
141
- finality
142
- );
138
+ result = await new Promise<SignatureResult>((resolve, reject) => {
139
+ let subscriptionId: number | undefined;
140
+
141
+ if(abortSignal!=null) abortSignal.addEventListener("abort", () => {
142
+ if(subscriptionId!=null) this.connection.removeSignatureListener(subscriptionId).catch(e => {
143
+ this.logger.debug("txConfirmFromWebsocket(): remove WS signature confirm listener error: ", e);
144
+ });
145
+ subscriptionId = undefined;
146
+ reject(abortSignal.reason);
147
+ });
148
+
149
+ subscriptionId = this.connection.onSignature(signature, (data) => {
150
+ resolve(data);
151
+ subscriptionId = undefined;
152
+ }, finality);
153
+ });
143
154
  this.logger.info("txConfirmFromWebsocket(): transaction confirmed from WS, signature: "+signature);
144
155
  } catch (err: any) {
145
156
  if(abortSignal!=null && abortSignal.aborted) throw err;
@@ -156,7 +167,7 @@ export class SolanaTransactions extends SolanaModule {
156
167
  throw err;
157
168
  }
158
169
  }
159
- if(result.value.err!=null) throw new TransactionRevertedError("Transaction reverted!");
170
+ if(result.err!=null) throw new TransactionRevertedError("Transaction reverted!");
160
171
  return signature;
161
172
  }
162
173
 
@@ -171,9 +182,12 @@ export class SolanaTransactions extends SolanaModule {
171
182
  */
172
183
  private async confirmTransaction(tx: Transaction, abortSignal?: AbortSignal, finality?: Finality) {
173
184
  const abortController = new AbortController();
174
- if(abortSignal!=null) abortSignal.addEventListener("abort", () => {
175
- abortController.abort();
176
- });
185
+ if(abortSignal!=null) {
186
+ abortSignal.throwIfAborted();
187
+ abortSignal.addEventListener("abort", () => {
188
+ abortController.abort(abortSignal.reason);
189
+ });
190
+ }
177
191
 
178
192
  let txSignature: string;
179
193
  try {
@@ -294,7 +308,11 @@ export class SolanaTransactions extends SolanaModule {
294
308
  const signature = await this.sendSignedTransaction(solTx.tx, options, onBeforePublish);
295
309
  const confirmPromise = this.confirmTransaction(solTx.tx, abortSignal, "confirmed");
296
310
  //Don't await the last promise when !waitForConfirmation
297
- if(i<txs.length-1 || e+50<_txs.length || waitForConfirmation) await confirmPromise;
311
+ if(i<txs.length-1 || e+50<_txs.length || waitForConfirmation) {
312
+ await confirmPromise;
313
+ } else {
314
+ confirmPromise.catch(err => this.logger.error(`sendAndConfirm(): Error while awaiting confirmation of ${signature}: `, err));
315
+ }
298
316
  signatures.push(signature);
299
317
  }
300
318
  }
@@ -329,23 +347,42 @@ export class SolanaTransactions extends SolanaModule {
329
347
  this.logger.debug("sendSignedAndConfirm(): sending transactions, count: "+signedTxs.length+
330
348
  " waitForConfirmation: "+waitForConfirmation+" parallel: "+parallel);
331
349
 
350
+ const abortController = new AbortController();
351
+ if(abortSignal!=null) {
352
+ abortSignal.throwIfAborted();
353
+ abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
354
+ }
355
+
332
356
  const signatures: string[] = [];
333
357
  const promises: Promise<void>[] = [];
334
358
  for(let i=0; i<signedTxs.length; i++) {
335
359
  const signedTx = signedTxs[i];
336
360
  this.logger.debug("sendSignedAndConfirm(): sending transaction "+i+", total count: "+signedTxs.length);
337
361
  const signature = await this.sendSignedTransaction(signedTx, options, onBeforePublish);
338
- const confirmPromise = this.confirmTransaction(signedTx, abortSignal, "confirmed");
362
+ if(abortSignal!=null) abortSignal.throwIfAborted();
363
+ const confirmPromise = this.confirmTransaction(signedTx, abortController.signal, "confirmed");
364
+ signatures.push(signature);
339
365
  if(!parallel) {
340
366
  //Don't await the last one when not wait for confirmations
341
- if(i<signedTxs.length-1 || waitForConfirmation) await confirmPromise;
367
+ if(i<signedTxs.length-1 || waitForConfirmation) {
368
+ await confirmPromise;
369
+ continue;
370
+ }
342
371
  } else {
343
- promises.push(confirmPromise);
372
+ if(waitForConfirmation) {
373
+ promises.push(confirmPromise.catch(err => {
374
+ this.logger.error(`sendSignedAndConfirm(): Error while awaiting confirmation of ${signature}: `, err);
375
+ abortController.abort(err);
376
+ }));
377
+ continue;
378
+ }
344
379
  }
345
- signatures.push(signature);
380
+ confirmPromise.catch(err => this.logger.error(`sendSignedAndConfirm(): Error while awaiting confirmation of ${signature}: `, err));
346
381
  }
347
382
 
383
+ abortController.signal.throwIfAborted();
348
384
  if(parallel && waitForConfirmation) await Promise.all(promises);
385
+ abortController.signal.throwIfAborted();
349
386
 
350
387
  this.logger.info("sendSignedAndConfirm(): sent transactions, count: "+signedTxs.length+
351
388
  " waitForConfirmation: "+waitForConfirmation+" parallel: "+parallel);
@@ -420,7 +457,7 @@ export class SolanaTransactions extends SolanaModule {
420
457
  maxSupportedTransactionVersion: 0
421
458
  });
422
459
  if(txReceipt==null) {
423
- const isValid = await this.connection.isBlockhashValid(parsedTx.recentBlockhash!, {commitment: "processed"});
460
+ const {value: isValid} = await this.connection.isBlockhashValid(parsedTx.recentBlockhash!, {commitment: "processed"});
424
461
  if(!isValid) return "not_found";
425
462
  return "pending";
426
463
  }
@@ -191,7 +191,7 @@ export class SolanaDataAccount extends SolanaSwapModule {
191
191
 
192
192
  try {
193
193
  const fetchedDataAccount = await this.connection.getAccountInfo(accountKey);
194
- if(fetchedDataAccount==null) {
194
+ if(fetchedDataAccount==null || fetchedDataAccount.lamports===0 || fetchedDataAccount.data.length===0) {
195
195
  await this.removeDataAccount(accountKey);
196
196
  continue;
197
197
  }