@atomiqlabs/chain-evm 1.0.0-dev.22 → 1.0.0-dev.27

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 (49) hide show
  1. package/dist/chains/citrea/CitreaBtcRelay.d.ts +21 -0
  2. package/dist/chains/citrea/CitreaBtcRelay.js +43 -0
  3. package/dist/chains/citrea/CitreaChainType.d.ts +4 -4
  4. package/dist/chains/citrea/CitreaFees.d.ts +29 -0
  5. package/dist/chains/citrea/CitreaFees.js +67 -0
  6. package/dist/chains/citrea/CitreaInitializer.d.ts +3 -3
  7. package/dist/chains/citrea/CitreaInitializer.js +13 -6
  8. package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +6 -0
  9. package/dist/chains/citrea/CitreaSpvVaultContract.js +16 -0
  10. package/dist/chains/citrea/CitreaSwapContract.d.ts +22 -0
  11. package/dist/chains/citrea/CitreaSwapContract.js +98 -0
  12. package/dist/chains/citrea/CitreaTokens.d.ts +9 -0
  13. package/dist/chains/citrea/CitreaTokens.js +20 -0
  14. package/dist/evm/btcrelay/EVMBtcRelay.d.ts +8 -1
  15. package/dist/evm/btcrelay/EVMBtcRelay.js +15 -11
  16. package/dist/evm/chain/EVMChainInterface.d.ts +6 -6
  17. package/dist/evm/chain/modules/EVMFees.d.ts +8 -7
  18. package/dist/evm/chain/modules/EVMFees.js +3 -3
  19. package/dist/evm/chain/modules/EVMTokens.d.ts +2 -0
  20. package/dist/evm/chain/modules/EVMTokens.js +10 -2
  21. package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +6 -1
  22. package/dist/evm/spv_swap/EVMSpvVaultContract.js +4 -4
  23. package/dist/evm/swaps/modules/EVMLpVault.js +2 -2
  24. package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +1 -0
  25. package/dist/evm/swaps/modules/EVMSwapClaim.js +40 -4
  26. package/dist/evm/swaps/modules/EVMSwapInit.d.ts +3 -3
  27. package/dist/evm/swaps/modules/EVMSwapInit.js +43 -9
  28. package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +2 -2
  29. package/dist/evm/swaps/modules/EVMSwapRefund.js +42 -7
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +1 -0
  32. package/package.json +1 -1
  33. package/src/chains/citrea/CitreaBtcRelay.ts +58 -0
  34. package/src/chains/citrea/CitreaChainType.ts +6 -6
  35. package/src/chains/citrea/CitreaFees.ts +77 -0
  36. package/src/chains/citrea/CitreaInitializer.ts +15 -5
  37. package/src/chains/citrea/CitreaSpvVaultContract.ts +18 -0
  38. package/src/chains/citrea/CitreaSwapContract.ts +105 -0
  39. package/src/chains/citrea/CitreaTokens.ts +22 -0
  40. package/src/evm/btcrelay/EVMBtcRelay.ts +17 -12
  41. package/src/evm/chain/EVMChainInterface.ts +6 -6
  42. package/src/evm/chain/modules/EVMFees.ts +10 -11
  43. package/src/evm/chain/modules/EVMTokens.ts +13 -2
  44. package/src/evm/spv_swap/EVMSpvVaultContract.ts +5 -5
  45. package/src/evm/swaps/modules/EVMLpVault.ts +2 -2
  46. package/src/evm/swaps/modules/EVMSwapClaim.ts +36 -4
  47. package/src/evm/swaps/modules/EVMSwapInit.ts +44 -10
  48. package/src/evm/swaps/modules/EVMSwapRefund.ts +38 -7
  49. package/src/index.ts +1 -0
@@ -15,7 +15,12 @@ import { EVMBtcStoredHeader } from "../btcrelay/headers/EVMBtcStoredHeader";
15
15
  export declare function packOwnerAndVaultId(owner: string, vaultId: bigint): string;
16
16
  export declare function unpackOwnerAndVaultId(data: string): [string, bigint];
