@atomiqlabs/chain-evm 2.1.12 → 2.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +75 -0
  2. package/dist/chains/EVMOptions.d.ts +66 -0
  3. package/dist/chains/EVMOptions.js +2 -0
  4. package/dist/chains/alpen/AlpenInitializer.d.ts +3 -30
  5. package/dist/chains/alpen/AlpenInitializer.js +3 -3
  6. package/dist/chains/botanix/BotanixInitializer.d.ts +3 -30
  7. package/dist/chains/botanix/BotanixInitializer.js +3 -3
  8. package/dist/chains/citrea/CitreaBtcRelay.d.ts +5 -0
  9. package/dist/chains/citrea/CitreaBtcRelay.js +7 -2
  10. package/dist/chains/citrea/CitreaFees.d.ts +3 -5
  11. package/dist/chains/citrea/CitreaFees.js +3 -5
  12. package/dist/chains/citrea/CitreaInitializer.d.ts +3 -29
  13. package/dist/chains/citrea/CitreaInitializer.js +3 -3
  14. package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +5 -0
  15. package/dist/chains/citrea/CitreaSpvVaultContract.js +7 -2
  16. package/dist/chains/citrea/CitreaSwapContract.d.ts +7 -2
  17. package/dist/chains/citrea/CitreaSwapContract.js +10 -5
  18. package/dist/chains/citrea/CitreaTokens.d.ts +5 -0
  19. package/dist/chains/citrea/CitreaTokens.js +5 -0
  20. package/dist/chains/goat/GoatInitializer.d.ts +3 -30
  21. package/dist/chains/goat/GoatInitializer.js +3 -3
  22. package/dist/evm/btcrelay/EVMBtcRelay.d.ts +41 -10
  23. package/dist/evm/btcrelay/EVMBtcRelay.js +50 -18
  24. package/dist/evm/btcrelay/headers/EVMBtcHeader.d.ts +53 -7
  25. package/dist/evm/btcrelay/headers/EVMBtcHeader.js +43 -5
  26. package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.d.ts +53 -8
  27. package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.js +41 -1
  28. package/dist/evm/chain/EVMChainInterface.d.ts +57 -2
  29. package/dist/evm/chain/EVMChainInterface.js +7 -7
  30. package/dist/evm/chain/EVMModule.d.ts +5 -0
  31. package/dist/evm/chain/EVMModule.js +6 -1
  32. package/dist/evm/chain/modules/EVMBlocks.d.ts +7 -0
  33. package/dist/evm/chain/modules/EVMBlocks.js +2 -0
  34. package/dist/evm/chain/modules/EVMEvents.js +19 -19
  35. package/dist/evm/chain/modules/EVMFees.d.ts +41 -5
  36. package/dist/evm/chain/modules/EVMFees.js +24 -5
  37. package/dist/evm/chain/modules/EVMTokens.d.ts +1 -1
  38. package/dist/evm/chain/modules/EVMTokens.js +1 -1
  39. package/dist/evm/chain/modules/EVMTransactions.d.ts +20 -2
  40. package/dist/evm/chain/modules/EVMTransactions.js +11 -8
  41. package/dist/evm/contract/EVMContractBase.d.ts +28 -10
  42. package/dist/evm/contract/EVMContractBase.js +9 -18
  43. package/dist/evm/contract/EVMContractModule.d.ts +5 -0
  44. package/dist/evm/contract/EVMContractModule.js +5 -0
  45. package/dist/evm/contract/modules/EVMContractEvents.d.ts +7 -1
  46. package/dist/evm/contract/modules/EVMContractEvents.js +23 -3
  47. package/dist/evm/events/EVMChainEvents.d.ts +8 -0
  48. package/dist/evm/events/EVMChainEvents.js +8 -0
  49. package/dist/evm/events/EVMChainEventsBrowser.d.ts +87 -19
  50. package/dist/evm/events/EVMChainEventsBrowser.js +53 -18
  51. package/dist/evm/providers/JsonRpcProviderWithRetries.d.ts +9 -0
  52. package/dist/evm/providers/JsonRpcProviderWithRetries.js +9 -0
  53. package/dist/evm/providers/ReconnectingWebSocketProvider.d.ts +5 -0
  54. package/dist/evm/providers/ReconnectingWebSocketProvider.js +5 -0
  55. package/dist/evm/providers/WebSocketProviderWithRetries.d.ts +9 -0
  56. package/dist/evm/providers/WebSocketProviderWithRetries.js +9 -0
  57. package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +46 -21
  58. package/dist/evm/spv_swap/EVMSpvVaultContract.js +61 -23
  59. package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +57 -2
  60. package/dist/evm/spv_swap/EVMSpvVaultData.js +57 -2
  61. package/dist/evm/spv_swap/EVMSpvWithdrawalData.d.ts +12 -0
  62. package/dist/evm/spv_swap/EVMSpvWithdrawalData.js +12 -0
  63. package/dist/evm/swaps/EVMSwapContract.d.ts +58 -13
  64. package/dist/evm/swaps/EVMSwapContract.js +81 -54
  65. package/dist/evm/swaps/EVMSwapData.d.ts +27 -6
  66. package/dist/evm/swaps/EVMSwapData.js +26 -0
  67. package/dist/evm/swaps/EVMSwapModule.d.ts +5 -0
  68. package/dist/evm/swaps/EVMSwapModule.js +5 -0
  69. package/dist/evm/swaps/handlers/IHandler.d.ts +5 -0
  70. package/dist/evm/swaps/handlers/claim/ClaimHandlers.d.ts +15 -0
  71. package/dist/evm/swaps/handlers/claim/ClaimHandlers.js +5 -0
  72. package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +5 -0
  73. package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +10 -0
  74. package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +5 -0
  75. package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +15 -0
  76. package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +7 -2
  77. package/dist/evm/swaps/modules/EVMLpVault.d.ts +5 -0
  78. package/dist/evm/swaps/modules/EVMLpVault.js +9 -4
  79. package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +7 -2
  80. package/dist/evm/swaps/modules/EVMSwapClaim.js +11 -6
  81. package/dist/evm/swaps/modules/EVMSwapInit.d.ts +10 -0
  82. package/dist/evm/swaps/modules/EVMSwapInit.js +11 -6
  83. package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +5 -0
  84. package/dist/evm/swaps/modules/EVMSwapRefund.js +9 -4
  85. package/dist/evm/wallet/EVMBrowserSigner.d.ts +22 -2
  86. package/dist/evm/wallet/EVMBrowserSigner.js +40 -2
  87. package/dist/evm/wallet/EVMPersistentSigner.d.ts +13 -2
  88. package/dist/evm/wallet/EVMPersistentSigner.js +13 -1
  89. package/dist/evm/wallet/EVMSigner.d.ts +30 -1
  90. package/dist/evm/wallet/EVMSigner.js +34 -1
  91. package/dist/index.d.ts +71 -0
  92. package/dist/index.js +70 -0
  93. package/dist/node/index.d.ts +10 -0
  94. package/dist/node/index.js +15 -0
  95. package/dist/utils/Utils.d.ts +50 -0
  96. package/dist/utils/Utils.js +45 -0
  97. package/node/index.d.ts +1 -0
  98. package/node/index.js +3 -0
  99. package/package.json +4 -3
  100. package/src/chains/EVMOptions.ts +70 -0
  101. package/src/chains/alpen/AlpenInitializer.ts +5 -27
  102. package/src/chains/botanix/BotanixChainType.ts +5 -5
  103. package/src/chains/botanix/BotanixInitializer.ts +5 -27
  104. package/src/chains/citrea/CitreaBtcRelay.ts +8 -3
  105. package/src/chains/citrea/CitreaFees.ts +3 -6
  106. package/src/chains/citrea/CitreaInitializer.ts +5 -27
  107. package/src/chains/citrea/CitreaSpvVaultContract.ts +7 -2
  108. package/src/chains/citrea/CitreaSwapContract.ts +11 -6
  109. package/src/chains/citrea/CitreaTokens.ts +6 -1
  110. package/src/chains/goat/GoatChainType.ts +5 -5
  111. package/src/chains/goat/GoatInitializer.ts +3 -25
  112. package/src/evm/btcrelay/EVMBtcRelay.ts +54 -22
  113. package/src/evm/btcrelay/headers/EVMBtcHeader.ts +60 -13
  114. package/src/evm/btcrelay/headers/EVMBtcStoredHeader.ts +55 -10
  115. package/src/evm/chain/EVMChainInterface.ts +66 -14
  116. package/src/evm/chain/EVMModule.ts +6 -1
  117. package/src/evm/chain/modules/EVMBlocks.ts +7 -0
  118. package/src/evm/chain/modules/EVMEvents.ts +19 -19
  119. package/src/evm/chain/modules/EVMFees.ts +41 -5
  120. package/src/evm/chain/modules/EVMTokens.ts +1 -1
  121. package/src/evm/chain/modules/EVMTransactions.ts +27 -8
  122. package/src/evm/contract/EVMContractBase.ts +29 -24
  123. package/src/evm/contract/EVMContractModule.ts +5 -0
  124. package/src/evm/contract/modules/EVMContractEvents.ts +27 -8
  125. package/src/evm/events/EVMChainEvents.ts +8 -0
  126. package/src/evm/events/EVMChainEventsBrowser.ts +103 -29
  127. package/src/evm/providers/JsonRpcProviderWithRetries.ts +10 -1
  128. package/src/evm/providers/ReconnectingWebSocketProvider.ts +6 -1
  129. package/src/evm/providers/WebSocketProviderWithRetries.ts +10 -1
  130. package/src/evm/spv_swap/EVMSpvVaultContract.ts +72 -32
  131. package/src/evm/spv_swap/EVMSpvVaultData.ts +57 -2
  132. package/src/evm/spv_swap/EVMSpvWithdrawalData.ts +12 -0
  133. package/src/evm/swaps/EVMSwapContract.ts +108 -63
  134. package/src/evm/swaps/EVMSwapData.ts +27 -1
  135. package/src/evm/swaps/EVMSwapModule.ts +5 -0
  136. package/src/evm/swaps/handlers/IHandler.ts +5 -0
  137. package/src/evm/swaps/handlers/claim/ClaimHandlers.ts +15 -0
  138. package/src/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +5 -0
  139. package/src/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +10 -0
  140. package/src/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +5 -0
  141. package/src/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +17 -2
  142. package/src/evm/swaps/modules/EVMLpVault.ts +10 -5
  143. package/src/evm/swaps/modules/EVMSwapClaim.ts +12 -7
  144. package/src/evm/swaps/modules/EVMSwapInit.ts +17 -7
  145. package/src/evm/swaps/modules/EVMSwapRefund.ts +9 -4
  146. package/src/evm/wallet/EVMBrowserSigner.ts +44 -5
  147. package/src/evm/wallet/EVMPersistentSigner.ts +14 -2
  148. package/src/evm/wallet/EVMSigner.ts +37 -1
  149. package/src/index.ts +72 -0
  150. package/src/node/index.ts +10 -0
  151. package/src/utils/Utils.ts +50 -1
