@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
@@ -0,0 +1,105 @@
1
+ import {EVMSwapContract} from "../../evm/swaps/EVMSwapContract";
2
+ import {EVMSwapData} from "../../evm/swaps/EVMSwapData";
3
+ import {CitreaFees} from "./CitreaFees";
4
+
5
+ export class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
6
+
7
+ public static readonly StateDiffSize = {
8
+ BASE_DIFF_SIZE: 35,
9
+ REPUTATION_UPDATE_DIFF_SIZE: 25,
10
+ LP_VAULT_UPDATE_DIFF_SIZE: 25,
11
+ ERC_20_TRANSFER_DIFF_SIZE: 50,
12
+ NATIVE_SELF_TRANSFER_DIFF_SIZE: 20,
13
+ NATIVE_TRANSFER_DIFF_SIZE: 30
14
+ };
15
+
16
+ private calculateStateDiff(signer: string, tokenStateChanges: Set<string>): number {
17
+ let stateDiffSize = 0;
18
+ tokenStateChanges.forEach(val => {
19
+ const [address, token] = val.split(":");
20
+ if(token.toLowerCase()===this.Chain.getNativeCurrencyAddress().toLowerCase()) {
21
+ stateDiffSize += address.toLowerCase()===signer?.toLowerCase() ? CitreaSwapContract.StateDiffSize.NATIVE_SELF_TRANSFER_DIFF_SIZE : CitreaSwapContract.StateDiffSize.NATIVE_TRANSFER_DIFF_SIZE;
22
+ } else {
23
+ stateDiffSize += CitreaSwapContract.StateDiffSize.ERC_20_TRANSFER_DIFF_SIZE;
24
+ }
25
+ });
26
+ return stateDiffSize;
27
+ }
28
+
29
+ /**
30
+ * Get the estimated solana fee of the commit transaction
31
+ */
32
+ async getCommitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
33
+ feeRate ??= await this.Chain.Fees.getFeeRate();
34
+
35
+ const tokenStateChanges: Set<string> = new Set();
36
+
37
+ let diffSize = CitreaSwapContract.StateDiffSize.BASE_DIFF_SIZE;
38
+ if(!swapData.isPayIn()) {
39
+ diffSize += CitreaSwapContract.StateDiffSize.LP_VAULT_UPDATE_DIFF_SIZE;
40
+ } else {
41
+ tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getToken().toLowerCase());
42
+ }
43
+ //TODO: Since we don't know who is sending the transaction, we calculate with the worst case
44
+ if(swapData.getTotalDeposit()>0n) {
45
+ tokenStateChanges.add(swapData.getClaimer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
46
+ }
47
+ diffSize += this.calculateStateDiff(null, tokenStateChanges);
48
+
49
+ const gasFee = await this.Init.getInitFee(swapData, feeRate);
50
+ return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
51
+ }
52
+
53
+ async getClaimFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
54
+ feeRate ??= await this.Chain.Fees.getFeeRate();
55
+
56
+ const tokenStateChanges: Set<string> = new Set();
57
+
58
+ let diffSize = CitreaSwapContract.StateDiffSize.BASE_DIFF_SIZE;
59
+ if(swapData.reputation) diffSize += CitreaSwapContract.StateDiffSize.REPUTATION_UPDATE_DIFF_SIZE;
60
+ if(!swapData.isPayOut()) {
61
+ diffSize += CitreaSwapContract.StateDiffSize.LP_VAULT_UPDATE_DIFF_SIZE;
62
+ } else {
63
+ tokenStateChanges.add(swapData.getClaimer().toLowerCase()+":"+swapData.getToken().toLowerCase());
64
+ }
65
+ if(swapData.getClaimerBounty() > 0) {
66
+ tokenStateChanges.add(signer.toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
67
+ }
68
+ if(swapData.getSecurityDeposit() > swapData.getClaimerBounty()) {
69
+ tokenStateChanges.add(swapData.getClaimer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
70
+ }
71
+ diffSize += this.calculateStateDiff(signer, tokenStateChanges);
72
+
73
+ const gasFee = await this.Claim.getClaimFee(swapData, feeRate);
74
+ return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
75
+ }
76
+
77
+ /**
78
+ * Get the estimated solana transaction fee of the refund transaction
79
+ */
80
+ async getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
81
+ feeRate ??= await this.Chain.Fees.getFeeRate();
82
+
83
+ const tokenStateChanges: Set<string> = new Set();
84
+
85
+ let diffSize = CitreaSwapContract.StateDiffSize.BASE_DIFF_SIZE;
86
+ if(swapData.reputation) diffSize += CitreaSwapContract.StateDiffSize.REPUTATION_UPDATE_DIFF_SIZE;
87
+ if(!swapData.isPayIn()) {
88
+ diffSize += CitreaSwapContract.StateDiffSize.LP_VAULT_UPDATE_DIFF_SIZE;
89
+ } else {
90
+ tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getToken().toLowerCase());
91
+ }
92
+ //TODO: Since we don't know if the refund is cooperative or not and also not the signer, we calculate with the worst case
93
+ if(swapData.getSecurityDeposit() > 0) {
94
+ tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
95
+ }
96
+ if(swapData.getClaimerBounty() > swapData.getSecurityDeposit()) {
97
+ tokenStateChanges.add(swapData.getClaimer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
98
+ }
99
+ diffSize += this.calculateStateDiff(null, tokenStateChanges);
100
+
101
+ const gasFee = await this.Refund.getRefundFee(swapData, feeRate);
102
+ return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
103
+ }
104
+
105
+ }
@@ -0,0 +1,22 @@
1
+ import {EVMTokens} from "../../evm/chain/modules/EVMTokens";
2
+ import {CitreaFees} from "./CitreaFees";
3
+
4
+
5
+ export class CitreaTokens extends EVMTokens {
6
+
7
+ public static readonly StateDiffSize = {
8
+ APPROVE_DIFF_SIZE: 35,
9
+ TRANSFER_DIFF_SIZE: 55
10
+ };
11
+
12
+ async getApproveFee(feeRate?: string): Promise<bigint> {
13
+ feeRate ??= await this.root.Fees.getFeeRate();
14
+ return CitreaFees.getGasFee(EVMTokens.GasCosts.APPROVE, feeRate, CitreaTokens.StateDiffSize.APPROVE_DIFF_SIZE);
15
+ }
16
+
17
+ async getTransferFee(feeRate?: string): Promise<bigint> {
18
+ feeRate ??= await this.root.Fees.getFeeRate();
19
+ return CitreaFees.getGasFee(EVMTokens.GasCosts.APPROVE, feeRate, CitreaTokens.StateDiffSize.TRANSFER_DIFF_SIZE);
20
+ }
21
+
22
+ }
@@ -23,25 +23,27 @@ function serializeBlockHeader(e: BtcBlock): EVMBtcHeader {
23
23
  });
