@atomiqlabs/chain-starknet 1.0.9 → 2.0.0-beta.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.
Files changed (121) hide show
  1. package/dist/index.d.ts +13 -9
  2. package/dist/index.js +13 -9
  3. package/dist/starknet/StarknetChainType.d.ts +6 -2
  4. package/dist/starknet/StarknetInitializer.d.ts +3 -2
  5. package/dist/starknet/StarknetInitializer.js +17 -6
  6. package/dist/starknet/btcrelay/StarknetBtcRelay.d.ts +28 -7
  7. package/dist/starknet/btcrelay/StarknetBtcRelay.js +75 -20
  8. package/dist/starknet/{base → chain}/StarknetAction.d.ts +2 -2
  9. package/dist/starknet/chain/StarknetChainInterface.d.ts +52 -0
  10. package/dist/starknet/chain/StarknetChainInterface.js +91 -0
  11. package/dist/starknet/{base → chain}/StarknetModule.d.ts +3 -3
  12. package/dist/starknet/{base → chain}/modules/StarknetAddresses.d.ts +1 -1
  13. package/dist/starknet/{base → chain}/modules/StarknetAddresses.js +1 -1
  14. package/dist/starknet/{base → chain}/modules/StarknetSignatures.d.ts +2 -2
  15. package/dist/starknet/{base → chain}/modules/StarknetTokens.js +2 -1
  16. package/dist/starknet/{base → chain}/modules/StarknetTransactions.d.ts +7 -1
  17. package/dist/starknet/{base → chain}/modules/StarknetTransactions.js +45 -16
  18. package/dist/starknet/contract/StarknetContractBase.d.ts +5 -5
  19. package/dist/starknet/contract/StarknetContractBase.js +5 -7
  20. package/dist/starknet/contract/StarknetContractModule.d.ts +8 -0
  21. package/dist/starknet/contract/StarknetContractModule.js +11 -0
  22. package/dist/starknet/contract/modules/StarknetContractEvents.d.ts +15 -4
  23. package/dist/starknet/contract/modules/StarknetContractEvents.js +26 -6
  24. package/dist/starknet/events/StarknetChainEvents.d.ts +3 -1
  25. package/dist/starknet/events/StarknetChainEvents.js +9 -9
  26. package/dist/starknet/events/StarknetChainEventsBrowser.d.ts +23 -6
  27. package/dist/starknet/events/StarknetChainEventsBrowser.js +106 -13
  28. package/dist/starknet/provider/RpcProviderWithRetries.d.ts +21 -0
  29. package/dist/starknet/provider/RpcProviderWithRetries.js +32 -0
  30. package/dist/starknet/spv_swap/SpvVaultContractAbi.d.ts +488 -0
  31. package/dist/starknet/spv_swap/SpvVaultContractAbi.js +656 -0
  32. package/dist/starknet/spv_swap/StarknetSpvVaultContract.d.ts +65 -0
  33. package/dist/starknet/spv_swap/StarknetSpvVaultContract.js +372 -0
  34. package/dist/starknet/spv_swap/StarknetSpvVaultData.d.ts +49 -0
  35. package/dist/starknet/spv_swap/StarknetSpvVaultData.js +144 -0
  36. package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.d.ts +24 -0
  37. package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.js +61 -0
  38. package/dist/starknet/swaps/StarknetSwapContract.d.ts +4 -22
  39. package/dist/starknet/swaps/StarknetSwapContract.js +23 -80
  40. package/dist/starknet/swaps/StarknetSwapModule.d.ts +6 -5
  41. package/dist/starknet/swaps/StarknetSwapModule.js +5 -6
  42. package/dist/starknet/swaps/handlers/IHandler.d.ts +2 -2
  43. package/dist/starknet/swaps/handlers/claim/ClaimHandlers.d.ts +1 -1
  44. package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.d.ts +2 -2
  45. package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +2 -2
  46. package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +2 -2
  47. package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +2 -2
  48. package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +2 -21
  49. package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +7 -41
  50. package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.d.ts +2 -2
  51. package/dist/starknet/swaps/modules/StarknetLpVault.d.ts +1 -1
  52. package/dist/starknet/swaps/modules/StarknetLpVault.js +9 -9
  53. package/dist/starknet/swaps/modules/StarknetSwapClaim.d.ts +1 -1
  54. package/dist/starknet/swaps/modules/StarknetSwapClaim.js +8 -8
  55. package/dist/starknet/swaps/modules/StarknetSwapInit.d.ts +1 -1
  56. package/dist/starknet/swaps/modules/StarknetSwapInit.js +9 -9
  57. package/dist/starknet/swaps/modules/StarknetSwapRefund.d.ts +1 -3
  58. package/dist/starknet/swaps/modules/StarknetSwapRefund.js +8 -11
  59. package/dist/starknet/wallet/StarknetSigner.js +1 -1
  60. package/dist/utils/Utils.d.ts +2 -2
  61. package/dist/utils/Utils.js +3 -1
  62. package/package.json +2 -2
  63. package/src/index.ts +15 -9
  64. package/src/starknet/StarknetChainType.ts +10 -2
  65. package/src/starknet/StarknetInitializer.ts +23 -7
  66. package/src/starknet/btcrelay/StarknetBtcRelay.ts +104 -30
  67. package/src/starknet/{base → chain}/StarknetAction.ts +3 -3
  68. package/src/starknet/chain/StarknetChainInterface.ts +149 -0
  69. package/src/starknet/{base → chain}/StarknetModule.ts +3 -3
  70. package/src/starknet/{base → chain}/modules/StarknetAddresses.ts +1 -1
  71. package/src/starknet/{base → chain}/modules/StarknetSignatures.ts +2 -2
  72. package/src/starknet/{base → chain}/modules/StarknetTokens.ts +2 -1
  73. package/src/starknet/{base → chain}/modules/StarknetTransactions.ts +43 -18
  74. package/src/starknet/contract/StarknetContractBase.ts +9 -12
  75. package/src/starknet/contract/StarknetContractModule.ts +16 -0
  76. package/src/starknet/contract/modules/StarknetContractEvents.ts +33 -7
  77. package/src/starknet/events/StarknetChainEvents.ts +15 -11
  78. package/src/starknet/events/StarknetChainEventsBrowser.ts +159 -26
  79. package/src/starknet/provider/RpcProviderWithRetries.ts +43 -0
  80. package/src/starknet/spv_swap/SpvVaultContractAbi.ts +656 -0
  81. package/src/starknet/spv_swap/StarknetSpvVaultContract.ts +475 -0
  82. package/src/starknet/spv_swap/StarknetSpvVaultData.ts +194 -0
  83. package/src/starknet/spv_swap/StarknetSpvWithdrawalData.ts +68 -0
  84. package/src/starknet/swaps/StarknetSwapContract.ts +28 -116
  85. package/src/starknet/swaps/StarknetSwapModule.ts +8 -8
  86. package/src/starknet/swaps/handlers/IHandler.ts +2 -2
  87. package/src/starknet/swaps/handlers/claim/ClaimHandlers.ts +1 -1
  88. package/src/starknet/swaps/handlers/claim/HashlockClaimHandler.ts +2 -2
  89. package/src/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +2 -2
  90. package/src/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +2 -2
  91. package/src/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +2 -2
  92. package/src/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +12 -61
  93. package/src/starknet/swaps/handlers/refund/TimelockRefundHandler.ts +2 -2
  94. package/src/starknet/swaps/modules/StarknetLpVault.ts +10 -10
  95. package/src/starknet/swaps/modules/StarknetSwapClaim.ts +9 -9
  96. package/src/starknet/swaps/modules/StarknetSwapInit.ts +10 -10
  97. package/src/starknet/swaps/modules/StarknetSwapRefund.ts +9 -13
  98. package/src/starknet/wallet/StarknetSigner.ts +1 -1
  99. package/src/utils/Utils.ts +4 -3
  100. package/dist/starknet/base/StarknetBase.d.ts +0 -34
  101. package/dist/starknet/base/StarknetBase.js +0 -29
  102. package/src/starknet/base/StarknetBase.ts +0 -56
  103. /package/dist/starknet/{base → chain}/StarknetAction.js +0 -0
  104. /package/dist/starknet/{base → chain}/StarknetModule.js +0 -0
  105. /package/dist/starknet/{base → chain}/modules/ERC20Abi.d.ts +0 -0
  106. /package/dist/starknet/{base → chain}/modules/ERC20Abi.js +0 -0
  107. /package/dist/starknet/{base → chain}/modules/StarknetAccounts.d.ts +0 -0
  108. /package/dist/starknet/{base → chain}/modules/StarknetAccounts.js +0 -0
  109. /package/dist/starknet/{base → chain}/modules/StarknetBlocks.d.ts +0 -0
  110. /package/dist/starknet/{base → chain}/modules/StarknetBlocks.js +0 -0
  111. /package/dist/starknet/{base → chain}/modules/StarknetEvents.d.ts +0 -0
  112. /package/dist/starknet/{base → chain}/modules/StarknetEvents.js +0 -0
  113. /package/dist/starknet/{base → chain}/modules/StarknetFees.d.ts +0 -0
  114. /package/dist/starknet/{base → chain}/modules/StarknetFees.js +0 -0
  115. /package/dist/starknet/{base → chain}/modules/StarknetSignatures.js +0 -0
  116. /package/dist/starknet/{base → chain}/modules/StarknetTokens.d.ts +0 -0
  117. /package/src/starknet/{base → chain}/modules/ERC20Abi.ts +0 -0
  118. /package/src/starknet/{base → chain}/modules/StarknetAccounts.ts +0 -0
  119. /package/src/starknet/{base → chain}/modules/StarknetBlocks.ts +0 -0
  120. /package/src/starknet/{base → chain}/modules/StarknetEvents.ts +0 -0
  121. /package/src/starknet/{base → chain}/modules/StarknetFees.ts +0 -0