@@ -8,11 +8,21 @@ import {keccak256} from "ethers";
8
8
  import {Buffer} from "buffer";
9
9
  import {EVMSwapData} from "../../../EVMSwapData";
10
10
 
11
+ /**
12
+ * Common commitment fields used by all bitcoin-based claim handlers.
13
+ *
14
+ * @category Internal/Handlers
15
+ */
11
16
  export type BitcoinCommitmentData = {
12
17
  btcRelay: EVMBtcRelay<any>,
13
18
  confirmations: number
14
19
  }
15
20
 
21
+ /**
22
+ * Common witness input for bitcoin-based claim handlers.
23
+ *
24
+ * @category Internal/Handlers
25
+ */
16
26
  export type BitcoinWitnessData = {
17
27
  tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
18
28
  requiredConfirmations: number,
@@ -23,6 +33,11 @@ export type BitcoinWitnessData = {
23
33
 
24
34
  const logger = getLogger("IBitcoinClaimHandler: ");
25
35
 
36
+ /**
37
+ * Shared base implementation for bitcoin-backed claim handlers.
38
+ *
39
+ * @category Internal/Handlers
40
+ */
26
41
  export abstract class IBitcoinClaimHandler<C, W extends BitcoinWitnessData> implements IClaimHandler<C & BitcoinCommitmentData, W> {
27
42
 
28
43
  public readonly address: string;
@@ -38,7 +53,7 @@ export abstract class IBitcoinClaimHandler<C, W extends BitcoinWitnessData> impl
38
53
  protected serializeCommitment(data: BitcoinCommitmentData): Buffer {
39
54
  const buffer = Buffer.alloc(24);
40
55
  buffer.writeUint32BE(data.confirmations, 0);
41
- Buffer.from(data.btcRelay.contractAddress.substring(2), "hex").copy(buffer, 4, 0, 20);
56
+ Buffer.from(data.btcRelay._contractAddress.substring(2), "hex").copy(buffer, 4, 0, 20);
42
57
  return buffer;
43
58
  }
44
59
 
@@ -68,7 +83,7 @@ export abstract class IBitcoinClaimHandler<C, W extends BitcoinWitnessData> impl
68
83
 
69
84
  if(!swapData.isClaimData(commitmentHash)) throw new Error("Invalid commit data");
70
85
 
71
- const merkleProof = await btcRelay.bitcoinRpc.getMerkleProof(tx.txid, tx.blockhash);
86
+ const merkleProof = await btcRelay._bitcoinRpc.getMerkleProof(tx.txid, tx.blockhash);
72
87
  if(merkleProof==null) throw new Error(`Failed to generate merkle proof for tx: ${tx.txid}!`);
73
88
  logger.debug("getWitness(): merkle proof computed: ", merkleProof);
74
89
 
@@ -4,6 +4,11 @@ import {TransactionRequest} from "ethers";
4
4
  import {EVMFees} from "../../chain/modules/EVMFees";
5
5
  import {EVMTx} from "../../chain/modules/EVMTransactions";
6
6
 
7
+ /**
8
+ * LP vault helper for intermediary balances, reputation and LP deposit/withdraw transactions.
9
+ *
10
+ * @category Internal/Swaps
11
+ */
7
12
  export class EVMLpVault extends EVMSwapModule {
8
13
 
9
14
  private static readonly GasCosts = {
@@ -71,13 +76,13 @@ export class EVMLpVault extends EVMSwapModule {
71
76
  * @param token
72
77
  */
73
78
  public async getIntermediaryReputation(address: string, token: string): Promise<IntermediaryReputationType> {
74
- const filter = Object.keys(this.contract.claimHandlersByAddress).map(claimHandler => ({owner: address, token, claimHandler}));
79
+ const filter = Object.keys(this.contract._claimHandlersByAddress).map(claimHandler => ({owner: address, token, claimHandler}));
75
80
  const resp = await this.swapContract.getReputation(filter);
76
81
  if(resp.length!==filter.length) throw new Error("getIntermediaryReputation(): Invalid response length");
77
82
 
78
83
  const result: any = {};
79
- Object.keys(this.contract.claimHandlersByAddress).forEach((address, index) => {
80
- const handler = this.contract.claimHandlersByAddress[address.toLowerCase()];
84
+ Object.keys(this.contract._claimHandlersByAddress).forEach((address, index) => {
85
+ const handler = this.contract._claimHandlersByAddress[address.toLowerCase()];
81
86
  const handlerResp = resp[index];
82
87
  result[handler.getType()] = {
83
88
  successVolume: handlerResp[0].amount,
@@ -140,7 +145,7 @@ export class EVMLpVault extends EVMSwapModule {
140
145
 
141
146
  //Approve first
142
147
  if(token.toLowerCase()!==this.root.getNativeCurrencyAddress().toLowerCase()) {
143
- const approveTx = await this.root.Tokens.checkAndGetApproveTx(signer, token, amount, this.contract.contractAddress, feeRate);
148
+ const approveTx = await this.root.Tokens.checkAndGetApproveTx(signer, token, amount, this.contract._contractAddress, feeRate);
144
149
  if(approveTx!=null) txs.push(approveTx);
145
150
  }
146
151
 
@@ -152,4 +157,4 @@ export class EVMLpVault extends EVMSwapModule {
152
157
  return txs;
153
158
  }
154
159
 
155
- }
160
+ }
@@ -10,6 +10,11 @@ import {EVMFees} from "../../chain/modules/EVMFees";
10
10
  import {EVMTx} from "../../chain/modules/EVMTransactions";
11
11
  import {EVMBtcStoredHeader} from "../../btcrelay/headers/EVMBtcStoredHeader";
12
12
 
13
+ /**
14
+ * Swap claim helper for HTLC and BTC on-chain claim paths.
15
+ *
16
+ * @category Internal/Swaps
17
+ */
13
18
  export class EVMSwapClaim extends EVMSwapModule {
14
19
 
15
20
  private static readonly GasCosts = {
@@ -67,7 +72,7 @@ export class EVMSwapClaim extends EVMSwapModule {
67
72
  throw new SwapDataVerificationError("Not enough time to reliably pay the invoice");
68
73
  }
69
74
 
70
- const claimHandler: IClaimHandler<Buffer, string> = this.contract.claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
75
+ const claimHandler: IClaimHandler<Buffer, string> = this.contract._claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
71
76
  if(claimHandler==null) throw new SwapDataVerificationError("Unknown claim handler!");
72
77
  if(claimHandler.getType()!==ChainSwapType.HTLC) throw new SwapDataVerificationError("Invalid claim handler!");
73
78
 
@@ -89,7 +94,7 @@ export class EVMSwapClaim extends EVMSwapModule {
89
94
  * @param tx bitcoin transaction that satisfies the swap condition
90
95
  * @param requiredConfirmations
91
96
  * @param vout vout of the bitcoin transaction that satisfies the swap condition
92
- * @param commitedHeader commited header data from btc relay (fetched internally if null)
97
+ * @param commitedHeader committed header data from BTC relay (fetched internally if null)
93
98
  * @param synchronizer optional synchronizer to use in case we need to sync up the btc relay ourselves
94
99
  * @param feeRate fee rate to be used for the transactions
95
100
  */
@@ -103,7 +108,7 @@ export class EVMSwapClaim extends EVMSwapModule {
103
108
  synchronizer?: RelaySynchronizer<EVMBtcStoredHeader, EVMTx, any>,
104
109
  feeRate?: string
105
110
  ): Promise<EVMTx[]> {
106
- const claimHandler: IClaimHandler<any, BitcoinOutputWitnessData | BitcoinWitnessData> = this.contract.claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
111
+ const claimHandler: IClaimHandler<any, BitcoinOutputWitnessData | BitcoinWitnessData> = this.contract._claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
107
112
  if(claimHandler==null) throw new SwapDataVerificationError("Unknown claim handler!");
108
113
  if(
109
114
  claimHandler.getType()!==ChainSwapType.CHAIN_NONCED &&
@@ -118,7 +123,7 @@ export class EVMSwapClaim extends EVMSwapModule {
118
123
  vout,
119
124
  requiredConfirmations,
120
125
  commitedHeader,
121
- btcRelay: this.contract.btcRelay,
126
+ btcRelay: this.contract._btcRelay,
122
127
  synchronizer,
123
128
  }, feeRate);
124
129
  const claimTx = await this.Claim(signer, swapData, witness, feeRate, claimHandler.getGas(swapData));
@@ -156,7 +161,7 @@ export class EVMSwapClaim extends EVMSwapModule {
156
161
  }
157
162
 
158
163
  /**
159
- * Get the estimated starknet transaction fee of the claim transaction
164
+ * Returns the estimated fee of the claim transaction.
160
165
  */
161
166
  public async getClaimFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
162
167
  feeRate ??= await this.root.Fees.getFeeRate();
@@ -164,10 +169,10 @@ export class EVMSwapClaim extends EVMSwapModule {
164
169
  //TODO: Claim with success action not supported yet!
165
170
  let gasRequired = this.getClaimGas(swapData);
166
171
 
167
- const claimHandler: IClaimHandler<any, any> = this.contract.claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
172
+ const claimHandler: IClaimHandler<any, any> = this.contract._claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
168
173
  if(claimHandler!=null) gasRequired += claimHandler.getGas(swapData);
169
174
 
170
175
  return EVMFees.getGasFee(gasRequired, feeRate);
171
176
  }
172
177
 
173
- }
178
+ }
@@ -6,6 +6,11 @@ import {EVMFees} from "../../chain/modules/EVMFees";
6
6
  import {EVMSigner} from "../../wallet/EVMSigner";
7
7
  import {EVMTx} from "../../chain/modules/EVMTransactions";
8
8
 
9
+ /**
10
+ * Prefetched values used for initialization signature verification.
11
+ *
12
+ * @category Internal/Swaps
13
+ */
9
14
  export type EVMPreFetchVerification = {
10
15
  safeBlockTime: number
11
16
  };
@@ -31,6 +36,11 @@ const Initialize = [
31
36
  { name: "extraDataHash", type: "bytes32" }
32
37
  ];
33
38
 
39
+ /**
40
+ * Swap initialization helper handling authorization signatures and init transaction construction.
41
+ *
42
+ * @category Internal/Swaps
43
+ */
34
44
  export class EVMSwapInit extends EVMSwapModule {
35
45
 
36
46
  private static readonly GasCosts = {
@@ -76,7 +86,7 @@ export class EVMSwapInit extends EVMSwapModule {
76
86
 
77
87
  public async preFetchForInitSignatureVerification(): Promise<EVMPreFetchVerification> {
78
88
  return {
79
- safeBlockTime: await this.root.Blocks.getBlockTime(this.root.config.safeBlockTag)
89
+ safeBlockTime: await this.root.Blocks.getBlockTime(this.root._config.safeBlockTag)
80
90
  };
81
91
  }
82
92
 
@@ -96,7 +106,7 @@ export class EVMSwapInit extends EVMSwapModule {
96
106
  ): Promise<{prefix: string, timeout: string, signature: string}> {
97
107
  const authExpiry = Math.floor(Date.now()/1000)+authorizationTimeout;
98
108
 
99
- const signature = await this.root.Signatures.signTypedMessage(this.contract.contractAddress, signer, Initialize, "Initialize", {
109
+ const signature = await this.root.Signatures.signTypedMessage(this.contract._contractAddress, signer, Initialize, "Initialize", {
100
110
  "swapHash": "0x"+swapData.getEscrowHash(),
101
111
  "offerer": swapData.offerer,
102
112
  "claimer": swapData.claimer,
@@ -156,11 +166,11 @@ export class EVMSwapInit extends EVMSwapModule {
156
166
 
157
167
  const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
158
168
  const timeoutBN = BigInt(timeout);
159
- const isExpired = (timeoutBN - currentTimestamp) < BigInt(this.contract.authGracePeriod);
169
+ const isExpired = (timeoutBN - currentTimestamp) < BigInt(this.contract._authGracePeriod);
160
170
  if (isExpired) throw new SignatureVerificationError("Authorization expired!");
161
171
  if(await this.isSignatureExpired(timeout, preFetchData)) throw new SignatureVerificationError("Authorization expired!");
162
172
 
163
- const valid = await this.root.Signatures.isValidSignature(this.contract.contractAddress, signature, signer, Initialize, "Initialize", {
173
+ const valid = await this.root.Signatures.isValidSignature(this.contract._contractAddress, signature, signer, Initialize, "Initialize", {
164
174
  "swapHash": "0x"+swapData.getEscrowHash(),
165
175
  "offerer": swapData.offerer,
166
176
  "claimer": swapData.claimer,
@@ -196,7 +206,7 @@ export class EVMSwapInit extends EVMSwapModule {
196
206
  timeout: string
197
207
  ): Promise<number> {
198
208
  const now = Date.now();
199
- const timeoutExpiryTime = (parseInt(timeout)-this.contract.authGracePeriod)*1000;
209
+ const timeoutExpiryTime = (parseInt(timeout)-this.contract._authGracePeriod)*1000;
200
210
 
201
211
  if(timeoutExpiryTime<now) return 0;
202
212
 
@@ -272,7 +282,7 @@ export class EVMSwapInit extends EVMSwapModule {
272
282
  }
273
283
 
274
284
  const requiredApprovalTxns = await Promise.all(
275
- Object.keys(requiredApprovals).map(token => this.root.Tokens.checkAndGetApproveTx(sender, token, requiredApprovals[token], this.contract.contractAddress, feeRate))
285
+ Object.keys(requiredApprovals).map(token => this.root.Tokens.checkAndGetApproveTx(sender, token, requiredApprovals[token], this.contract._contractAddress, feeRate))
276
286
  );
277
287
  requiredApprovalTxns.forEach(tx => tx!=null && txs.push(tx));
278
288
 
@@ -324,4 +334,4 @@ export class EVMSwapInit extends EVMSwapModule {
324
334
 
325
335
  return totalFee;
326
336
  }
327
- }
337
+ }
@@ -13,6 +13,11 @@ const Refund = [
13
13
  { name: "timeout", type: "uint256" }
14
14
  ];
15
15
 
16
+ /**
17
+ * Swap refund helper for timeout and cooperative refund flows.
18
+ *
19
+ * @category Internal/Swaps
20
+ */
16
21
  export class EVMSwapRefund extends EVMSwapModule {
17
22
 
18
23
  private static readonly GasCosts = {
@@ -77,7 +82,7 @@ export class EVMSwapRefund extends EVMSwapModule {
77
82
  const authPrefix = "refund";
78
83
  const authTimeout = Math.floor(Date.now()/1000)+authorizationTimeout;
79
84
 
80
- const signature = await this.root.Signatures.signTypedMessage(this.contract.contractAddress, signer, Refund, "Refund", {
85
+ const signature = await this.root.Signatures.signTypedMessage(this.contract._contractAddress, signer, Refund, "Refund", {
81
86
  "swapHash": "0x"+swapData.getEscrowHash(),
82
87
  "timeout": BigInt(authTimeout)
83
88
  });
@@ -100,10 +105,10 @@ export class EVMSwapRefund extends EVMSwapModule {
100
105
  const expiryTimestamp = BigInt(timeout);
101
106
  const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
102
107
 
103
- const isExpired = (expiryTimestamp - currentTimestamp) < BigInt(this.contract.authGracePeriod);
108
+ const isExpired = (expiryTimestamp - currentTimestamp) < BigInt(this.contract._authGracePeriod);
104
109
  if(isExpired) throw new SignatureVerificationError("Authorization expired!");
105
110
 
106
- const valid = await this.root.Signatures.isValidSignature(this.contract.contractAddress, signature, swapData.claimer, Refund, "Refund", {
111
+ const valid = await this.root.Signatures.isValidSignature(this.contract._contractAddress, signature, swapData.claimer, Refund, "Refund", {
107
112
  "swapHash": "0x"+swapData.getEscrowHash(),
108
113
  "timeout": BigInt(expiryTimestamp)
109
114
  });
@@ -131,7 +136,7 @@ export class EVMSwapRefund extends EVMSwapModule {
131
136
  feeRate?: string,
132
137
  witnessData?: T
133
138
  ): Promise<EVMTx[]> {
134
- const refundHandler: IHandler<any, T> = this.contract.refundHandlersByAddress[swapData.refundHandler.toLowerCase()];
139
+ const refundHandler: IHandler<any, T> = this.contract._refundHandlersByAddress[swapData.refundHandler.toLowerCase()];
135
140
  if(refundHandler==null) throw new Error("Invalid refund handler");
136
141
 
137
142
  if(check && !await this.contract.isRequestRefundable(swapData.offerer.toString(), swapData)) {
@@ -1,18 +1,57 @@
1
- import {Signer, TransactionRequest, TransactionResponse} from "ethers";
1
+ import {Signer, TransactionRequest, TransactionResponse, verifyMessage} from "ethers";
2
2
  import {EVMSigner} from "./EVMSigner";
3
3
 
4
-
5
4
  /**
6
- * Browser-based EVM signer for external wallet integration
5
+ * Browser-based EVM signer, intended for injected/external wallets. This ensures no explicit
6
+ * `signTransaction()` flow is required and transaction submission goes through `sendTransaction()`.
7
+ *
7
8
  * @category Wallets
8
9
  */
9
10
  export class EVMBrowserSigner extends EVMSigner {
10
11
 
11
- constructor(account: Signer, address: string) {
12
+ private usesECDSADN?: boolean;
13
+
14
+ getReproducibleEntropy?: (appName: string) => Promise<Buffer>;
15
+
16
+ /**
17
+ * @param account Signer account to request signatures and send transaction through
18
+ * @param address Signer address
19
+ * @param usesECDSADN Optional flag indicating whether the signer supports signing using ECDSA-DN (deterministic
20
+ * nonce) algorithm, this allows the wallet to produce reproducible entropy. Only pass `true` here if you are
21
+ * 100% sure that the signer supports this!
22
+ */
23
+ constructor(account: Signer, address: string, usesECDSADN?: boolean) {
12
24
  super(account, address, false);
25
+ this.usesECDSADN = usesECDSADN;
13
26
  this.signTransaction = undefined;
27
+ if(this.usesECDSADN!==false) {
28
+ this.getReproducibleEntropy = async (appName: string) => {
29
+ if(this.usesECDSADN===false) throw new Error("This wallet doesn't support generating recoverable entropy!");
30
+
31
+ const message = EVMSigner.getReproducibleEntropyMessage(appName);
32
+ const signature = await account.signMessage(message);
33
+ if(this.usesECDSADN!==true) {
34
+ const secondSignature = await account.signMessage(message);
35
+ if(signature!==secondSignature) {
36
+ this.usesECDSADN = false;
37
+ this.getReproducibleEntropy = undefined;
38
+ throw new Error("This wallet doesn't support generating recoverable entropy!");
39
+ }
40
+ this.usesECDSADN = true;
41
+ }
42
+ if(verifyMessage(message, signature)!==address) throw new Error("Invalid wallet signature provided!");
43
+ return Buffer.from(signature.substring(2), "hex");
44
+ }
45
+ }
14
46
  }
15
47
 
48
+ /**
49
+ * Signs and sends the provided EVM transaction.
50
+ * Maps common wallet rejection errors to a consistent user-facing message.
51
+ *
52
+ * @param transaction A transaction to sign and send
53
+ * @param onBeforePublish Optional callback called after signing and before broadcast when available
54
+ */
16
55
  async sendTransaction(transaction: TransactionRequest, onBeforePublish?: (txId: string, rawTx: string) => Promise<void>): Promise<TransactionResponse> {
17
56
  try {
18
57
  return await super.sendTransaction(transaction, onBeforePublish);
@@ -23,4 +62,4 @@ export class EVMBrowserSigner extends EVMSigner {
23
62
  }
24
63
  }
25
64
 
26
- }
65
+ }
@@ -16,9 +16,15 @@ const WAIT_BEFORE_BUMP = 15*1000;
16
16
  const MIN_FEE_INCREASE_ABSOLUTE = 1n*1_000_000_000n; //1GWei
17
17
  const MIN_FEE_INCREASE_PPM = 100_000n; // +10%
18
18
 
19
+ /**
20
+ * A robust EVM signer implementation with internal nonce management, automatic rebroadcasting and fee bumping.
21
+ * Uses Node.js `fs` to persist transaction data across restarts, so it is intended for backend runtimes.
22
+ *
23
+ * @category Wallets
24
+ */
19
25
  export class EVMPersistentSigner extends EVMSigner {
20
26
 
21
- readonly safeBlockTag: EVMBlockTag;
27
+ private readonly safeBlockTag: EVMBlockTag;
22
28
 
23
29
  private pendingTxs: Map<number, {
24
30
  txs: Transaction[],
@@ -58,7 +64,7 @@ export class EVMPersistentSigner extends EVMSigner {
58
64
  this.minFeeIncreaseAbsolute = minFeeIncreaseAbsolute ?? MIN_FEE_INCREASE_ABSOLUTE;
59
65
  this.minFeeIncreasePpm = minFeeIncreasePpm ?? MIN_FEE_INCREASE_PPM;
60
66
  this.waitBeforeBump = waitBeforeBumpMillis ?? WAIT_BEFORE_BUMP;
61
- this.safeBlockTag = chainInterface.config.safeBlockTag;
67
+ this.safeBlockTag = chainInterface._config.safeBlockTag;
62
68
  this.logger = getLogger("EVMPersistentSigner("+address+"): ");
63
69
  }
64
70
 
@@ -247,6 +253,9 @@ export class EVMPersistentSigner extends EVMSigner {
247
253
  }
248
254
  }
249
255
 
256
+ /**
257
+ * @inheritDoc
258
+ */
250
259
  async init(): Promise<void> {
251
260
  try {
252
261
  await fs.mkdir(this.directory)
@@ -261,6 +270,9 @@ export class EVMPersistentSigner extends EVMSigner {
261
270
  this.startFeeBumper();
262
271
  }
263
272
 
273
+ /**
274
+ * @inheritDoc
275
+ */
264
276
  stop(): Promise<void> {
265
277
  this.stopped = true;
266
278
  if(this.feeBumper!=null) {
@@ -2,16 +2,46 @@ import {AbstractSigner} from "@atomiqlabs/base";
2
2
  import {getAddress, Signer, Transaction, TransactionRequest, TransactionResponse} from "ethers";
3
3
 
4
4
  /**
5
- * EVM signer implementation wrapping an ethers Signer
5
+ * EVM signer implementation wrapping an ethers {@link Signer}, for browser-based wallet use
6
+ * {@link EVMBrowserSigner}.
7
+ *
6
8
  * @category Wallets
7
9
  */
8
10
  export class EVMSigner implements AbstractSigner {
11
+ /**
12
+ * A static message, which should be signed by the EVM wallets to generate reproducible entropy. Works when
13
+ * wallets use signing with deterministic nonce, such that signature over the same message always yields the
14
+ * same signature (same entropy).
15
+ */
16
+ private static readonly EVM_REPRODUCIBLE_ENTROPY_MESSAGE =
17
+ "Signing this messages generates a reproducible secret to be used on %APPNAME%.\n\nPLEASE DOUBLE CHECK THAT YOU"+
18
+ " ARE ON THE %APPNAME% WEBSITE BEFORE SIGNING THE MESSAGE, SIGNING THIS MESSAGE ON ANY OTHER WEBSITE MIGHT LEAD TO"+
19
+ " LOSS OF FUNDS!";
20
+
21
+ /**
22
+ * Returns a static message, which should be signed by the EVM wallets to generate reproducible entropy. Works when
23
+ * wallets use signing with deterministic nonce, such that signature over the same message always yields the
24
+ * same signature (same entropy).
25
+ *
26
+ * @param appName Application name to differentiate reproducible entropy generated across different apps
27
+ */
28
+ public static getReproducibleEntropyMessage(appName: string): string {
29
+ return EVMSigner.EVM_REPRODUCIBLE_ENTROPY_MESSAGE.replace(new RegExp("%APPNAME%", 'g'), appName);
30
+ }
31
+
9
32
  type = "AtomiqAbstractSigner" as const;
10
33
 
11
34
  account: Signer;
12
35
  public readonly address: string;
13
36
  public readonly isManagingNoncesInternally: boolean;
14
37
 
38
+ /**
39
+ * Constructs a signer wrapping an ethers {@link Signer}.
40
+ *
41
+ * @param account
42
+ * @param address
43
+ * @param isManagingNoncesInternally
44
+ */
15
45
  constructor(account: Signer, address: string, isManagingNoncesInternally: boolean = false) {
16
46
  this.account = account;
17
47
  this.address = address;
@@ -25,10 +55,16 @@ export class EVMSigner implements AbstractSigner {
25
55
  return getAddress(this.address);
26
56
  }
27
57
 
58
+ /**
59
+ * @inheritDoc
60
+ */
28
61
  async signTransaction?(transaction: TransactionRequest): Promise<string> {
29
62
  return this.account.signTransaction(transaction);
30
63
  }
31
64
 
65
+ /**
66
+ * @inheritDoc
67
+ */
32
68
  async sendTransaction(transaction: TransactionRequest, onBeforePublish?: (txId: string, rawTx: string) => Promise<void>): Promise<TransactionResponse> {
33
69
  const txResponse = await this.account.sendTransaction(transaction);
34
70
  if(onBeforePublish!=null) await onBeforePublish(txResponse.hash, Transaction.from({
package/src/index.ts CHANGED
@@ -1,3 +1,74 @@
1
+ /**
2
+ * # @atomiqlabs/chain-evm
3
+ *
4
+ * `@atomiqlabs/chain-evm` is the EVM integration package for the Atomiq protocol.
5
+ *
6
+ * Within the Atomiq stack, this library provides the EVM-side building blocks used for Bitcoin-aware swaps and SPV-backed vault flows on supported EVM-compatible chains. It includes:
7
+ *
8
+ * - chain initializers for Atomiq-supported EVM networks
9
+ * - the `EVMChainInterface` used to talk to chain RPCs
10
+ * - BTC relay, escrow swap, and SPV vault contract wrappers
11
+ * - browser and server-side EVM signer helpers
12
+ * - event utilities for tracking swap and vault activity
13
+ *
14
+ * This package is intended for direct protocol integrations and for higher-level Atomiq SDK layers that need EVM chain support.
15
+ *
16
+ * ## Installation
17
+ *
18
+ * Install the package with its `ethers` peer dependency:
19
+ *
20
+ * ```bash
21
+ * npm install @atomiqlabs/chain-evm ethers
22
+ * ```
23
+ *
24
+ * ## Supported Chains
25
+ *
26
+ * The package currently exports chain initializers for:
27
+ *
28
+ * - Botanix via `BotanixInitializer`
29
+ * - Citrea via `CitreaInitializer`
30
+ * - Alpen via `AlpenInitializer`
31
+ * - GOAT Network via `GoatInitializer`
32
+ *
33
+ * Canonical deployments currently defined in this package:
34
+ *
35
+ * | Chain | Canonical deployments included |
36
+ * | --- | --- |
37
+ * | Botanix | `MAINNET`, `TESTNET` |
38
+ * | Citrea | `MAINNET`, `TESTNET4` |
39
+ * | Alpen | `TESTNET`, `TESTNET4` |
40
+ * | GOAT Network | `TESTNET`, `TESTNET4` |
41
+ *
42
+ * For Alpen and GOAT Network, `MAINNET` chain types exist in the API, but default mainnet contract addresses are not populated in this package yet. In those cases, pass explicit contract addresses if you want to use non-canonical deployments.
43
+ *
44
+ * ## SDK Example
45
+ *
46
+ * Initialize the atomiq SDK with Citrea network support:
47
+ *
48
+ * ```ts
49
+ * import {CitreaInitializer, CitreaInitializerType} from "@atomiqlabs/chain-evm";
50
+ * import {BitcoinNetwork, SwapperFactory, TypedSwapper} from "@atomiqlabs/sdk";
51
+ *
52
+ * //Define chains that you want to support here
53
+ * const chains = [CitreaInitializer] as const;
54
+ * type SupportedChains = typeof chains; //It's helpful that we also get the type of the chains array
55
+ *
56
+ * const Factory = new SwapperFactory<SupportedChains>(chains); //Create swapper factory
57
+ *
58
+ * const swapper: TypedSwapper<SupportedChains> = Factory.newSwapper({
59
+ * chains: {
60
+ * CITREA: {
61
+ * rpcUrl: citreaRpc, //You can also pass JsonApiProvider object here
62
+ * }
63
+ * },
64
+ * bitcoinNetwork: BitcoinNetwork.MAINNET //or BitcoinNetwork.TESTNET3, BitcoinNetwork.TESTNET4 - this also sets the deployment to use for EVM chains
65
+ * });
66
+ * ```
67
+ *
68
+ * @packageDocumentation
69
+ */
70
+ export * from "./chains/EVMOptions";
71
+
1
72
  export * from "./chains/citrea/CitreaInitializer";
2
73
  export * from "./chains/citrea/CitreaChainType";
3
74
  export * from "./chains/citrea/CitreaFees";
@@ -17,6 +88,7 @@ export {EVMBtcRelay} from "./evm/btcrelay/EVMBtcRelay";
17
88
 
18
89
  export * from "./evm/chain/EVMChainInterface";
19
90
  export * from "./evm/chain/modules/EVMFees";
91
+ export {EVMTx, SignedEVMTx} from "./evm/chain/modules/EVMTransactions";
20
92
 
21
93
  export * from "./evm/events/EVMChainEventsBrowser";
22
94
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Node.js-only entrypoint for filesystem-backed EVM helpers.
3
+ *
4
+ * Import from `@atomiqlabs/chain-evm/node` when you need runtime features
5
+ * that depend on Node's `fs` module.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export {EVMChainEvents} from "../evm/events/EVMChainEvents";
10
+ export {EVMPersistentSigner} from "../evm/wallet/EVMPersistentSigner";
@@ -1,5 +1,9 @@
1
1
 
2
-
2
+ /**
3
+ * Logger interface used across EVM modules.
4
+ *
5
+ * @category Internal/Utils
6
+ */
3
7
  export type LoggerType = {
4
8
  debug: (msg: string, ...args: any[]) => void,
5
9
  info: (msg: string, ...args: any[]) => void,
@@ -7,6 +11,11 @@ export type LoggerType = {
7
11
  error: (msg: string, ...args: any[]) => void,
8
12
  }
9
13
 
14
+ /**
15
+ * Returns a promise that resolves after `timeoutMillis` unless aborted first.
16
+ *
17
+ * @category Internal/Utils
18
+ */
10
19
  export function timeoutPromise(timeoutMillis: number, abortSignal?: AbortSignal): Promise<void> {
11
20
  return new Promise((resolve, reject) => {
12
21
  const timeout = setTimeout(resolve, timeoutMillis)
@@ -17,6 +26,11 @@ export function timeoutPromise(timeoutMillis: number, abortSignal?: AbortSignal)
17
26
  });
18
27
  }
19
28
 
29
+ /**
30
+ * Wraps an async executor so it runs once at a time and reuses the same promise.
31
+ *
32
+ * @category Internal/Utils
33
+ */
20
34
  export function onceAsync<T>(executor: () => Promise<T>): () => Promise<T> {
21
35
  let promise: Promise<T>;
22
36
 
@@ -30,6 +44,11 @@ export function onceAsync<T>(executor: () => Promise<T>): () => Promise<T> {
30
44
  }
31
45
  }
32
46
 
47
+ /**
48
+ * Creates a prefixed logger that honors the global `atomiqLogLevel`.
49
+ *
50
+ * @category Internal/Utils
51
+ */
33
52
  export function getLogger(prefix: string): LoggerType {
34
53
  return {
35
54
  debug: (msg, ...args) => (global as any).atomiqLogLevel >= 3 && console.debug(prefix+msg, ...args),
@@ -41,6 +60,11 @@ export function getLogger(prefix: string): LoggerType {
41
60
 
42
61
  const logger = getLogger("Utils: ");
43
62
 
63
+ /**
64
+ * Retries an async function using the provided retry policy.
65
+ *
66
+ * @category Internal/Utils
67
+ */
44
68
  export async function tryWithRetries<T>(func: () => Promise<T>, retryPolicy?: {
45
69
  maxRetries?: number, delay?: number, exponential?: boolean
46
70
  }, errorAllowed?: (e: any) => boolean, abortSignal?: AbortSignal): Promise<T> {
@@ -72,6 +96,11 @@ export async function tryWithRetries<T>(func: () => Promise<T>, retryPolicy?: {
72
96
  throw err;
73
97
  }
74
98
 
99
+ /**
100
+ * Reverses byte order of a 32-bit unsigned integer.
101
+ *
102
+ * @category Internal/Utils
103
+ */
75
104
  export function uint32ReverseEndianness(value: number): number {
76
105
  const valueBN = BigInt(value);
77
106
  return Number(((valueBN & 0xFFn) << 24n) |
@@ -80,10 +109,20 @@ export function uint32ReverseEndianness(value: number): number {
80
109
  ((valueBN >> 24n) & 0xFFn));
81
110
  }
82
111
 
112
+ /**
113
+ * Returns the greater of two bigint values.
114
+ *
115
+ * @category Internal/Utils
116
+ */
83
117
  export function bigIntMax(a: bigint, b: bigint) {
84
118
  return a>b ? a : b;
85
119
  }
86
120
 
121
+ /**
122
+ * Ethers.js error codes considered recoverable for retry logic.
123
+ *
124
+ * @category Internal/Utils
125
+ */
87
126
  export const allowedEthersErrorCodes: Set<string> = new Set([
88
127
  "NOT_IMPLEMENTED", "UNSUPPORTED_OPERATION", "BAD_DATA",
89
128
  "NUMERIC_FAULT",
@@ -91,6 +130,11 @@ export const allowedEthersErrorCodes: Set<string> = new Set([
91
130
  "CALL_EXCEPTION", "NONCE_EXPIRED", "REPLACEMENT_UNDERPRICED", "TRANSACTION_REPLACED", "UNCONFIGURED_NAME", "OFFCHAIN_FAULT", "ACTION_REJECTED"
92
131
  ]);
93
132
 
133
+ /**
134
+ * JSON-RPC error numbers considered recoverable for retry logic.
135
+ *
136
+ * @category Internal/Utils
137
+ */
94
138
  export const allowedEthersErrorNumbers: Set<number> = new Set([
95
139
  3, //Revertion during eth_getAccessList call
96
140
  -32700, //Invalid JSON
@@ -107,6 +151,11 @@ export const allowedEthersErrorNumbers: Set<number> = new Set([
107
151
  -32006 //JSON-RPC version not supported
108
152
  ]);
109
153
 
154
+ /**
155
+ * RPC error messages considered recoverable for retry logic.
156
+ *
157
+ * @category Internal/Utils
158
+ */
110
159
  export const allowedEthersErrorMessages: Set<string> = new Set([
111
160
  "already known"
112
161
  ]);