24
24
  }
25
25
 
26
- const GAS_PER_BLOCKHEADER = 30_000;
27
- const GAS_BASE_MAIN = 15_000;
28
- const GAS_PER_BLOCKHEADER_FORK = 65_000;
29
- const GAS_PER_BLOCKHEADER_FORKED = 10_000;
30
- const GAS_BASE_FORK = 25_000;
31
-
32
26
  const logger = getLogger("EVMBtcRelay: ");
33
27
 
34
28
  export class EVMBtcRelay<B extends BtcBlock>
35
29
  extends EVMContractBase<BtcRelayTypechain>
36
30
  implements BtcRelay<EVMBtcStoredHeader, EVMTx, B, EVMSigner> {
37
31
 
32
+ public static GasCosts = {
33
+ GAS_PER_BLOCKHEADER: 30_000,
34
+ GAS_BASE_MAIN: 15_000 + 21_000,
35
+ GAS_PER_BLOCKHEADER_FORK: 65_000,
36
+ GAS_PER_BLOCKHEADER_FORKED: 10_000,
37
+ GAS_BASE_FORK: 25_000 + 21_000
38
+ }
39
+
38
40
  public async SaveMainHeaders(signer: string, mainHeaders: EVMBtcHeader[], storedHeader: EVMBtcStoredHeader, feeRate: string): Promise<EVMTx> {
39
41
  const tx = await this.contract.submitMainBlockheaders.populateTransaction(Buffer.concat([
40
42
  storedHeader.serialize(),
41
43
  Buffer.concat(mainHeaders.map(header => header.serializeCompact()))
42
44
  ]));
43
45
  tx.from = signer;
44
- EVMFees.applyFeeRate(tx, GAS_BASE_MAIN + (GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
46
+ EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
45
47
  return tx;
46
48
  }
47
49
 
@@ -51,7 +53,7 @@ export class EVMBtcRelay<B extends BtcBlock>
51
53
  Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
52
54
  ]));