@@ -1,21 +1,21 @@
1
1
  import {Buffer} from "buffer";
2
2
  import {StarknetBtcHeader} from "./headers/StarknetBtcHeader";
3
- import {BigIntBufferUtils, BitcoinRpc, BtcBlock, BtcRelay, StatePredictorUtils} from "@atomiqlabs/base";
3
+ import {BitcoinRpc, BtcBlock, BtcRelay, RelaySynchronizer, StatePredictorUtils} from "@atomiqlabs/base";
4
4
  import {
5
5
  bigNumberishToBuffer,
6
- bufferToU32Array,
7
- toHex,
6
+ bufferToU32Array, getLogger,
7
+ toHex, tryWithRetries,
8
8
  u32ReverseEndianness
9
9
  } from "../../utils/Utils";
10
10
  import {StarknetContractBase} from "../contract/StarknetContractBase";
11
11
  import {StarknetBtcStoredHeader} from "./headers/StarknetBtcStoredHeader";
12
- import {StarknetTx} from "../base/modules/StarknetTransactions";
12
+ import {StarknetTx} from "../chain/modules/StarknetTransactions";
13
13
  import {StarknetSigner} from "../wallet/StarknetSigner";
14
14
  import {BtcRelayAbi} from "./BtcRelayAbi";