17
17
  export declare class EVMSpvVaultContract<ChainId extends string> extends EVMContractBase<SpvVaultManager> implements SpvVaultContract<EVMTx, EVMSigner, ChainId, EVMSpvVaultData, EVMSpvWithdrawalData> {
18
- private static readonly GasCosts;
18
+ static readonly GasCosts: {
19
+ DEPOSIT: number;
20
+ OPEN: number;
21
+ FRONT: number;
22
+ CLAIM: number;
23
+ };
19
24
  readonly chainId: ChainId;
20
25
  readonly btcRelay: EVMBtcRelay<any>;
21
26
  readonly bitcoinRpc: BitcoinRpc<any>;
@@ -403,8 +403,8 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
403
403
  }
404
404
  exports.EVMSpvVaultContract = EVMSpvVaultContract;
405
405
  EVMSpvVaultContract.GasCosts = {
406
- DEPOSIT: 150000,
407
- OPEN: 100000,
408
- FRONT: 250000,
409
- CLAIM: 250000
406
+ DEPOSIT: 150000 + 21000,
407
+ OPEN: 100000 + 21000,
408
+ FRONT: 250000 + 21000,
409
+ CLAIM: 250000 + 21000
410
410
  };
@@ -126,6 +126,6 @@ class EVMLpVault extends EVMSwapModule_1.EVMSwapModule {
126
126
  }
127
127
  exports.EVMLpVault = EVMLpVault;
128
128
  EVMLpVault.GasCosts = {
129
- WITHDRAW: 100000,
130
- DEPOSIT: 100000
129
+ WITHDRAW: 100000 + 21000,
130
+ DEPOSIT: 100000 + 21000
131
131
  };
@@ -46,6 +46,7 @@ export declare class EVMSwapClaim extends EVMSwapModule {
46
46
  hex: string;
47
47
  height: number;
48
48
  }, requiredConfirmations: number, vout: number, commitedHeader?: EVMBtcStoredHeader, synchronizer?: RelaySynchronizer<EVMBtcStoredHeader, EVMTx, any>, feeRate?: string): Promise<EVMTx[] | null>;
49
+ getClaimGas(swapData: EVMSwapData): number;
49
50
  /**
50
51
  * Get the estimated starknet transaction fee of the claim transaction
51
52
  */
@@ -19,7 +19,7 @@ class EVMSwapClaim extends EVMSwapModule_1.EVMSwapModule {
19
19
  //TODO: Claim with success action not supported yet!
20
20
  const tx = await this.swapContract.claim.populateTransaction(swapData.toEscrowStruct(), witness);
21
21
  tx.from = signer;
22
- EVMFees_1.EVMFees.applyFeeRate(tx, EVMSwapClaim.GasCosts.CLAIM + (claimHandlerGas ?? 0), feeRate);
22
+ EVMFees_1.EVMFees.applyFeeRate(tx, this.getClaimGas(swapData) + (claimHandlerGas ?? 0), feeRate);
23
23
  return tx;
24
24
  }
25
25
  /**
@@ -81,13 +81,46 @@ class EVMSwapClaim extends EVMSwapModule_1.EVMSwapModule {
81
81
  const claimTx = await this.Claim(signer, swapData, witness, feeRate, claimHandler.getGas(swapData));
82
82
  return [...initialTxns, claimTx];
83
83
  }
84
+ getClaimGas(swapData) {
85
+ let totalGas = EVMSwapClaim.GasCosts.BASE;
86
+ if (swapData.reputation)
87
+ totalGas += EVMSwapClaim.GasCosts.REPUTATION;
88
+ if (swapData.isPayOut()) {
89
+ if (swapData.isToken(this.root.getNativeCurrencyAddress())) {
90
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
91
+ }
92
+ else {
93
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
94
+ }
95
+ }
96
+ else {
97
+ totalGas += EVMSwapClaim.GasCosts.LP_VAULT_TRANSFER;
98
+ }
99
+ if (swapData.getClaimerBounty() > 0n) {
100
+ if (swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
101
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
102
+ }
103
+ else {
104
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
105
+ }
106
+ }
107
+ if (swapData.getSecurityDeposit() > swapData.getClaimerBounty()) {
108
+ if (swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
109
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
110
+ }
111
+ else {
112
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
113
+ }
114
+ }
115
+ return totalGas;
116
+ }
84
117
  /**
85
118
  * Get the estimated starknet transaction fee of the claim transaction
86
119
  */