53
55
  tx.from = signer;
54
- EVMFees.applyFeeRate(tx, GAS_BASE_MAIN + (GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
56
+ EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
55
57
  return tx;
56
58
  }
57
59
 
@@ -61,7 +63,7 @@ export class EVMBtcRelay<B extends BtcBlock>
61
63
  Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
62
64
  ]));
63
65
  tx.from = signer;
64
- EVMFees.applyFeeRate(tx, GAS_BASE_FORK + (GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
66
+ EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_FORK + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
65
67
  return tx;
66
68
  }
67
69
 
@@ -398,6 +400,8 @@ export class EVMBtcRelay<B extends BtcBlock>
398
400
  * @param feeRate
399
401
  */
400
402
  public async estimateSynchronizeFee(requiredBlockheight: number, feeRate?: string): Promise<bigint> {
403
+ feeRate ??= await this.Chain.Fees.getFeeRate();
404
+
401
405
  const tipData = await this.getTipData();
402
406
  const currBlockheight = tipData.blockheight;
403
407
 
@@ -405,7 +409,8 @@ export class EVMBtcRelay<B extends BtcBlock>
405
409
 
406
410
  if(blockheightDelta<=0) return 0n;
407
411
 
408
- const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
412
+ const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
413
+ + EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_BASE_MAIN * Math.ceil(blockheightDelta / this.maxHeadersPerTx), feeRate);
409
414
  logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
410
415
  " blockheight delta: "+blockheightDelta+" fee: "+synchronizationFee.toString(10));
411
416
 
@@ -413,13 +418,13 @@ export class EVMBtcRelay<B extends BtcBlock>
413
418
  }
414
419
 
415
420
  /**
416
- * Returns fee required (in SOL) to synchronize a single block to btc relay
421
+ * Returns fee required (in native token) to synchronize a single block to btc relay
417
422
  *
418
423
  * @param feeRate
419
424
  */
420
425
  public async getFeePerBlock(feeRate?: string): Promise<bigint> {
421
426
  feeRate ??= await this.Chain.Fees.getFeeRate();
422
- return EVMFees.getGasFee(GAS_PER_BLOCKHEADER, feeRate);
427
+ return EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER, feeRate);
423
428
  }
424
429
 
425
430
  /**
@@ -33,13 +33,13 @@ export class EVMChainInterface<ChainId extends string = string, EVMChainId exten
33
33
  public readonly config: EVMConfiguration;
34
34
 
35
35
  public Fees: EVMFees;
36
- public readonly Tokens: EVMTokens;
37
- public readonly Transactions: EVMTransactions;
38
- public readonly Signatures: EVMSignatures;
39
- public readonly Events: EVMEvents;
40
- public readonly Blocks: EVMBlocks;
36
+ public Tokens: EVMTokens;
37
+ public Transactions: EVMTransactions;
38
+ public Signatures: EVMSignatures;
39
+ public Events: EVMEvents;
40
+ public Blocks: EVMBlocks;
41
41
 
42
- protected readonly logger: LoggerType;
42
+ protected logger: LoggerType;
43
43
 
44
44
  constructor(
45
45
  chainId: ChainId,
@@ -1,7 +1,5 @@
1
1
  import { getLogger } from "../../../utils/Utils";
2
- import {Provider, TransactionRequest} from "ethers";
3
-
4
- const MAX_FEE_AGE = 5000;
2
+ import {JsonRpcApiProvider, TransactionRequest} from "ethers";
5
3
 
6
4
  export type EVMFeeRate = {
7
5
  maxFeePerGas: bigint;
@@ -9,14 +7,15 @@ export type EVMFeeRate = {
9
7
  };
10
8
 
11
9
  export class EVMFees {
10
+ protected MAX_FEE_AGE = 5000;
12
11
 
13
- private readonly logger = getLogger("EVMFees: ");
12
+ protected readonly logger = getLogger("EVMFees: ");
14
13
 
15
- private readonly provider: Provider;
16
- private readonly maxFeeRatePerGas: bigint;
17
- private readonly priorityFee: bigint;
14
+ protected readonly provider: JsonRpcApiProvider;
15
+ protected readonly maxFeeRatePerGas: bigint;
16
+ protected readonly priorityFee: bigint;
18
17
 
19
- private readonly feeMultiplierPPM: bigint;
18
+ protected readonly feeMultiplierPPM: bigint;
20
19
 
21
20
  private blockFeeCache: {
22
21
  timestamp: number,
@@ -24,7 +23,7 @@ export class EVMFees {
24
23
  } = null;
25
24
 
26
25
  constructor(
27
- provider: Provider,
26
+ provider: JsonRpcApiProvider,
28
27
  maxFeeRatePerGas: bigint = 500n * 1_000_000_000n,
29
28
  priorityFee: bigint = 1n * 1_000_000_000n,
30
29
  feeMultiplier: number = 1.25,
@@ -56,7 +55,7 @@ export class EVMFees {
56
55
  * @private
57
56
  */