15
- import {BigNumberish, constants, hash, Provider} from "starknet";
16
- import {StarknetFees} from "../base/modules/StarknetFees";
17
- import {StarknetRetryPolicy} from "../base/StarknetBase";
18
- import {StarknetAction} from "../base/StarknetAction";
15
+ import {BigNumberish, constants, hash} from "starknet";
16
+ import {StarknetFees} from "../chain/modules/StarknetFees";
17
+ import {StarknetChainInterface} from "../chain/StarknetChainInterface";
18
+ import {StarknetAction} from "../chain/StarknetAction";
19
19
 
20
20
  function serializeBlockHeader(e: BtcBlock): StarknetBtcHeader {
21
21
  return new StarknetBtcHeader({
@@ -29,8 +29,8 @@ function serializeBlockHeader(e: BtcBlock): StarknetBtcHeader {
29
29
  });
30
30
  }
31
31
 
32
- const GAS_PER_BLOCKHEADER = 750;
33
- const GAS_PER_BLOCKHEADER_FORK = 750;
32
+ const GAS_PER_BLOCKHEADER = 850;
33
+ const GAS_PER_BLOCKHEADER_FORK = 1000;
34
34
 
35
35
  const btcRelayAddreses = {
36
36
  [constants.StarknetChainId.SN_SEPOLIA]: "0x068601c79da2231d21e015ccfd59c243861156fa523a12c9f987ec28eb8dbc8c",
@@ -46,13 +46,16 @@ function serializeCalldata(headers: StarknetBtcHeader[], storedHeader: StarknetB
46
46
  return span;
47
47
  }
48
48
 
49
+ const logger = getLogger("StarknetBtcRelay: ");
50
+
49
51
  export class StarknetBtcRelay<B extends BtcBlock>
50
52
  extends StarknetContractBase<typeof BtcRelayAbi>
51
53
  implements BtcRelay<StarknetBtcStoredHeader, StarknetTx, B, StarknetSigner> {
52
54
 
55
+
53
56
  public SaveMainHeaders(signer: string, mainHeaders: StarknetBtcHeader[], storedHeader: StarknetBtcStoredHeader): StarknetAction {
54
57
 
55
- return new StarknetAction(signer, this,
58
+ return new StarknetAction(signer, this.Chain,
56
59
  {
57
60
  contractAddress: this.contract.address,
58
61
  entrypoint: "submit_main_blockheaders",
@@ -63,7 +66,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
63
66
  }
64
67
 
65
68
  public SaveShortForkHeaders(signer: string, forkHeaders: StarknetBtcHeader[], storedHeader: StarknetBtcStoredHeader): StarknetAction {
66
- return new StarknetAction(signer, this,
69
+ return new StarknetAction(signer, this.Chain,
67
70
  {
68
71
  contractAddress: this.contract.address,
69
72
  entrypoint: "submit_short_fork_blockheaders",
@@ -74,7 +77,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
74
77
  }
75
78
 
76
79
  public SaveLongForkHeaders(signer: string, forkId: number, forkHeaders: StarknetBtcHeader[], storedHeader: StarknetBtcStoredHeader, totalForkHeaders: number = 100): StarknetAction {
77
- return new StarknetAction(signer, this,
80
+ return new StarknetAction(signer, this.Chain,
78
81
  {
79
82
  contractAddress: this.contract.address,
80
83
  entrypoint: "submit_fork_blockheaders",
@@ -91,14 +94,11 @@ export class StarknetBtcRelay<B extends BtcBlock>
91
94
  readonly maxShortForkHeadersPerTx: number = 100;
92
95
 
93
96
  constructor(
94
- chainId: constants.StarknetChainId,
95
- provider: Provider,
97
+ chainInterface: StarknetChainInterface,
96
98
  bitcoinRpc: BitcoinRpc<B>,
97
- contractAddress: string = btcRelayAddreses[chainId],
98
- retryPolicy?: StarknetRetryPolicy,
99
- solanaFeeEstimator: StarknetFees = new StarknetFees(provider)
99
+ contractAddress: string = btcRelayAddreses[chainInterface.starknetChainId],
100
100
  ) {
101
- super(chainId, provider, contractAddress, BtcRelayAbi, retryPolicy, solanaFeeEstimator);
101
+ super(chainInterface, contractAddress, BtcRelayAbi);
102
102
  this.bitcoinRpc = bitcoinRpc;
103
103
  }
104
104
 
@@ -232,7 +232,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
232
232
  const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
233
233
  if(BigInt(chainCommitment)!==BigInt(commitHash)) return null;
234
234
 
235
- this.logger.debug("retrieveLogAndBlockheight(): block found," +
235
+ logger.debug("retrieveLogAndBlockheight(): block found," +
236
236
  " commit hash: "+toHex(commitHash)+" blockhash: "+blockData.blockhash+" current btc relay height: "+blockHeight);
237
237
 
238
238
  return {header: storedBlockHeader, height: blockHeight};
@@ -254,7 +254,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
254
254
  const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
255
255
  if(BigInt(chainCommitment)!==BigInt(commitHash)) return null;
256
256
 
257
- this.logger.debug("retrieveLogByCommitHash(): block found," +
257
+ logger.debug("retrieveLogByCommitHash(): block found," +
258
258
  " commit hash: "+commitmentHashStr+" blockhash: "+blockData.blockhash+" height: "+storedBlockHeader.block_height);
259
259
 
260
260
  return storedBlockHeader;
@@ -292,7 +292,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
292
292
  }
293
293
  )
294
294
 
295
- if(data!=null) this.logger.debug("retrieveLatestKnownBlockLog(): block found," +
295
+ if(data!=null) logger.debug("retrieveLatestKnownBlockLog(): block found," +
296
296
  " commit hash: "+toHex(data.commitHash)+" blockhash: "+data.resultBitcoinHeader.getHash()+
297
297
  " height: "+data.resultStoredHeader.getBlockheight());
298
298
 
@@ -308,7 +308,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
308
308
  * @param feeRate
309
309
  */
310
310
  public saveMainHeaders(signer: string, mainHeaders: BtcBlock[], storedHeader: StarknetBtcStoredHeader, feeRate?: string) {
311
- this.logger.debug("saveMainHeaders(): submitting main blockheaders, count: "+mainHeaders.length);
311
+ logger.debug("saveMainHeaders(): submitting main blockheaders, count: "+mainHeaders.length);
312
312
  return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate);
313
313
  }
314
314
 
@@ -324,7 +324,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
324
324
  public async saveNewForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: StarknetBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
325
325
  let forkId: number = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
326
326
 
327
- this.logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
327
+ logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
328
328
  " count: "+forkHeaders.length+" forkId: 0x"+forkId.toString(16));
329
329
 
330
330
  return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
@@ -341,7 +341,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
341
341
  * @param feeRate
342
342
  */
343
343
  public saveForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: StarknetBtcStoredHeader, forkId: number, tipWork: Buffer, feeRate?: string) {
344
- this.logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
344
+ logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
345
345
  " count: "+forkHeaders.length+" forkId: 0x"+forkId.toString(16));
346
346
 
347
347
  return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
@@ -357,7 +357,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
357
357
  * @param feeRate
358
358
  */
359
359
  public saveShortForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: StarknetBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
360
- this.logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
360
+ logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
361
361
  " count: "+forkHeaders.length);
362
362
 
363
363
  return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate);
@@ -378,7 +378,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
378
378
  if(blockheightDelta<=0) return 0n;
379
379
 
380
380
  const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
381
- this.logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
381
+ logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
382
382
  " blockheight delta: "+blockheightDelta+" fee: "+synchronizationFee.toString(10));
383
383
 
384
384
  return synchronizationFee;
@@ -390,7 +390,7 @@ export class StarknetBtcRelay<B extends BtcBlock>
390
390
  * @param feeRate
391
391
  */
392
392
  public async getFeePerBlock(feeRate?: string): Promise<bigint> {
393
- feeRate ??= await this.Fees.getFeeRate();
393
+ feeRate ??= await this.Chain.Fees.getFeeRate();
394
394
  return StarknetFees.getGasFee(GAS_PER_BLOCKHEADER, feeRate);
395
395
  }
396
396
 
@@ -398,18 +398,92 @@ export class StarknetBtcRelay<B extends BtcBlock>
398
398
  * Gets fee rate required for submitting blockheaders to the main chain
399
399
  */
400
400
  public getMainFeeRate(signer: string | null): Promise<string> {
401
- return this.Fees.getFeeRate();
401
+ return this.Chain.Fees.getFeeRate();
402
402
  }
403
403
 
404
404
  /**
405
405
  * Gets fee rate required for submitting blockheaders to the specific fork
406
406
  */
407
407
  public getForkFeeRate(signer: string, forkId: number): Promise<string> {
408
- return this.Fees.getFeeRate();
408
+ return this.Chain.Fees.getFeeRate();
409
409
  }
410
410
 
411
411
  saveInitialHeader(signer: string, header: B, epochStart: number, pastBlocksTimestamps: number[], feeRate?: string): Promise<StarknetTx> {
412
412
  throw new Error("Not supported, starknet contract is initialized with constructor!");
413
413
  }
414
414
 
415
+ /**
416
+ * Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
417
+ * requiredConfirmations
418
+ * If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
419
+ * current chain tip & adds them to the txs array
420
+ *
421
+ * @param signer
422
+ * @param btcRelay
423
+ * @param btcTxs
424
+ * @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
425
+ * txns are added here
426
+ * @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
427
+ * to the required blockheight
428
+ * @param feeRate Fee rate to use for synchronization transactions
429
+ * @private
430
+ */
431
+ static async getCommitedHeadersAndSynchronize(
432
+ signer: string,
433
+ btcRelay: StarknetBtcRelay<any>,
434
+ btcTxs: {blockheight: number, requiredConfirmations: number, blockhash: string}[],
435
+ txs: StarknetTx[],
436
+ synchronizer?: RelaySynchronizer<StarknetBtcStoredHeader, StarknetTx, any>,
437
+ feeRate?: string
438
+ ): Promise<{
439
+ [blockhash: string]: StarknetBtcStoredHeader
440
+ }> {
441
+ const leavesTxs: {blockheight: number, requiredConfirmations: number, blockhash: string}[] = [];
442
+
443
+ const blockheaders: {
444
+ [blockhash: string]: StarknetBtcStoredHeader
445
+ } = {};
446
+
447
+ for(let btcTx of btcTxs) {
448
+ const requiredBlockheight = btcTx.blockheight+btcTx.requiredConfirmations-1;
449
+
450
+ const result = await tryWithRetries(
451
+ () => btcRelay.retrieveLogAndBlockheight({
452
+ blockhash: btcTx.blockhash
453
+ }, requiredBlockheight)
454
+ );
455
+
456
+ if(result!=null) {
457
+ blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
458
+ } else {
459
+ leavesTxs.push(btcTx);
460
+ }
461
+ }
462
+
463
+ if(leavesTxs.length===0) return blockheaders;
464
+
465
+ //Need to synchronize
466
+ if(synchronizer==null) return null;
467
+
468
+ //TODO: We don't have to synchronize to tip, only to our required blockheight
469
+ const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
470
+ logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, "+
471
+ "synchronizing ourselves in "+resp.txs.length+" txs");
472
+ logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ",resp.computedHeaderMap);
473
+ txs.push(...resp.txs);
474
+
475
+ for(let key in resp.computedHeaderMap) {
476
+ const header = resp.computedHeaderMap[key];
477
+ blockheaders[header.getBlockHash().toString("hex")] = header;
478
+ }
479
+
480
+ //Check that blockhashes of all the rest txs are included
481
+ for(let btcTx of leavesTxs) {
482
+ if(blockheaders[btcTx.blockhash]==null) return null;
483
+ }
484
+
485
+ //Retrieve computed headers
486
+ return blockheaders;
487
+ }
488
+
415
489
  }
@@ -1,5 +1,5 @@
1
1
  import {Call} from "starknet";
2
- import {StarknetBase} from "./StarknetBase";
2
+ import {StarknetChainInterface} from "./StarknetChainInterface";
3
3
  import {StarknetTx} from "./modules/StarknetTransactions";
4
4
 
5
5
  export type StarknetGas = {l1?: number, l2?: number};
@@ -16,13 +16,13 @@ export class StarknetAction {
16
16
  L1GasLimit: number;
17
17
  L2GasLimit: number;
18
18
  readonly mainSigner: string;
19
- private readonly root: StarknetBase;
19
+ private readonly root: StarknetChainInterface;
20
20
  private readonly instructions: Call[];
21
21
  private feeRate: string;
22
22
 
23
23
  constructor(
24
24
  mainSigner: string,
25
- root: StarknetBase,
25
+ root: StarknetChainInterface,
26
26
  instructions: Call[] | Call = [],
27
27
  gasLimit?: StarknetGas,
28
28
  feeRate?: string
@@ -0,0 +1,149 @@
1
+ import {Provider, constants, stark, ec} from "starknet";
2
+ import {getLogger, toHex} from "../../utils/Utils";
3
+ import {StarknetTransactions, StarknetTx} from "./modules/StarknetTransactions";
4
+ import {StarknetFees} from "./modules/StarknetFees";
5
+ import {StarknetAddresses} from "./modules/StarknetAddresses";
6
+ import {StarknetTokens} from "./modules/StarknetTokens";
7
+ import {StarknetEvents} from "./modules/StarknetEvents";
8
+ import {StarknetSignatures} from "./modules/StarknetSignatures";
9
+ import {StarknetAccounts} from "./modules/StarknetAccounts";
10
+ import {StarknetBlocks} from "./modules/StarknetBlocks";
11
+ import {ChainInterface, TransactionConfirmationOptions} from "@atomiqlabs/base";
12
+ import {StarknetSigner} from "../wallet/StarknetSigner";
13
+ import {Buffer} from "buffer";
14
+ import {StarknetKeypairWallet} from "../wallet/StarknetKeypairWallet";
15
+
16
+ export type StarknetRetryPolicy = {
17
+ maxRetries?: number,
18
+ delay?: number,
19
+ exponential?: boolean
20
+ }
21
+
22
+ export class StarknetChainInterface implements ChainInterface {
23
+
24
+ readonly chainId = "STARKNET";
25
+
26
+ readonly provider: Provider;
27
+ readonly retryPolicy: StarknetRetryPolicy;
28
+
29
+ public readonly starknetChainId: constants.StarknetChainId;
30
+
31
+ public Fees: StarknetFees;
32
+ public readonly Tokens: StarknetTokens;
33
+ public readonly Transactions: StarknetTransactions;
34
+ public readonly Signatures: StarknetSignatures;
35
+ public readonly Events: StarknetEvents;
36
+ public readonly Accounts: StarknetAccounts;
37
+ public readonly Blocks: StarknetBlocks;
38
+
39
+ protected readonly logger = getLogger("StarknetChainInterface: ");
40
+
41
+ constructor(
42
+ chainId: constants.StarknetChainId,
43
+ provider: Provider,
44
+ retryPolicy?: StarknetRetryPolicy,
45
+ solanaFeeEstimator: StarknetFees = new StarknetFees(provider)
46
+ ) {
47
+ this.starknetChainId = chainId;
48
+ this.provider = provider;
49
+ this.retryPolicy = retryPolicy;
50
+
51
+ this.Fees = solanaFeeEstimator;
52
+ this.Tokens = new StarknetTokens(this);
53
+ this.Transactions = new StarknetTransactions(this);
54
+
55
+ this.Signatures = new StarknetSignatures(this);
56
+ this.Events = new StarknetEvents(this);
57
+ this.Accounts = new StarknetAccounts(this);
58
+ this.Blocks = new StarknetBlocks(this);
59
+ }
60
+
61
+ async getBalance(signer: string, tokenAddress: string): Promise<bigint> {
62
+ //TODO: For native token we should discount the cost of deploying an account if it is not deployed yet
63
+ return await this.Tokens.getTokenBalance(signer, tokenAddress);
64
+ }
65
+
66
+ getNativeCurrencyAddress(): string {
67
+ return this.Tokens.getNativeCurrencyAddress();
68
+ }
69
+
70
+ isValidToken(tokenIdentifier: string): boolean {
71
+ return this.Tokens.isValidToken(tokenIdentifier);
72
+ }
73
+
74
+ isValidAddress(address: string): boolean {
75
+ return StarknetAddresses.isValidAddress(address);
76
+ }
77
+
78
+ ///////////////////////////////////
79
+ //// Callbacks & handlers
80
+ offBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): boolean {
81
+ return true;
82
+ }
83
+
84
+ onBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): void {}
85
+
86
+ onBeforeTxSigned(callback: (tx: StarknetTx) => Promise<void>): void {
87
+ this.Transactions.onBeforeTxSigned(callback);
88
+ }
89
+
90
+ offBeforeTxSigned(callback: (tx: StarknetTx) => Promise<void>): boolean {
91
+ return this.Transactions.offBeforeTxSigned(callback);
92
+ }
93
+
94
+ randomAddress(): string {
95
+ return toHex(stark.randomAddress());
96
+ }
97
+
98
+ randomSigner(): StarknetSigner {
99
+ const privateKey = "0x"+Buffer.from(ec.starkCurve.utils.randomPrivateKey()).toString("hex");
100
+ const wallet = new StarknetKeypairWallet(this.provider, privateKey);
101
+ return new StarknetSigner(wallet);
102
+ }
103
+
104
+ ////////////////////////////////////////////
105
+ //// Transactions
106
+ sendAndConfirm(
107
+ signer: StarknetSigner,
108
+ txs: StarknetTx[],
109
+ waitForConfirmation?: boolean,
110
+ abortSignal?: AbortSignal,
111
+ parallel?: boolean,
112
+ onBeforePublish?: (txId: string, rawTx: string) => Promise<void>
113
+ ): Promise<string[]> {
114
+ return this.Transactions.sendAndConfirm(signer, txs, waitForConfirmation, abortSignal, parallel, onBeforePublish);
115
+ }
116
+
117
+ serializeTx(tx: StarknetTx): Promise<string> {
118
+ return this.Transactions.serializeTx(tx);
119
+ }
120
+
121
+ deserializeTx(txData: string): Promise<StarknetTx> {
122
+ return this.Transactions.deserializeTx(txData);
123
+ }
124
+
125
+ getTxIdStatus(txId: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
126
+ return this.Transactions.getTxIdStatus(txId);
127
+ }
128
+
129
+ getTxStatus(tx: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
130
+ return this.Transactions.getTxStatus(tx);
131
+ }
132
+
133
+ txsTransfer(signer: string, token: string, amount: bigint, dstAddress: string, feeRate?: string): Promise<StarknetTx[]> {
134
+ return this.Tokens.txsTransfer(signer, token, amount, dstAddress, feeRate);
135
+ }
136
+
137
+ async transfer(
138
+ signer: StarknetSigner,
139
+ token: string,
140
+ amount: bigint,
141
+ dstAddress: string,
142
+ txOptions?: TransactionConfirmationOptions
143
+ ): Promise<string> {
144
+ const txs = await this.Tokens.txsTransfer(signer.getAddress(), token, amount, dstAddress, txOptions?.feeRate);
145
+ const [txId] = await this.Transactions.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
146
+ return txId;
147
+ }
148
+
149
+ }
@@ -1,16 +1,16 @@
1
1
  import {Provider} from "starknet";
2
- import {StarknetBase, StarknetRetryPolicy} from "./StarknetBase";
2
+ import {StarknetChainInterface, StarknetRetryPolicy} from "./StarknetChainInterface";
3
3
  import {getLogger} from "../../utils/Utils";
4
4
 
5
5
  export class StarknetModule {
6
6
  protected readonly provider: Provider;
7
7
  protected readonly retryPolicy: StarknetRetryPolicy;
8
- protected readonly root: StarknetBase;
8
+ protected readonly root: StarknetChainInterface;
9
9
 
10
10
  protected readonly logger = getLogger(this.constructor.name+": ");
11
11
 
12
12
  constructor(
13
- root: StarknetBase
13
+ root: StarknetChainInterface
14
14
  ) {
15
15
  this.provider = root.provider;
16
16
  this.retryPolicy = root.retryPolicy;
@@ -10,7 +10,7 @@ export class StarknetAddresses extends StarknetModule {
10
10
  *
11
11
  * @param value
12
12
  */
13
- isValidAddress(value: string): boolean {
13
+ static isValidAddress(value: string): boolean {
14
14
  if(value.length!==66) return false;
15
15
  try {
16
16
  validateAndParseAddress(value);
@@ -8,7 +8,7 @@ import {
8
8
  StarknetDomain, StarknetType,
9
9
  TypedData
10
10
  } from "starknet";
11
- import {StarknetBase} from "../StarknetBase";
11
+ import {StarknetChainInterface} from "../StarknetChainInterface";
12
12
  import {toHex} from "../../../utils/Utils";
13
13
  import {sha256} from "@noble/hashes/sha2";
14
14
 
@@ -27,7 +27,7 @@ export class StarknetSignatures extends StarknetModule {
27
27
 
28
28
  private readonly domain: StarknetDomain;
29
29
 
30
- constructor(root: StarknetBase, domainName: string = "atomiq.exchange") {
30
+ constructor(root: StarknetChainInterface, domainName: string = "atomiq.exchange") {
31
31
  super(root);
32
32
  this.domain = {
33
33
  name: domainName,
@@ -3,6 +3,7 @@ import {StarknetAction} from "../StarknetAction";
3
3
  import {ERC20Abi} from "./ERC20Abi";
4
4
  import { Contract } from "starknet";
5
5
  import {toBigInt} from "../../../utils/Utils";
6
+ import {StarknetAddresses} from "./StarknetAddresses";
6
7
 
7
8
 
8
9
  export class StarknetTokens extends StarknetModule {
@@ -63,7 +64,7 @@ export class StarknetTokens extends StarknetModule {
63
64
  * @param token
64
65
  */
65
66
  public isValidToken(token: string) {
66
- return this.root.Addresses.isValidAddress(token);
67
+ return StarknetAddresses.isValidAddress(token);
67
68
  }
68
69
 
69
70
  /**
@@ -6,7 +6,7 @@ import {
6
6
  BigNumberish
7
7
  } from "starknet";
8
8
  import {StarknetSigner} from "../../wallet/StarknetSigner";
9
- import {calculateHash, timeoutPromise, toHex, tryWithRetries} from "../../../utils/Utils";
9
+ import {calculateHash, timeoutPromise, toBigInt, toHex, tryWithRetries} from "../../../utils/Utils";
10
10
 
11
11
  export type StarknetTx = ({
12
12
  type: "DEPLOY_ACCOUNT",
@@ -23,6 +23,8 @@ export type StarknetTx = ({
23
23
 
24
24
  export class StarknetTransactions extends StarknetModule {
25
25
 
26
+ private readonly latestConfirmedNonces: {[address: string]: bigint} = {};
27
+
26
28
  private cbkBeforeTxSigned: (tx: StarknetTx) => Promise<void>;
27
29
 
28
30
  /**
@@ -37,12 +39,19 @@ export class StarknetTransactions extends StarknetModule {
37
39
  let state = "pending";
38
40
  while(state==="pending" || state==="not_found") {
39
41
  await timeoutPromise(3000, abortSignal);
40
- state = await this.getTxIdStatus(tx.txId);
41
- if(state==="not_found" && tx.signed!=null) await this.sendSignedTransaction(tx, undefined, undefined, false).catch(e => {
42
+ state = await this._getTxIdStatus(tx.txId);
43
+ if(state==="not_found" && tx.signed!=null) await this.sendSignedTransaction(tx).catch(e => {
42
44
  if(e.baseError?.code === 59) return; //Transaction already in the mempool
43
45
  console.error("Error on transaction re-send: ", e);
44
46
  });
45
47
  }
48
+ if(state!=="rejected") {
49
+ const nextAccountNonce = toBigInt(tx.details.nonce) + 1n;
50
+ const currentNonce = this.latestConfirmedNonces[tx.details.walletAddress];
51
+ if(currentNonce==null || nextAccountNonce > currentNonce) {
52
+ this.latestConfirmedNonces[tx.details.walletAddress] = nextAccountNonce;
53
+ }
54
+ }
46
55
  if(state==="reverted") throw new Error("Transaction reverted!");
47
56
  }
48
57
 
@@ -55,6 +64,11 @@ export class StarknetTransactions extends StarknetModule {
55
64
  */
56
65
  private async prepareTransactions(signer: StarknetSigner, txs: StarknetTx[]): Promise<void> {
57
66
  let nonce: bigint = await signer.getNonce();
67
+ const latestConfirmedNonce = this.latestConfirmedNonces[signer.getAddress()];
68
+ if(latestConfirmedNonce!=null && latestConfirmedNonce > nonce) {
69
+ console.debug("StarknetTransactions: prepareTransactions(): Using nonce from local cache!");
70
+ nonce = latestConfirmedNonce;
71
+ }
58
72
  if(nonce===BigInt(0) && signer.isWalletAccount()) {
59
73
  //Just increment the nonce by one and hope the wallet is smart enough to deploy account first
60
74
  nonce = BigInt(1);
@@ -84,14 +98,12 @@ export class StarknetTransactions extends StarknetModule {
84
98
  * @param tx Starknet tx to send
85
99
  * @param onBeforePublish a callback called before every transaction is published
86
100
  * @param signer
87
- * @param retryOnSubmissionFailure
88
101
  * @private
89
102
  */
90
103
  private async sendSignedTransaction(
91
104
  tx: StarknetTx,
92
105
  onBeforePublish?: (txId: string, rawTx: string) => Promise<void>,
93
- signer?: StarknetSigner,
94
- retryOnSubmissionFailure: boolean = true
106
+ signer?: StarknetSigner
95
107
  ): Promise<string> {
96
108
  if(onBeforePublish!=null) await onBeforePublish(tx.txId, await this.serializeTx(tx));
97
109
  this.logger.debug("sendSignedTransaction(): sending transaction: ", tx);
@@ -112,16 +124,17 @@ export class StarknetTransactions extends StarknetModule {
112
124
  return txHash;
113
125
  }
114
126
 
115
- const txResult = await tryWithRetries(() => {
116
- switch(tx.type) {
117
- case "INVOKE":
118
- return this.provider.channel.invoke(tx.signed, tx.details).then(res => res.transaction_hash);
119
- case "DEPLOY_ACCOUNT":
120
- return this.provider.channel.deployAccount(tx.signed, tx.details).then((res: any) => res.transaction_hash);
121
- default:
122
- throw new Error("Unsupported tx type!");
123
- }
124
- }, retryOnSubmissionFailure ? this.retryPolicy : {maxRetries: 1});
127
+ let txResult: string;
128
+ switch(tx.type) {
129
+ case "INVOKE":
130
+ txResult = await this.provider.channel.invoke(tx.signed, tx.details).then(res => res.transaction_hash);
131
+ break;
132
+ case "DEPLOY_ACCOUNT":
133
+ txResult = await this.provider.channel.deployAccount(tx.signed, tx.details).then((res: any) => res.transaction_hash);
134
+ break;
135
+ default:
136
+ throw new Error("Unsupported tx type!");
137
+ }
125
138
  if(tx.txId!==txResult) this.logger.warn("sendSignedTransaction(): sent tx hash not matching the precomputed hash!");
126
139
  this.logger.info("sendSignedTransaction(): tx sent, expected txHash: "+tx.txId+", txHash: "+txResult);
127
140
  return txResult;
@@ -229,19 +242,31 @@ export class StarknetTransactions extends StarknetModule {
229
242
  *
230
243
  * @param txId
231
244
  */
232
- public async getTxIdStatus(txId: string): Promise<"pending" | "success" | "not_found" | "reverted"> {
245
+ public async _getTxIdStatus(txId: string): Promise<"pending" | "success" | "not_found" | "reverted" | "rejected"> {
233
246
  const status = await this.provider.getTransactionStatus(txId).catch(e => {
234
247
  if(e.message!=null && e.message.includes("29: Transaction hash not found")) return null;
235
248
  throw e;
236
249
  });
237
250
  if(status==null) return "not_found";
238
251
  if(status.finality_status==="RECEIVED") return "pending";
239
- if(status.finality_status!=="REJECTED" && status.execution_status==="SUCCEEDED"){
252
+ if(status.finality_status==="REJECTED") return "rejected";
253
+ if(status.execution_status==="SUCCEEDED"){
240
254
  return "success";
241
255
  }
242
256
  return "reverted";
243
257
  }
244
258
 
259
+ /**
260
+ * Gets the status of the starknet transaction with a specific txId
261
+ *
262
+ * @param txId
263
+ */
264
+ public async getTxIdStatus(txId: string): Promise<"pending" | "success" | "not_found" | "reverted"> {
265
+ const status = await this._getTxIdStatus(txId);
266
+ if(status==="rejected") return "reverted";
267
+ return status;
268
+ }
269
+
245
270
  public onBeforeTxSigned(callback: (tx: StarknetTx) => Promise<void>): void {
246
271
  this.cbkBeforeTxSigned = callback;
247
272
  }