87
120
  async getClaimFee(swapData, feeRate) {
88
121
  feeRate ?? (feeRate = await this.root.Fees.getFeeRate());
89
122
  //TODO: Claim with success action not supported yet!
90
- let gasRequired = EVMSwapClaim.GasCosts.CLAIM;
123
+ let gasRequired = this.getClaimGas(swapData);
91
124
  const claimHandler = this.contract.claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
92
125
  if (claimHandler != null)
93
126
  gasRequired += claimHandler.getGas(swapData);
@@ -96,6 +129,9 @@ class EVMSwapClaim extends EVMSwapModule_1.EVMSwapModule {
96
129
  }
97
130
  exports.EVMSwapClaim = EVMSwapClaim;
98
131
  EVMSwapClaim.GasCosts = {
99
- CLAIM: 120000,
100
- CLAIM_WITH_SUCCESS_ACTION: 150000
132
+ BASE: 30000 + 21000,
133
+ ERC20_TRANSFER: 40000,
134
+ NATIVE_TRANSFER: 7500,
135
+ LP_VAULT_TRANSFER: 10000,
136
+ REPUTATION: 25000
101
137
  };
@@ -80,9 +80,9 @@ export declare class EVMSwapInit extends EVMSwapModule {
80
80
  * @param feeRate fee rate to use for the transaction
81
81
  */
82
82
  txsInit(sender: string, swapData: EVMSwapData, timeout: string, prefix: string, signature: string, skipChecks?: boolean, feeRate?: string): Promise<EVMTx[]>;
83
+ private getInitGas;
83
84
  /**
84
- * Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
85
- * and also deposit for ATAs
85
+ * Get the estimated fee of the init transaction
86
86
  */
87
- getInitFee(swapData?: EVMSwapData, feeRate?: string): Promise<bigint>;
87
+ getInitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
88
88
  }
@@ -39,15 +39,17 @@ class EVMSwapInit extends EVMSwapModule_1.EVMSwapModule {
39
39
  */
40
40
  async Init(sender, swapData, timeout, signature, feeRate) {
41
41
  let value = 0n;
42
- if (swapData.isToken(this.root.getNativeCurrencyAddress()))
43
- value += swapData.getAmount();
42
+ if (swapData.isPayIn()) {
43
+ if (swapData.isOfferer(sender) && swapData.isToken(this.root.getNativeCurrencyAddress()))
44
+ value += swapData.getAmount();
45
+ }
44
46
  if (swapData.isDepositToken(this.root.getNativeCurrencyAddress()))
45
47
  value += swapData.getTotalDeposit();
46
48
  const tx = await this.swapContract.initialize.populateTransaction(swapData.toEscrowStruct(), signature, timeout, "0x" + (swapData.extraData ?? ""), {
47
49
  value
48
50
  });
49
51
  tx.from = sender;
50
- EVMFees_1.EVMFees.applyFeeRate(tx, swapData.isPayIn() ? EVMSwapInit.GasCosts.INIT_PAY_IN : EVMSwapInit.GasCosts.INIT, feeRate);
52
+ EVMFees_1.EVMFees.applyFeeRate(tx, this.getInitGas(swapData), feeRate);
51
53
  return tx;
52
54
  }
53
55
  /**
@@ -206,7 +208,7 @@ class EVMSwapInit extends EVMSwapModule_1.EVMSwapModule {
206
208
  feeRate ?? (feeRate = await this.root.Fees.getFeeRate());
207
209
  const txs = [];
208
210
  const requiredApprovals = {};
209
- if (swapData.payIn && swapData.isOfferer(sender)) {
211
+ if (swapData.isPayIn() && swapData.isOfferer(sender)) {
210
212
  if (!swapData.isToken(this.root.getNativeCurrencyAddress())) {
211
213
  requiredApprovals[swapData.token.toLowerCase()] = swapData.amount;
212
214
  }
@@ -225,17 +227,49 @@ class EVMSwapInit extends EVMSwapModule_1.EVMSwapModule {
225
227
  " feerate: " + feeRate);
226
228
  return txs;
227
229
  }
230
+ getInitGas(swapData) {
231
+ let totalGas = EVMSwapInit.GasCosts.BASE;
232
+ if (swapData.isPayIn()) {
233
+ if (!swapData.isToken(this.root.getNativeCurrencyAddress())) {
234
+ totalGas += EVMSwapInit.GasCosts.ERC20_TRANSFER;
235
+ }
236
+ }
237
+ else {
238
+ totalGas += EVMSwapInit.GasCosts.LP_VAULT_TRANSFER;
239
+ }
240
+ if (swapData.getTotalDeposit() > 0) {
241
+ if (!swapData.isPayIn() || !swapData.isDepositToken(swapData.token)) {
242
+ if (!swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
243
+ totalGas += EVMSwapInit.GasCosts.ERC20_TRANSFER;
244
+ }
245
+ }
246
+ }
247
+ return totalGas;
248
+ }
228
249
  /**
229
- * Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
230
- * and also deposit for ATAs
250
+ * Get the estimated fee of the init transaction
231
251
  */
232
252
  async getInitFee(swapData, feeRate) {
233
253
  feeRate ?? (feeRate = await this.root.Fees.getFeeRate());
234
- return EVMFees_1.EVMFees.getGasFee(swapData.payIn ? EVMSwapInit.GasCosts.INIT_PAY_IN : EVMSwapInit.GasCosts.INIT, feeRate);
254
+ let totalFee = EVMFees_1.EVMFees.getGasFee(this.getInitGas(swapData), feeRate);
255
+ if (swapData.isPayIn()) {
256
+ if (!swapData.isToken(this.root.getNativeCurrencyAddress())) {
257
+ totalFee += await this.root.Tokens.getApproveFee(feeRate);
258
+ }
259
+ }
260
+ if (swapData.getTotalDeposit() > 0) {
261
+ if (!swapData.isPayIn() || !swapData.isDepositToken(swapData.token)) {
262
+ if (!swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
263
+ totalFee += await this.root.Tokens.getApproveFee(feeRate);
264
+ }
265
+ }
266
+ }
267
+ return totalFee;
235
268
  }
236
269
  }
237
270
  exports.EVMSwapInit = EVMSwapInit;
238
271
  EVMSwapInit.GasCosts = {
239
- INIT: 100000,
240
- INIT_PAY_IN: 130000,
272
+ BASE: 45000 + 21000,
273
+ ERC20_TRANSFER: 40000,
274
+ LP_VAULT_TRANSFER: 10000
241
275
  };
@@ -54,9 +54,9 @@ export declare class EVMSwapRefund extends EVMSwapModule {
54
54
  * @param feeRate fee rate to be used for the transactions
55
55
  */
56
56
  txsRefundWithAuthorization(signer: string, swapData: EVMSwapData, timeout: string, prefix: string, signature: string, check?: boolean, feeRate?: string): Promise<EVMTx[]>;
57
+ getRefundGas(swapData: EVMSwapData): number;
57
58
  /**
58
- * Get the estimated solana transaction fee of the refund transaction, in the worst case scenario in case where the
59
- * ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
59
+ * Get the estimated transaction fee of the refund transaction
60
60
  */
61
61
  getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
62
62
  }
@@ -23,7 +23,7 @@ class EVMSwapRefund extends EVMSwapModule_1.EVMSwapModule {
23
23
  async Refund(signer, swapData, witness, feeRate, handlerGas) {
24
24
  const tx = await this.swapContract.refund.populateTransaction(swapData.toEscrowStruct(), witness);
25
25
  tx.from = signer;
26
- EVMFees_1.EVMFees.applyFeeRate(tx, (swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND) + (handlerGas ?? 0), feeRate);
26
+ EVMFees_1.EVMFees.applyFeeRate(tx, this.getRefundGas(swapData) + (handlerGas ?? 0), feeRate);
27
27
  return tx;
28
28
  }
29
29
  /**
@@ -39,7 +39,7 @@ class EVMSwapRefund extends EVMSwapModule_1.EVMSwapModule {
39
39
  async RefundWithSignature(sender, swapData, timeout, signature, feeRate) {
40
40
  const tx = await this.swapContract.cooperativeRefund.populateTransaction(swapData.toEscrowStruct(), signature, BigInt(timeout));
41
41
  tx.from = sender;
42
- EVMFees_1.EVMFees.applyFeeRate(tx, swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND, feeRate);
42
+ EVMFees_1.EVMFees.applyFeeRate(tx, this.getRefundGas(swapData), feeRate);
43
43
  return tx;
44
44
  }
45
45
  async signSwapRefund(signer, swapData, authorizationTimeout) {
@@ -116,17 +116,52 @@ class EVMSwapRefund extends EVMSwapModule_1.EVMSwapModule {
116
116
  " auth expiry: " + timeout + " signature: " + signature);
117
117
  return [tx];
118
118
  }
119
+ getRefundGas(swapData) {
120
+ let totalGas = EVMSwapRefund.GasCosts.BASE;
121
+ if (swapData.reputation)
122
+ totalGas += EVMSwapRefund.GasCosts.REPUTATION;
123
+ if (swapData.isPayIn()) {
124
+ if (swapData.isToken(this.root.getNativeCurrencyAddress())) {
125
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
126
+ }
127
+ else {
128
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
129
+ }
130
+ }
131
+ else {
132
+ totalGas += EVMSwapRefund.GasCosts.LP_VAULT_TRANSFER;
133
+ }
134
+ if (swapData.getSecurityDeposit() > 0n) {
135
+ if (swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
136
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
137
+ }
138
+ else {
139
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
140
+ }
141
+ }
142
+ if (swapData.getClaimerBounty() > swapData.getSecurityDeposit()) {
143
+ if (swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
144
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
145
+ }
146
+ else {
147
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
148
+ }
149
+ }
150
+ return totalGas;
151
+ }
119
152
  /**
120
- * Get the estimated solana transaction fee of the refund transaction, in the worst case scenario in case where the
121
- * ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
153
+ * Get the estimated transaction fee of the refund transaction
122
154
  */
123
155
  async getRefundFee(swapData, feeRate) {
124
156
  feeRate ?? (feeRate = await this.root.Fees.getFeeRate());
125
- return EVMFees_1.EVMFees.getGasFee(swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND, feeRate);
157
+ return EVMFees_1.EVMFees.getGasFee(this.getRefundGas(swapData), feeRate);
126
158
  }
127
159
  }
128
160
  exports.EVMSwapRefund = EVMSwapRefund;
129
161
  EVMSwapRefund.GasCosts = {
130
- REFUND: 100000,
131
- REFUND_PAY_OUT: 130000
162
+ BASE: 35000 + 21000,
163
+ ERC20_TRANSFER: 40000,
164
+ NATIVE_TRANSFER: 7500,
165
+ LP_VAULT_TRANSFER: 10000,
166
+ REPUTATION: 25000
132
167
  };
package/dist/index.d.ts CHANGED
@@ -35,3 +35,4 @@ export * from "./evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler";
35
35
  export * from "./evm/wallet/EVMSigner";
36
36
  export * from "./chains/citrea/CitreaInitializer";
37
37
  export * from "./chains/citrea/CitreaChainType";
38
+ export * from "./chains/citrea/CitreaFees";
package/dist/index.js CHANGED
@@ -51,3 +51,4 @@ __exportStar(require("./evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHan
51
51
  __exportStar(require("./evm/wallet/EVMSigner"), exports);
52
52
  __exportStar(require("./chains/citrea/CitreaInitializer"), exports);
53
53
  __exportStar(require("./chains/citrea/CitreaChainType"), exports);
54
+ __exportStar(require("./chains/citrea/CitreaFees"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-evm",
3
- "version": "1.0.0-dev.22",
3
+ "version": "1.0.0-dev.27",
4
4
  "description": "EVM specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -0,0 +1,58 @@
1
+ import {EVMBtcRelay} from "../../evm/btcrelay/EVMBtcRelay";
2
+ import {BtcBlock} from "@atomiqlabs/base";
3
+ import {getLogger} from "../../utils/Utils";
4
+ import {CitreaFees} from "./CitreaFees";
5
+
6
+ const logger = getLogger("CitreaBtcRelay: ");
7
+
8
+ export class CitreaBtcRelay<B extends BtcBlock> extends EVMBtcRelay<B> {
9
+
10
+ public static StateDiffSize = {
11
+ STATE_DIFF_PER_BLOCKHEADER: 22,
12
+ STATE_DIFF_BASE: 30
13
+ }
14
+
15
+ /**
16
+ * Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
17
+ *
18
+ * @param requiredBlockheight
19
+ * @param feeRate
20
+ */
21
+ public async estimateSynchronizeFee(requiredBlockheight: number, feeRate?: string): Promise<bigint> {
22
+ feeRate ??= await this.Chain.Fees.getFeeRate();
23
+ const tipData = await this.getTipData();
24
+ const currBlockheight = tipData.blockheight;
25
+
26
+ const blockheightDelta = requiredBlockheight-currBlockheight;
27
+
28
+ if(blockheightDelta<=0) return 0n;
29
+
30
+ const numTxs = Math.ceil(blockheightDelta / this.maxHeadersPerTx);
31
+
32
+ const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
33
+ + CitreaFees.getGasFee(
34
+ EVMBtcRelay.GasCosts.GAS_BASE_MAIN * numTxs,
35
+ feeRate,
36
+ CitreaBtcRelay.StateDiffSize.STATE_DIFF_BASE * numTxs
37
+ );
38
+ logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
39
+ " blockheight delta: "+blockheightDelta+" fee: "+synchronizationFee.toString(10));
40
+
41
+ return synchronizationFee;
42
+ }
43
+
44
+ /**
45
+ * Returns fee required (in native token) to synchronize a single block to btc relay
46
+ *
47
+ * @param feeRate
48
+ */
49
+ public async getFeePerBlock(feeRate?: string): Promise<bigint> {
50
+ feeRate ??= await this.Chain.Fees.getFeeRate();
51
+ return CitreaFees.getGasFee(
52
+ EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER,
53
+ feeRate,
54
+ CitreaBtcRelay.StateDiffSize.STATE_DIFF_PER_BLOCKHEADER
55
+ );
56
+ }
57
+
58
+ }
@@ -3,13 +3,13 @@ import {EVMPreFetchVerification} from "../../evm/swaps/modules/EVMSwapInit";
3
3
  import {EVMTx} from "../../evm/chain/modules/EVMTransactions";
4
4
  import {EVMSigner} from "../../evm/wallet/EVMSigner";
5
5
  import {EVMSwapData} from "../../evm/swaps/EVMSwapData";
6
- import {EVMSwapContract} from "../../evm/swaps/EVMSwapContract";
7
6
  import {EVMChainInterface} from "../../evm/chain/EVMChainInterface";
8
7
  import {EVMChainEventsBrowser} from "../../evm/events/EVMChainEventsBrowser";
9
- import {EVMBtcRelay} from "../../evm/btcrelay/EVMBtcRelay";
10
8
  import { EVMSpvVaultData } from "../../evm/spv_swap/EVMSpvVaultData";
11
9
  import { EVMSpvWithdrawalData } from "../../evm/spv_swap/EVMSpvWithdrawalData";
12
- import {EVMSpvVaultContract} from "../../evm/spv_swap/EVMSpvVaultContract";
10
+ import {CitreaSwapContract} from "./CitreaSwapContract";
11
+ import {CitreaBtcRelay} from "./CitreaBtcRelay";
12
+ import {CitreaSpvVaultContract} from "./CitreaSpvVaultContract";
13
13
 
14
14
  export type CitreaChainType = ChainType<
15
15
  "CITREA",
@@ -18,11 +18,11 @@ export type CitreaChainType = ChainType<
18
18
  EVMTx,
19
19
  EVMSigner,
20
20
  EVMSwapData,
21
- EVMSwapContract<"CITREA">,
21
+ CitreaSwapContract,
22
22
  EVMChainInterface<"CITREA", 5115>,
23
23
  EVMChainEventsBrowser,
24
- EVMBtcRelay<any>,
24
+ CitreaBtcRelay<any>,
25
25
  EVMSpvVaultData,
26
26
  EVMSpvWithdrawalData,
27
- EVMSpvVaultContract<"CITREA">
27
+ CitreaSpvVaultContract
28
28
  >;
@@ -0,0 +1,77 @@
1
+ import {EVMFees} from "../../evm/chain/modules/EVMFees";
2
+ import {getLogger} from "../../utils/Utils";
3
+
4
+ export class CitreaFees extends EVMFees {
5
+
6
+ public static readonly StateDiffSize = {
7
+ APPROVE_DIFF_SIZE: 40,
8
+ };
9
+
10
+ protected readonly logger = getLogger("CitreaFees: ");
11
+
12
+ private _blockFeeCache: {
13
+ timestamp: number,
14
+ feeRate: Promise<{baseFee: bigint, l1Fee: bigint}>
15
+ } = null;
16
+
17
+ /**
18
+ * Gets evm fee rate
19
+ *
20
+ * @private
21
+ * @returns {Promise<bigint>} L1 gas price denominated in Wei
22
+ */
23
+ private async __getFeeRate(): Promise<{baseFee: bigint, l1Fee: bigint}> {
24
+ const res = await this.provider.send("eth_getBlockByNumber", ["latest", false]);
25
+ const l1Fee = BigInt(res.l1FeeRate);
26
+ const baseFee = BigInt(res.baseFeePerGas) * this.feeMultiplierPPM / 1_000_000n;
27
+
28
+ this.logger.debug("__getFeeRate(): Base fee rate: "+baseFee.toString(10)+", l1 fee rate: "+l1Fee.toString(10));
29
+
30
+ return {baseFee, l1Fee};
31
+ }
32
+
33
+ /**
34
+ * Gets the gas price with caching, format: <gas price in Wei>;<transaction version: v1/v3>
35
+ *
36
+ * @private
37
+ */
38
+ public async getFeeRate(): Promise<string> {
39
+ if(this._blockFeeCache==null || Date.now() - this._blockFeeCache.timestamp > this.MAX_FEE_AGE) {
40
+ let obj = {
41
+ timestamp: Date.now(),
42
+ feeRate: null
43
+ };
44
+ obj.feeRate = this.__getFeeRate().catch(e => {
45
+ if(this._blockFeeCache===obj) this._blockFeeCache=null;
46
+ throw e;
47
+ });
48
+ this._blockFeeCache = obj;
49
+ }
50
+
51
+ let {baseFee, l1Fee} = await this._blockFeeCache.feeRate;
52
+ if(baseFee>this.maxFeeRatePerGas) baseFee = this.maxFeeRatePerGas;
53
+
54
+ const fee = baseFee.toString(10)+","+this.priorityFee.toString(10)+","+l1Fee.toString(10);
55
+
56
+ this.logger.debug("getFeeRate(): calculated fee: "+fee);
57
+
58
+ return fee;
59
+ }
60
+
61
+
62
+ /**
63
+ * Calculates the total gas fee paid for a given gas limit and state diff size at a given fee rate
64
+ *
65
+ * @param gas
66
+ * @param stateDiffSize
67
+ * @param feeRate
68
+ */
69
+ public static getGasFee(gas: number, feeRate: string, stateDiffSize: number = 0): bigint {
70
+ if(feeRate==null) return 0n;
71
+
72
+ const [maxFee, priorityFee, l1StateDiffFee] = feeRate.split(",");
73
+
74
+ return (BigInt(gas) * BigInt(maxFee)) + (BigInt(stateDiffSize) * BigInt(l1StateDiffFee ?? 0n));
75
+ }
76
+
77
+ }
@@ -10,6 +10,10 @@ import {EVMChainEventsBrowser} from "../../evm/events/EVMChainEventsBrowser";
10
10
  import {EVMSwapData} from "../../evm/swaps/EVMSwapData";
11
11
  import {EVMSpvVaultData} from "../../evm/spv_swap/EVMSpvVaultData";
12
12
  import {EVMSpvWithdrawalData} from "../../evm/spv_swap/EVMSpvWithdrawalData";
13
+ import {CitreaFees} from "./CitreaFees";
14
+ import {CitreaBtcRelay} from "./CitreaBtcRelay";
15
+ import {CitreaSwapContract} from "./CitreaSwapContract";
16
+ import {CitreaTokens} from "./CitreaTokens";
13
17
 
14
18
  const CitreaChainIds = {
15
19
  MAINNET: null,
@@ -57,12 +61,17 @@ const CitreaContractAddresses = {
57
61
  }
58
62
  };
59
63
 
60
- export type CitreaAssetsType = BaseTokenType<"CBTC">;
64
+ export type CitreaAssetsType = BaseTokenType<"CBTC" | "USDC">;
61
65
  export const CitreaAssets: CitreaAssetsType = {
62
66
  CBTC: {
63
67
  address: "0x0000000000000000000000000000000000000000",
64
68
  decimals: 18,
65
69
  displayDecimals: 8
70
+ },
71
+ USDC: {
72
+ address: "0x2C8abD2A528D19AFc33d2ebA507c0F405c131335",
73
+ decimals: 6,
74
+ displayDecimals: 6
66
75
  }
67
76
  } as const;
68
77
 
@@ -86,7 +95,7 @@ export type CitreaOptions = {
86
95
  }
87
96
  }
88
97
 
89
- fees?: EVMFees
98
+ fees?: CitreaFees
90
99
  }
91
100
 
92
101
  export function initializeCitrea(
@@ -112,19 +121,20 @@ export function initializeCitrea(
112
121
  new JsonRpcProvider(options.rpcUrl, {name: "Citrea", chainId}) :
113
122
  options.rpcUrl;
114
123
 
115
- const Fees = options.fees ?? new EVMFees(provider, 2n * 1_000_000_000n, 1_000_000n);
124
+ const Fees = options.fees ?? new CitreaFees(provider, 2n * 1_000_000_000n, 1_000_000n);
116
125
 
117
126
  const chainInterface = new EVMChainInterface("CITREA", chainId, provider, {
118
127
  safeBlockTag: "latest",
119
128
  maxLogsBlockRange: options.maxLogsBlockRange ?? 500
120
129
  }, options.retryPolicy, Fees);
130
+ chainInterface.Tokens = new CitreaTokens(chainInterface); //Override with custom token module allowing l1 state diff based fee calculation
121
131
 
122
- const btcRelay = new EVMBtcRelay(
132
+ const btcRelay = new CitreaBtcRelay(
123
133
  chainInterface, bitcoinRpc, network, options.btcRelayContract ?? defaultContractAddresses.btcRelayContract,
124
134
  options.btcRelayDeploymentHeight ?? defaultContractAddresses.btcRelayDeploymentHeight
125
135
  );
126
136
 
127
- const swapContract = new EVMSwapContract(
137
+ const swapContract = new CitreaSwapContract(
128
138
  chainInterface, btcRelay, options.swapContract ?? defaultContractAddresses.swapContract, {
129
139
  refund: {
130
140
  ...defaultContractAddresses.handlerContracts.refund,
@@ -0,0 +1,18 @@
1
+ import {EVMSpvVaultContract} from "../../evm/spv_swap/EVMSpvVaultContract";
2
+ import {EVMSpvWithdrawalData} from "../../evm/spv_swap/EVMSpvWithdrawalData";
3
+ import {EVMFees} from "../../evm/chain/modules/EVMFees";
4
+
5
+
6
+ export class CitreaSpvVaultContract extends EVMSpvVaultContract<"CITREA"> {
7
+
8
+ async getClaimFee(signer: string, withdrawalData: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
9
+ feeRate ??= await this.Chain.Fees.getFeeRate();
10
+ return EVMFees.getGasFee(EVMSpvVaultContract.GasCosts.CLAIM, feeRate);
11
+ }
12
+
13
+ async getFrontFee(signer: string, withdrawalData: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
14
+ feeRate ??= await this.Chain.Fees.getFeeRate();
15
+ return EVMFees.getGasFee(EVMSpvVaultContract.GasCosts.FRONT, feeRate);
16
+ }
17
+
18
+ }