@atomiqlabs/chain-starknet 4.0.0-dev.29 → 4.0.0-dev.30

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.
@@ -54,7 +54,8 @@ class StarknetTransactions extends StarknetModule_1.StarknetModule {
54
54
  return BigInt(await this.provider.getNonceForAddress(address, blockTag));
55
55
  }
56
56
  catch (e) {
57
- if (e.message != null && e.message.includes("20: Contract not found")) {
57
+ if (e.baseError?.code === 20 ||
58
+ (e.message != null && e.message.includes("20: Contract not found"))) {
58
59
  return BigInt(0);
59
60
  }
60
61
  throw e;
@@ -78,26 +79,46 @@ class StarknetTransactions extends StarknetModule_1.StarknetModule {
78
79
  this.onBeforeTxReplace(txReplaceListener);
79
80
  let state = "pending";
80
81
  let confirmedTxId = null;
81
- while (state === "pending" || state === "not_found") {
82
+ while (state === "pending") {
82
83
  await (0, Utils_1.timeoutPromise)(3000, abortSignal);
83
- for (let txId of checkTxns) {
84
- //TODO: Rebroadcast latest tx if possible, we might need to do that in case we use 2 different RPCs on the backend
85
- // as the other one might not have the tx in their mempool
86
- // if(state==="not_found" && tx.signed!=null) await this.sendSignedTransaction(tx).catch(e => {
87
- // if(e.baseError?.code === 59) return; //Transaction already in the mempool
88
- // this.logger.error("confirmTransaction(): Error on transaction re-send: ", e);
89
- // });
90
- state = await this._getTxIdStatus(txId);
91
- if (state === "rejected" || state === "reverted" || state === "success") {
84
+ const latestConfirmedNonce = this.latestConfirmedNonces[(0, Utils_1.toHex)(tx.details.walletAddress)];
85
+ const snapshot = [...checkTxns]; //Iterate over a snapshot
86
+ const totalTxnCount = snapshot.length;
87
+ let rejectedTxns = 0;
88
+ let notFoundTxns = 0;
89
+ for (let txId of snapshot) {
90
+ let _state = await this._getTxIdStatus(txId);
91
+ if (_state === "not_found")
92
+ notFoundTxns++;
93
+ if (_state === "rejected")
94
+ rejectedTxns++;
95
+ if (_state === "reverted" || _state === "success") {
92
96
  confirmedTxId = txId;
97
+ state = _state;
93
98
  break;
94
99
  }
95
100
  }
101
+ if (rejectedTxns === totalTxnCount) { //All rejected
102
+ state = "rejected";
103
+ break;
104
+ }
105
+ if (notFoundTxns === totalTxnCount) { //All not found, check the latest account nonce
106
+ if (latestConfirmedNonce != null && latestConfirmedNonce > BigInt(tx.details.nonce)) {
107
+ //Confirmed nonce is already higher than the TX nonce, meaning the TX got replaced
108
+ throw new Error("Transaction failed - replaced!");
109
+ }
110
+ this.logger.warn("confirmTransaction(): All transactions not found, fetching the latest account nonce...");
111
+ const _latestConfirmedNonce = this.latestConfirmedNonces[(0, Utils_1.toHex)(tx.details.walletAddress)];
112
+ const currentLatestNonce = await this.getNonce(tx.details.walletAddress, starknet_1.BlockTag.LATEST);
113
+ if (_latestConfirmedNonce == null || _latestConfirmedNonce < currentLatestNonce) {
114
+ this.latestConfirmedNonces[(0, Utils_1.toHex)(tx.details.walletAddress)] = currentLatestNonce;
115
+ }
116
+ }
96
117
  }
97
118
  this.offBeforeTxReplace(txReplaceListener);
98
119
  if (state === "rejected")
99
120
  throw new Error("Transaction rejected!");
100
- const nextAccountNonce = (0, Utils_1.toBigInt)(tx.details.nonce) + 1n;
121
+ const nextAccountNonce = BigInt(tx.details.nonce) + 1n;
101
122
  const currentConfirmedNonce = this.latestConfirmedNonces[(0, Utils_1.toHex)(tx.details.walletAddress)];
102
123
  if (currentConfirmedNonce == null || nextAccountNonce > currentConfirmedNonce) {
103
124
  this.latestConfirmedNonces[(0, Utils_1.toHex)(tx.details.walletAddress)] = nextAccountNonce;
@@ -309,7 +330,8 @@ class StarknetTransactions extends StarknetModule_1.StarknetModule {
309
330
  */
310
331
  async _getTxIdStatus(txId) {
311
332
  const status = await this.provider.getTransactionStatus(txId).catch(e => {
312
- if (e.message != null && e.message.includes("29: Transaction hash not found"))
333
+ if (e.baseError?.code === 29 ||
334
+ (e.message != null && e.message.includes("29: Transaction hash not found")))
313
335
  return null;
314
336
  throw e;
315
337
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-starknet",
3
- "version": "4.0.0-dev.29",
3
+ "version": "4.0.0-dev.30",
4
4
  "description": "Starknet specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -8,7 +8,7 @@ import {
8
8
  ETransactionExecutionStatus, BlockTag
9
9
  } from "starknet";
10
10
  import {StarknetSigner} from "../../wallet/StarknetSigner";
11
- import {calculateHash, timeoutPromise, toBigInt, toHex, tryWithRetries} from "../../../utils/Utils";
11
+ import {timeoutPromise, toHex} from "../../../utils/Utils";
12
12
 
13
13
  export type StarknetTxBase = {
14
14
  details: InvocationsSignerDetails & {maxFee?: BigNumberish},
@@ -81,7 +81,10 @@ export class StarknetTransactions extends StarknetModule {
81
81
  try {
82
82
  return BigInt(await this.provider.getNonceForAddress(address, blockTag));
83
83
  } catch (e) {
84
- if(e.message!=null && e.message.includes("20: Contract not found")) {
84
+ if(
85
+ e.baseError?.code === 20 ||
86
+ (e.message!=null && e.message.includes("20: Contract not found"))
87
+ ) {
85
88
  return BigInt(0);
86
89
  }
87
90
  throw e;
@@ -107,28 +110,47 @@ export class StarknetTransactions extends StarknetModule {
107
110
 
108
111
  let state = "pending";
109
112
  let confirmedTxId: string = null;
110
- while(state==="pending" || state==="not_found") {
113
+ while(state==="pending") {
111
114
  await timeoutPromise(3000, abortSignal);
112
- for(let txId of checkTxns) {
113
- //TODO: Rebroadcast latest tx if possible, we might need to do that in case we use 2 different RPCs on the backend
114
- // as the other one might not have the tx in their mempool
115
- // if(state==="not_found" && tx.signed!=null) await this.sendSignedTransaction(tx).catch(e => {
116
- // if(e.baseError?.code === 59) return; //Transaction already in the mempool
117
- // this.logger.error("confirmTransaction(): Error on transaction re-send: ", e);
118
- // });
119
- state = await this._getTxIdStatus(txId);
120
- if(state==="rejected" || state==="reverted" || state==="success") {
115
+ const latestConfirmedNonce = this.latestConfirmedNonces[toHex(tx.details.walletAddress)];
116
+
117
+ const snapshot = [...checkTxns]; //Iterate over a snapshot
118
+ const totalTxnCount = snapshot.length;
119
+ let rejectedTxns = 0;
120
+ let notFoundTxns = 0;
121
+ for(let txId of snapshot) {
122
+ let _state = await this._getTxIdStatus(txId);
123
+ if(_state==="not_found") notFoundTxns++;
124
+ if(_state==="rejected") rejectedTxns++;
125
+ if(_state==="reverted" || _state==="success") {
121
126
  confirmedTxId = txId;
127
+ state = _state;
122
128
  break;
123
129
  }
124
130
  }
131
+ if(rejectedTxns===totalTxnCount) { //All rejected
132
+ state = "rejected";
133
+ break;
134
+ }
135
+ if(notFoundTxns===totalTxnCount) { //All not found, check the latest account nonce
136
+ if(latestConfirmedNonce!=null && latestConfirmedNonce>BigInt(tx.details.nonce)) {
137
+ //Confirmed nonce is already higher than the TX nonce, meaning the TX got replaced
138
+ throw new Error("Transaction failed - replaced!");
139
+ }
140
+ this.logger.warn("confirmTransaction(): All transactions not found, fetching the latest account nonce...");
141
+ const _latestConfirmedNonce = this.latestConfirmedNonces[toHex(tx.details.walletAddress)];
142
+ const currentLatestNonce = await this.getNonce(tx.details.walletAddress, BlockTag.LATEST);
143
+ if(_latestConfirmedNonce==null || _latestConfirmedNonce < currentLatestNonce) {
144
+ this.latestConfirmedNonces[toHex(tx.details.walletAddress)] = currentLatestNonce;
145
+ }
146
+ }
125
147
  }
126
148
 
127
149
  this.offBeforeTxReplace(txReplaceListener);
128
150
 
129
151
  if(state==="rejected") throw new Error("Transaction rejected!");
130
152
 
131
- const nextAccountNonce = toBigInt(tx.details.nonce) + 1n;
153
+ const nextAccountNonce = BigInt(tx.details.nonce) + 1n;
132
154
  const currentConfirmedNonce = this.latestConfirmedNonces[toHex(tx.details.walletAddress)];
133
155
  if(currentConfirmedNonce==null || nextAccountNonce > currentConfirmedNonce) {
134
156
  this.latestConfirmedNonces[toHex(tx.details.walletAddress)] = nextAccountNonce;
@@ -352,7 +374,10 @@ export class StarknetTransactions extends StarknetModule {
352
374
  */
353
375
  public async _getTxIdStatus(txId: string): Promise<"pending" | "success" | "not_found" | "reverted" | "rejected"> {
354
376
  const status = await this.provider.getTransactionStatus(txId).catch(e => {
355
- if(e.message!=null && e.message.includes("29: Transaction hash not found")) return null;
377
+ if(
378
+ e.baseError?.code===29 ||
379
+ (e.message!=null && e.message.includes("29: Transaction hash not found"))
380
+ ) return null;
356
381
  throw e;
357
382
  });
358
383
  if(status==null) return this._knownTxSet.has(txId) ? "pending" : "not_found";
@@ -1,8 +1,8 @@
1
1
  import {StarknetSigner} from "./StarknetSigner";
2
2
  import {StarknetTransactions, StarknetTx} from "../chain/modules/StarknetTransactions";
3
3
  import {StarknetChainInterface} from "../chain/StarknetChainInterface";
4
- import {bigIntMax, getLogger, LoggerType, toBigInt} from "../../utils/Utils";
5
- import {Account, Block, BlockTag} from "starknet";
4
+ import {bigIntMax, getLogger, LoggerType} from "../../utils/Utils";
5
+ import {Account, BlockTag} from "starknet";
6
6
  import {access, readFile, writeFile, mkdir, constants} from "fs/promises";
7
7
  import {StarknetFees} from "../chain/modules/StarknetFees";
8
8
  import {cloneDeep} from "@scure/btc-signer/transaction";