58
57
  public async getFeeRate(): Promise<string> {
59
- if(this.blockFeeCache==null || Date.now() - this.blockFeeCache.timestamp > MAX_FEE_AGE) {
58
+ if(this.blockFeeCache==null || Date.now() - this.blockFeeCache.timestamp > this.MAX_FEE_AGE) {
60
59
  let obj = {
61
60
  timestamp: Date.now(),
62
61
  feeRate: null
@@ -99,7 +98,7 @@ export class EVMFees {
99
98
 
100
99
  tx.maxFeePerGas = BigInt(baseFee) + BigInt(priorityFee);
101
100
  tx.maxPriorityFeePerGas = BigInt(priorityFee);
102
- tx.gasLimit = BigInt(gas) + 21_000n;
101
+ tx.gasLimit = BigInt(gas);
103
102
  }
104
103
 
105
104
  }
@@ -3,6 +3,7 @@ import {Contract, TransactionRequest} from "ethers";
3
3
  import {ERC20Abi} from "./ERC20Abi";
4
4
  import {EVMAddresses} from "./EVMAddresses";
5
5
  import {EVMFees} from "./EVMFees";
6
+ import {EVMSwapData} from "../../swaps/EVMSwapData";
6
7
 
7
8
 
8
9
  export class EVMTokens extends EVMModule<any> {
@@ -10,8 +11,8 @@ export class EVMTokens extends EVMModule<any> {
10
11
  public static readonly ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
11
12
 
12
13
  public static readonly GasCosts = {
13
- TRANSFER: 80_000,
14
- APPROVE: 80_000
14
+ TRANSFER: 80_000 + 21_000,
15
+ APPROVE: 80_000 + 21_000
15
16
  };
16
17
 
17
18
  private getContract(address: string) {
@@ -112,4 +113,14 @@ export class EVMTokens extends EVMModule<any> {
112
113
  return tx;
113
114
  }
114
115
 
116
+ async getApproveFee(feeRate?: string): Promise<bigint> {
117
+ feeRate ??= await this.root.Fees.getFeeRate();
118
+ return EVMFees.getGasFee(EVMTokens.GasCosts.APPROVE, feeRate);
119
+ }
120
+
121
+ async getTransferFee(feeRate?: string): Promise<bigint> {
122
+ feeRate ??= await this.root.Fees.getFeeRate();
123
+ return EVMFees.getGasFee(EVMTokens.GasCosts.APPROVE, feeRate);
124
+ }
125
+
115
126
  }
@@ -54,11 +54,11 @@ export class EVMSpvVaultContract<ChainId extends string>
54
54
  EVMSpvWithdrawalData
55
55
  >
56
56
  {
57
- private static readonly GasCosts = {
58
- DEPOSIT: 150_000,
59
- OPEN: 100_000,
60
- FRONT: 250_000,
61
- CLAIM: 250_000
57
+ public static readonly GasCosts = {
58
+ DEPOSIT: 150_000 + 21_000,
59
+ OPEN: 100_000 + 21_000,
60
+ FRONT: 250_000 + 21_000,
61
+ CLAIM: 250_000 + 21_000
62
62
  };
63
63
 
64
64
  readonly chainId: ChainId;
@@ -7,8 +7,8 @@ import {EVMTx} from "../../chain/modules/EVMTransactions";
7
7
  export class EVMLpVault extends EVMSwapModule {
8
8
 
9
9
  private static readonly GasCosts = {
10
- WITHDRAW: 100_000,
11
- DEPOSIT: 100_000
10
+ WITHDRAW: 100_000 + 21_000,
11
+ DEPOSIT: 100_000 + 21_000
12
12
  };
13
13
 
14
14
  /**
@@ -13,8 +13,11 @@ import {EVMBtcStoredHeader} from "../../btcrelay/headers/EVMBtcStoredHeader";
13
13
  export class EVMSwapClaim extends EVMSwapModule {
14
14
 
15
15
  private static readonly GasCosts = {
16
- CLAIM: 120_000,
17
- CLAIM_WITH_SUCCESS_ACTION: 150_000
16
+ BASE: 30_000 + 21_000,
17
+ ERC20_TRANSFER: 40_000,
18
+ NATIVE_TRANSFER: 7500,
19
+ LP_VAULT_TRANSFER: 10_000,
20
+ REPUTATION: 25_000
18
21
  };
19
22
 
20
23
  /**
@@ -37,7 +40,7 @@ export class EVMSwapClaim extends EVMSwapModule {
37
40
  //TODO: Claim with success action not supported yet!
38
41
  const tx = await this.swapContract.claim.populateTransaction(swapData.toEscrowStruct(), witness);
39
42
  tx.from = signer;
40
- EVMFees.applyFeeRate(tx, EVMSwapClaim.GasCosts.CLAIM + (claimHandlerGas ?? 0), feeRate);
43
+ EVMFees.applyFeeRate(tx, this.getClaimGas(swapData) + (claimHandlerGas ?? 0), feeRate);
41
44
  return tx;
42
45
  }
43
46
 
@@ -123,6 +126,35 @@ export class EVMSwapClaim extends EVMSwapModule {
123
126
  return [...initialTxns, claimTx];
124
127
  }
125
128
 
129
+ getClaimGas(swapData: EVMSwapData): number {
130
+ let totalGas = EVMSwapClaim.GasCosts.BASE;
131
+ if(swapData.reputation) totalGas += EVMSwapClaim.GasCosts.REPUTATION;
132
+ if(swapData.isPayOut()) {
133
+ if(swapData.isToken(this.root.getNativeCurrencyAddress())) {
134
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
135
+ } else {
136
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
137
+ }
138
+ } else {
139
+ totalGas += EVMSwapClaim.GasCosts.LP_VAULT_TRANSFER;
140
+ }
141
+ if(swapData.getClaimerBounty() > 0n) {
142
+ if(swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
143
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
144
+ } else {
145
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
146
+ }
147
+ }
148
+ if(swapData.getSecurityDeposit() > swapData.getClaimerBounty()) {
149
+ if(swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
150
+ totalGas += EVMSwapClaim.GasCosts.NATIVE_TRANSFER;
151
+ } else {
152
+ totalGas += EVMSwapClaim.GasCosts.ERC20_TRANSFER;
153
+ }
154
+ }
155
+ return totalGas;
156
+ }
157
+
126
158
  /**
127
159
  * Get the estimated starknet transaction fee of the claim transaction
128
160
  */
@@ -130,7 +162,7 @@ export class EVMSwapClaim extends EVMSwapModule {
130
162
  feeRate ??= await this.root.Fees.getFeeRate();
131
163
 
132
164
  //TODO: Claim with success action not supported yet!
133
- let gasRequired = EVMSwapClaim.GasCosts.CLAIM;
165
+ let gasRequired = this.getClaimGas(swapData);
134
166
 
135
167
  const claimHandler: IClaimHandler<any, any> = this.contract.claimHandlersByAddress[swapData.claimHandler.toLowerCase()];
136
168
  if(claimHandler!=null) gasRequired += claimHandler.getGas(swapData);
@@ -1,5 +1,4 @@
1
1
  import {SignatureVerificationError, SwapCommitStateType, SwapDataVerificationError} from "@atomiqlabs/base";
2
- import {Buffer} from "buffer";
3
2
  import { EVMSwapModule } from "../EVMSwapModule";
4
3
  import {EVMSwapData} from "../EVMSwapData";
5
4
  import {keccak256, TransactionRequest, ZeroHash} from "ethers";
@@ -36,8 +35,9 @@ const Initialize = [
36
35
  export class EVMSwapInit extends EVMSwapModule {
37
36
 
38
37
  private static readonly GasCosts = {
39
- INIT: 100_000,
40
- INIT_PAY_IN: 130_000,
38
+ BASE: 45_000 + 21_000,
39
+ ERC20_TRANSFER: 40_000,
40
+ LP_VAULT_TRANSFER: 10_000
41
41
  };
42
42
 
43
43
  /**
@@ -52,13 +52,15 @@ export class EVMSwapInit extends EVMSwapModule {
52
52
  */
53
53
  private async Init(sender: string, swapData: EVMSwapData, timeout: bigint, signature: string, feeRate: string): Promise<TransactionRequest> {
54
54
  let value = 0n;
55
- if(swapData.isToken(this.root.getNativeCurrencyAddress())) value += swapData.getAmount();
55
+ if(swapData.isPayIn()) {
56
+ if(swapData.isOfferer(sender) && swapData.isToken(this.root.getNativeCurrencyAddress())) value += swapData.getAmount();
57
+ }
56
58
  if(swapData.isDepositToken(this.root.getNativeCurrencyAddress())) value += swapData.getTotalDeposit();
57
59
  const tx = await this.swapContract.initialize.populateTransaction(swapData.toEscrowStruct(), signature, timeout, "0x"+(swapData.extraData ?? ""), {
58
60
  value
59
61
  });
60
62
  tx.from = sender;
61
- EVMFees.applyFeeRate(tx, swapData.isPayIn() ? EVMSwapInit.GasCosts.INIT_PAY_IN : EVMSwapInit.GasCosts.INIT, feeRate);
63
+ EVMFees.applyFeeRate(tx, this.getInitGas(swapData), feeRate);
62
64
  return tx;
63
65
  }
64
66
 
@@ -259,7 +261,7 @@ export class EVMSwapInit extends EVMSwapModule {
259
261
 
260
262
  const txs: EVMTx[] = [];
261
263
  const requiredApprovals: {[address: string]: bigint} = {};
262
- if(swapData.payIn && swapData.isOfferer(sender)) {
264
+ if(swapData.isPayIn() && swapData.isOfferer(sender)) {
263
265
  if(!swapData.isToken(this.root.getNativeCurrencyAddress())) {
264
266
  requiredApprovals[swapData.token.toLowerCase()] = swapData.amount;
265
267
  }
@@ -281,12 +283,44 @@ export class EVMSwapInit extends EVMSwapModule {
281
283
  return txs;
282
284
  }
283
285
 
286
+ private getInitGas(swapData: EVMSwapData): number {
287
+ let totalGas = EVMSwapInit.GasCosts.BASE;
288
+ if(swapData.isPayIn()) {
289
+ if(!swapData.isToken(this.root.getNativeCurrencyAddress())) {
290
+ totalGas += EVMSwapInit.GasCosts.ERC20_TRANSFER;
291
+ }
292
+ } else {
293
+ totalGas += EVMSwapInit.GasCosts.LP_VAULT_TRANSFER;
294
+ }
295
+ if(swapData.getTotalDeposit() > 0) {
296
+ if(!swapData.isPayIn() || !swapData.isDepositToken(swapData.token)) {
297
+ if(!swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
298
+ totalGas += EVMSwapInit.GasCosts.ERC20_TRANSFER;
299
+ }
300
+ }
301
+ }
302
+ return totalGas;
303
+ }
304
+
284
305
  /**
285
- * Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
286
- * and also deposit for ATAs
306
+ * Get the estimated fee of the init transaction
287
307
  */
288
- async getInitFee(swapData?: EVMSwapData, feeRate?: string): Promise<bigint> {
308
+ async getInitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
289
309
  feeRate ??= await this.root.Fees.getFeeRate();
290
- return EVMFees.getGasFee(swapData.payIn ? EVMSwapInit.GasCosts.INIT_PAY_IN : EVMSwapInit.GasCosts.INIT, feeRate);
310
+ let totalFee = EVMFees.getGasFee(this.getInitGas(swapData), feeRate);
311
+ if(swapData.isPayIn()) {
312
+ if(!swapData.isToken(this.root.getNativeCurrencyAddress())) {
313
+ totalFee += await this.root.Tokens.getApproveFee(feeRate);
314
+ }
315
+ }
316
+ if(swapData.getTotalDeposit() > 0) {
317
+ if(!swapData.isPayIn() || !swapData.isDepositToken(swapData.token)) {
318
+ if(!swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
319
+ totalFee += await this.root.Tokens.getApproveFee(feeRate);
320
+ }
321
+ }
322
+ }
323
+
324
+ return totalFee;
291
325
  }
292
326
  }
@@ -17,8 +17,11 @@ const Refund = [
17
17
  export class EVMSwapRefund extends EVMSwapModule {
18
18
 
19
19
  private static readonly GasCosts = {
20
- REFUND: 100_000,
21
- REFUND_PAY_OUT: 130_000
20
+ BASE: 35_000 + 21_000,
21
+ ERC20_TRANSFER: 40_000,
22
+ NATIVE_TRANSFER: 7500,
23
+ LP_VAULT_TRANSFER: 10_000,
24
+ REPUTATION: 25_000
22
25
  };
23
26
 
24
27
  /**
@@ -40,7 +43,7 @@ export class EVMSwapRefund extends EVMSwapModule {
40
43
  ): Promise<TransactionRequest> {
41
44
  const tx = await this.swapContract.refund.populateTransaction(swapData.toEscrowStruct(), witness);
42
45
  tx.from = signer;
43
- EVMFees.applyFeeRate(tx, (swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND) + (handlerGas ?? 0), feeRate)
46
+ EVMFees.applyFeeRate(tx, this.getRefundGas(swapData) + (handlerGas ?? 0), feeRate)
44
47
  return tx;
45
48
  }
46
49
 
@@ -63,7 +66,7 @@ export class EVMSwapRefund extends EVMSwapModule {
63
66
  ): Promise<TransactionRequest> {
64
67
  const tx = await this.swapContract.cooperativeRefund.populateTransaction(swapData.toEscrowStruct(), signature, BigInt(timeout));
65
68
  tx.from = sender;
66
- EVMFees.applyFeeRate(tx, swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND, feeRate)
69
+ EVMFees.applyFeeRate(tx, this.getRefundGas(swapData), feeRate)
67
70
  return tx;
68
71
  }
69
72
 
@@ -186,13 +189,41 @@ export class EVMSwapRefund extends EVMSwapModule {
186
189
  return [tx];
187
190
  }
188
191
 
192
+ getRefundGas(swapData: EVMSwapData): number {
193
+ let totalGas = EVMSwapRefund.GasCosts.BASE;
194
+ if(swapData.reputation) totalGas += EVMSwapRefund.GasCosts.REPUTATION;
195
+ if(swapData.isPayIn()) {
196
+ if(swapData.isToken(this.root.getNativeCurrencyAddress())) {
197
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
198
+ } else {
199
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
200
+ }
201
+ } else {
202
+ totalGas += EVMSwapRefund.GasCosts.LP_VAULT_TRANSFER;
203
+ }
204
+ if(swapData.getSecurityDeposit() > 0n) {
205
+ if(swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
206
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
207
+ } else {
208
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
209
+ }
210
+ }
211
+ if(swapData.getClaimerBounty() > swapData.getSecurityDeposit()) {
212
+ if(swapData.isDepositToken(this.root.getNativeCurrencyAddress())) {
213
+ totalGas += EVMSwapRefund.GasCosts.NATIVE_TRANSFER;
214
+ } else {
215
+ totalGas += EVMSwapRefund.GasCosts.ERC20_TRANSFER;
216
+ }
217
+ }
218
+ return totalGas;
219
+ }
220
+
189
221
  /**
190
- * Get the estimated solana transaction fee of the refund transaction, in the worst case scenario in case where the
191
- * ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
222
+ * Get the estimated transaction fee of the refund transaction
192
223
  */
193
224
  async getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
194
225
  feeRate ??= await this.root.Fees.getFeeRate();
195
- return EVMFees.getGasFee(swapData.payIn ? EVMSwapRefund.GasCosts.REFUND_PAY_OUT : EVMSwapRefund.GasCosts.REFUND, feeRate);
226
+ return EVMFees.getGasFee(this.getRefundGas(swapData), feeRate);
196
227
  }
197
228
 
198
229
  }
package/src/index.ts CHANGED
@@ -42,3 +42,4 @@ export * from "./evm/wallet/EVMSigner";
42
42
 
43
43
  export * from "./chains/citrea/CitreaInitializer";
44
44
  export * from "./chains/citrea/CitreaChainType";
45
+ export * from "./chains/citrea/CitreaFees";