@atomiqlabs/chain-evm 1.0.0-dev.27 → 1.0.0-dev.28
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.
- package/dist/chains/citrea/CitreaInitializer.js +2 -2
- package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +11 -2
- package/dist/chains/citrea/CitreaSpvVaultContract.js +64 -5
- package/dist/chains/citrea/CitreaSwapContract.d.ts +2 -2
- package/dist/chains/citrea/CitreaSwapContract.js +5 -7
- package/dist/evm/chain/EVMChainInterface.js +1 -2
- package/dist/evm/chain/modules/EVMAddresses.d.ts +1 -0
- package/dist/evm/chain/modules/EVMAddresses.js +5 -0
- package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +14 -5
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +83 -13
- package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +1 -0
- package/dist/evm/spv_swap/EVMSpvVaultData.js +21 -0
- package/dist/evm/swaps/EVMSwapContract.d.ts +4 -4
- package/dist/evm/swaps/EVMSwapContract.js +4 -4
- package/package.json +2 -2
- package/src/chains/citrea/CitreaInitializer.ts +2 -1
- package/src/chains/citrea/CitreaSpvVaultContract.ts +63 -5
- package/src/chains/citrea/CitreaSwapContract.ts +5 -7
- package/src/evm/chain/EVMChainInterface.ts +1 -2
- package/src/evm/chain/modules/EVMAddresses.ts +6 -0
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +83 -13
- package/src/evm/spv_swap/EVMSpvVaultData.ts +24 -1
- package/src/evm/swaps/EVMSwapContract.ts +4 -4
|
@@ -4,7 +4,6 @@ exports.CitreaInitializer = exports.initializeCitrea = exports.CitreaAssets = vo
|
|
|
4
4
|
const base_1 = require("@atomiqlabs/base");
|
|
5
5
|
const ethers_1 = require("ethers");
|
|
6
6
|
const EVMChainInterface_1 = require("../../evm/chain/EVMChainInterface");
|
|
7
|
-
const EVMSpvVaultContract_1 = require("../../evm/spv_swap/EVMSpvVaultContract");
|
|
8
7
|
const EVMChainEventsBrowser_1 = require("../../evm/events/EVMChainEventsBrowser");
|
|
9
8
|
const EVMSwapData_1 = require("../../evm/swaps/EVMSwapData");
|
|
10
9
|
const EVMSpvVaultData_1 = require("../../evm/spv_swap/EVMSpvVaultData");
|
|
@@ -13,6 +12,7 @@ const CitreaFees_1 = require("./CitreaFees");
|
|
|
13
12
|
const CitreaBtcRelay_1 = require("./CitreaBtcRelay");
|
|
14
13
|
const CitreaSwapContract_1 = require("./CitreaSwapContract");
|
|
15
14
|
const CitreaTokens_1 = require("./CitreaTokens");
|
|
15
|
+
const CitreaSpvVaultContract_1 = require("./CitreaSpvVaultContract");
|
|
16
16
|
const CitreaChainIds = {
|
|
17
17
|
MAINNET: null,
|
|
18
18
|
TESTNET4: 5115
|
|
@@ -102,7 +102,7 @@ function initializeCitrea(options, bitcoinRpc, network) {
|
|
|
102
102
|
...options?.handlerContracts?.claim
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
|
-
const spvVaultContract = new
|
|
105
|
+
const spvVaultContract = new CitreaSpvVaultContract_1.CitreaSpvVaultContract(chainInterface, btcRelay, bitcoinRpc, options.spvVaultContract ?? defaultContractAddresses.spvVaultContract, options.spvVaultDeploymentHeight ?? defaultContractAddresses.spvVaultDeploymentHeight);
|
|
106
106
|
const chainEvents = new EVMChainEventsBrowser_1.EVMChainEventsBrowser(chainInterface, swapContract, spvVaultContract);
|
|
107
107
|
return {
|
|
108
108
|
chainId: "CITREA",
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { EVMSpvVaultContract } from "../../evm/spv_swap/EVMSpvVaultContract";
|
|
2
2
|
import { EVMSpvWithdrawalData } from "../../evm/spv_swap/EVMSpvWithdrawalData";
|
|
3
|
+
import { EVMSpvVaultData } from "../../evm/spv_swap/EVMSpvVaultData";
|
|
3
4
|
export declare class CitreaSpvVaultContract extends EVMSpvVaultContract<"CITREA"> {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
static readonly StateDiffSize: {
|
|
6
|
+
BASE_DIFF_SIZE: number;
|
|
7
|
+
ERC_20_TRANSFER_DIFF_SIZE: number;
|
|
8
|
+
NATIVE_SELF_TRANSFER_DIFF_SIZE: number;
|
|
9
|
+
NATIVE_TRANSFER_DIFF_SIZE: number;
|
|
10
|
+
EXECUTION_SCHEDULE_DIFF_SIZE: number;
|
|
11
|
+
};
|
|
12
|
+
private calculateStateDiff;
|
|
13
|
+
getClaimFee(signer: string, vault?: EVMSpvVaultData, data?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint>;
|
|
14
|
+
getFrontFee(signer: string, vault?: EVMSpvVaultData, data?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint>;
|
|
6
15
|
}
|
|
@@ -2,15 +2,74 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CitreaSpvVaultContract = void 0;
|
|
4
4
|
const EVMSpvVaultContract_1 = require("../../evm/spv_swap/EVMSpvVaultContract");
|
|
5
|
-
const
|
|
5
|
+
const EVMSpvVaultData_1 = require("../../evm/spv_swap/EVMSpvVaultData");
|
|
6
|
+
const lib_esm_1 = require("ethers/lib.esm");
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const CitreaFees_1 = require("./CitreaFees");
|
|
9
|
+
const EVMAddresses_1 = require("../../evm/chain/modules/EVMAddresses");
|
|
6
10
|
class CitreaSpvVaultContract extends EVMSpvVaultContract_1.EVMSpvVaultContract {
|
|
7
|
-
|
|
11
|
+
calculateStateDiff(signer, tokenStateChanges) {
|
|
12
|
+
let stateDiffSize = 0;
|
|
13
|
+
tokenStateChanges.forEach(val => {
|
|
14
|
+
const [address, token] = val.split(":");
|
|
15
|
+
if (token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
16
|
+
stateDiffSize += address.toLowerCase() === signer?.toLowerCase() ? CitreaSpvVaultContract.StateDiffSize.NATIVE_SELF_TRANSFER_DIFF_SIZE : CitreaSpvVaultContract.StateDiffSize.NATIVE_TRANSFER_DIFF_SIZE;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
stateDiffSize += CitreaSpvVaultContract.StateDiffSize.ERC_20_TRANSFER_DIFF_SIZE;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return stateDiffSize;
|
|
23
|
+
}
|
|
24
|
+
async getClaimFee(signer, vault, data, feeRate) {
|
|
25
|
+
vault ?? (vault = EVMSpvVaultData_1.EVMSpvVaultData.randomVault());
|
|
8
26
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
9
|
-
|
|
27
|
+
const tokenStateChanges = new Set();
|
|
28
|
+
let diffSize = CitreaSpvVaultContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
29
|
+
const recipient = data != null ? data.recipient : EVMAddresses_1.EVMAddresses.randomAddress();
|
|
30
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
31
|
+
tokenStateChanges.add(recipient.toLowerCase() + ":" + vault.token0.token.toLowerCase());
|
|
32
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
33
|
+
tokenStateChanges.add(ethers_1.ZeroAddress + ":" + vault.token0.token.toLowerCase()); //Also needs to pay out to fronter
|
|
34
|
+
if (data == null || data.callerFeeRate > 0n)
|
|
35
|
+
tokenStateChanges.add(signer + ":" + vault.token0.token.toLowerCase()); //Also needs to pay out to caller
|
|
36
|
+
}
|
|
37
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
38
|
+
tokenStateChanges.add(recipient.toLowerCase() + ":" + vault.token1.token.toLowerCase());
|
|
39
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
40
|
+
tokenStateChanges.add(ethers_1.ZeroAddress + ":" + vault.token1.token.toLowerCase()); //Also needs to pay out to fronter
|
|
41
|
+
if (data == null || data.callerFeeRate > 0n)
|
|
42
|
+
tokenStateChanges.add(signer + ":" + vault.token1.token.toLowerCase()); //Also needs to pay out to caller
|
|
43
|
+
}
|
|
44
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
45
|
+
if (data == null || (data.executionHash != null && data.executionHash !== lib_esm_1.ZeroHash))
|
|
46
|
+
diffSize += CitreaSpvVaultContract.StateDiffSize.EXECUTION_SCHEDULE_DIFF_SIZE;
|
|
47
|
+
const gasFee = await super.getClaimFee(signer, vault, data, feeRate);
|
|
48
|
+
return gasFee + CitreaFees_1.CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
10
49
|
}
|
|
11
|
-
async getFrontFee(signer,
|
|
50
|
+
async getFrontFee(signer, vault, data, feeRate) {
|
|
51
|
+
vault ?? (vault = EVMSpvVaultData_1.EVMSpvVaultData.randomVault());
|
|
12
52
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
13
|
-
|
|
53
|
+
const tokenStateChanges = new Set();
|
|
54
|
+
let diffSize = CitreaSpvVaultContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
55
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
56
|
+
tokenStateChanges.add(signer + ":" + vault.token0.token.toLowerCase());
|
|
57
|
+
}
|
|
58
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
59
|
+
tokenStateChanges.add(signer + ":" + vault.token1.token.toLowerCase());
|
|
60
|
+
}
|
|
61
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
62
|
+
if (data == null || (data.executionHash != null && data.executionHash !== lib_esm_1.ZeroHash))
|
|
63
|
+
diffSize += CitreaSpvVaultContract.StateDiffSize.EXECUTION_SCHEDULE_DIFF_SIZE;
|
|
64
|
+
const gasFee = await super.getFrontFee(signer, vault, data, feeRate);
|
|
65
|
+
return gasFee + CitreaFees_1.CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
14
66
|
}
|
|
15
67
|
}
|
|
16
68
|
exports.CitreaSpvVaultContract = CitreaSpvVaultContract;
|
|
69
|
+
CitreaSpvVaultContract.StateDiffSize = {
|
|
70
|
+
BASE_DIFF_SIZE: 50,
|
|
71
|
+
ERC_20_TRANSFER_DIFF_SIZE: 50,
|
|
72
|
+
NATIVE_SELF_TRANSFER_DIFF_SIZE: 20,
|
|
73
|
+
NATIVE_TRANSFER_DIFF_SIZE: 30,
|
|
74
|
+
EXECUTION_SCHEDULE_DIFF_SIZE: 40
|
|
75
|
+
};
|
|
@@ -13,10 +13,10 @@ export declare class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
|
|
|
13
13
|
/**
|
|
14
14
|
* Get the estimated solana fee of the commit transaction
|
|
15
15
|
*/
|
|
16
|
-
getCommitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
16
|
+
getCommitFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
17
17
|
getClaimFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
18
18
|
/**
|
|
19
19
|
* Get the estimated solana transaction fee of the refund transaction
|
|
20
20
|
*/
|
|
21
|
-
getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
21
|
+
getRefundFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
22
22
|
}
|
|
@@ -20,7 +20,7 @@ class CitreaSwapContract extends EVMSwapContract_1.EVMSwapContract {
|
|
|
20
20
|
/**
|
|
21
21
|
* Get the estimated solana fee of the commit transaction
|
|
22
22
|
*/
|
|
23
|
-
async getCommitFee(swapData, feeRate) {
|
|
23
|
+
async getCommitFee(signer, swapData, feeRate) {
|
|
24
24
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
25
25
|
const tokenStateChanges = new Set();
|
|
26
26
|
let diffSize = CitreaSwapContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
@@ -30,11 +30,10 @@ class CitreaSwapContract extends EVMSwapContract_1.EVMSwapContract {
|
|
|
30
30
|
else {
|
|
31
31
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase() + ":" + swapData.getToken().toLowerCase());
|
|
32
32
|
}
|
|
33
|
-
//TODO: Since we don't know who is sending the transaction, we calculate with the worst case
|
|
34
33
|
if (swapData.getTotalDeposit() > 0n) {
|
|
35
|
-
tokenStateChanges.add(
|
|
34
|
+
tokenStateChanges.add(signer.toLowerCase() + ":" + swapData.getDepositToken().toLowerCase());
|
|
36
35
|
}
|
|
37
|
-
diffSize += this.calculateStateDiff(
|
|
36
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
38
37
|
const gasFee = await this.Init.getInitFee(swapData, feeRate);
|
|
39
38
|
return gasFee + CitreaFees_1.CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
40
39
|
}
|
|
@@ -63,7 +62,7 @@ class CitreaSwapContract extends EVMSwapContract_1.EVMSwapContract {
|
|
|
63
62
|
/**
|
|
64
63
|
* Get the estimated solana transaction fee of the refund transaction
|
|
65
64
|
*/
|
|
66
|
-
async getRefundFee(swapData, feeRate) {
|
|
65
|
+
async getRefundFee(signer, swapData, feeRate) {
|
|
67
66
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
68
67
|
const tokenStateChanges = new Set();
|
|
69
68
|
let diffSize = CitreaSwapContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
@@ -75,14 +74,13 @@ class CitreaSwapContract extends EVMSwapContract_1.EVMSwapContract {
|
|
|
75
74
|
else {
|
|
76
75
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase() + ":" + swapData.getToken().toLowerCase());
|
|
77
76
|
}
|
|
78
|
-
//TODO: Since we don't know if the refund is cooperative or not and also not the signer, we calculate with the worst case
|
|
79
77
|
if (swapData.getSecurityDeposit() > 0) {
|
|
80
78
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase() + ":" + swapData.getDepositToken().toLowerCase());
|
|
81
79
|
}
|
|
82
80
|
if (swapData.getClaimerBounty() > swapData.getSecurityDeposit()) {
|
|
83
81
|
tokenStateChanges.add(swapData.getClaimer().toLowerCase() + ":" + swapData.getDepositToken().toLowerCase());
|
|
84
82
|
}
|
|
85
|
-
diffSize += this.calculateStateDiff(
|
|
83
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
86
84
|
const gasFee = await this.Refund.getRefundFee(swapData, feeRate);
|
|
87
85
|
return gasFee + CitreaFees_1.CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
88
86
|
}
|
|
@@ -54,8 +54,7 @@ class EVMChainInterface {
|
|
|
54
54
|
return this.Transactions.offBeforeTxSigned(callback);
|
|
55
55
|
}
|
|
56
56
|
randomAddress() {
|
|
57
|
-
|
|
58
|
-
return wallet.address;
|
|
57
|
+
return EVMAddresses_1.EVMAddresses.randomAddress();
|
|
59
58
|
}
|
|
60
59
|
randomSigner() {
|
|
61
60
|
const wallet = ethers_1.Wallet.createRandom();
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EVMAddresses = void 0;
|
|
4
4
|
const EVMModule_1 = require("../EVMModule");
|
|
5
5
|
const ethers_1 = require("ethers");
|
|
6
|
+
const lib_esm_1 = require("ethers/lib.esm");
|
|
6
7
|
class EVMAddresses extends EVMModule_1.EVMModule {
|
|
7
8
|
///////////////////
|
|
8
9
|
//// Address utils
|
|
@@ -22,5 +23,9 @@ class EVMAddresses extends EVMModule_1.EVMModule {
|
|
|
22
23
|
return false;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
26
|
+
static randomAddress() {
|
|
27
|
+
const wallet = lib_esm_1.Wallet.createRandom();
|
|
28
|
+
return wallet.address;
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
exports.EVMAddresses = EVMAddresses;
|
|
@@ -16,10 +16,17 @@ export declare function packOwnerAndVaultId(owner: string, vaultId: bigint): str
|
|
|
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
18
|
static readonly GasCosts: {
|
|
19
|
-
|
|
19
|
+
DEPOSIT_BASE: number;
|
|
20
|
+
DEPOSIT_ERC20: number;
|
|
20
21
|
OPEN: number;
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
CLAIM_BASE: number;
|
|
23
|
+
CLAIM_NATIVE_TRANSFER: number;
|
|
24
|
+
CLAIM_ERC20_TRANSFER: number;
|
|
25
|
+
CLAIM_EXECUTION_SCHEDULE: number;
|
|
26
|
+
FRONT_BASE: number;
|
|
27
|
+
FRONT_NATIVE_TRANSFER: number;
|
|
28
|
+
FRONT_ERC20_TRANSFER: number;
|
|
29
|
+
FRONT_EXECUTION_SCHEDULE: number;
|
|
23
30
|
};
|
|
24
31
|
readonly chainId: ChainId;
|
|
25
32
|
readonly btcRelay: EVMBtcRelay<any>;
|
|
@@ -64,6 +71,8 @@ export declare class EVMSpvVaultContract<ChainId extends string> extends EVMCont
|
|
|
64
71
|
txsDeposit(signer: string, vault: EVMSpvVaultData, rawAmounts: bigint[], feeRate?: string): Promise<EVMTx[]>;
|
|
65
72
|
txsFrontLiquidity(signer: string, vault: EVMSpvVaultData, realWithdrawalTx: EVMSpvWithdrawalData, withdrawSequence: number, feeRate?: string): Promise<EVMTx[]>;
|
|
66
73
|
txsOpen(signer: string, vault: EVMSpvVaultData, feeRate?: string): Promise<EVMTx[]>;
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
getClaimGas(signer: string, vault: EVMSpvVaultData, data?: EVMSpvWithdrawalData): number;
|
|
75
|
+
getFrontGas(signer: string, vault: EVMSpvVaultData, data?: EVMSpvWithdrawalData): number;
|
|
76
|
+
getClaimFee(signer: string, vault: EVMSpvVaultData, withdrawalData: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint>;
|
|
77
|
+
getFrontFee(signer: string, vault?: EVMSpvVaultData, withdrawalData?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint>;
|
|
69
78
|
}
|
|
@@ -49,14 +49,25 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
49
49
|
return tx;
|
|
50
50
|
}
|
|
51
51
|
async Deposit(signer, vault, rawAmounts, feeRate) {
|
|
52
|
+
let totalGas = EVMSpvVaultContract.GasCosts.DEPOSIT_BASE;
|
|
52
53
|
let value = 0n;
|
|
53
|
-
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
54
|
+
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
54
55
|
value += rawAmounts[0] * vault.token0.multiplier;
|
|
55
|
-
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
if (rawAmounts[0] > 0n)
|
|
59
|
+
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
60
|
+
}
|
|
61
|
+
if (vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
56
62
|
value += (rawAmounts[1] ?? 0n) * vault.token1.multiplier;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
if (rawAmounts[1] != null && rawAmounts[1] > 0n && vault.token0.token.toLowerCase() !== vault.token1.token.toLowerCase())
|
|
66
|
+
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
67
|
+
}
|
|
57
68
|
const tx = await this.contract.deposit.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), rawAmounts[0], rawAmounts[1] ?? 0n, { value });
|
|
58
69
|
tx.from = signer;
|
|
59
|
-
EVMFees_1.EVMFees.applyFeeRate(tx,
|
|
70
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, totalGas, feeRate);
|
|
60
71
|
return tx;
|
|
61
72
|
}
|
|
62
73
|
async Front(signer, vault, data, withdrawalSequence, feeRate) {
|
|
@@ -68,13 +79,13 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
68
79
|
value += (frontingAmount[1] ?? 0n) * vault.token1.multiplier;
|
|
69
80
|
const tx = await this.contract.front.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), withdrawalSequence, data.getTxHash(), data.serializeToStruct(), { value });
|
|
70
81
|
tx.from = signer;
|
|
71
|
-
EVMFees_1.EVMFees.applyFeeRate(tx,
|
|
82
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, this.getFrontGas(signer, vault, data), feeRate);
|
|
72
83
|
return tx;
|
|
73
84
|
}
|
|
74
85
|
async Claim(signer, vault, data, blockheader, merkle, position, feeRate) {
|
|
75
86
|
const tx = await this.contract.claim.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), "0x" + data.btcTx.hex, blockheader.serializeToStruct(), merkle, position);
|
|
76
87
|
tx.from = signer;
|
|
77
|
-
EVMFees_1.EVMFees.applyFeeRate(tx,
|
|
88
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, this.getClaimGas(signer, vault, data), feeRate);
|
|
78
89
|
return tx;
|
|
79
90
|
}
|
|
80
91
|
async checkWithdrawalTx(tx) {
|
|
@@ -392,19 +403,78 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
392
403
|
" vaultId: " + vault.getVaultId().toString(10));
|
|
393
404
|
return [tx];
|
|
394
405
|
}
|
|
395
|
-
|
|
406
|
+
getClaimGas(signer, vault, data) {
|
|
407
|
+
let totalGas = EVMSpvVaultContract.GasCosts.CLAIM_BASE;
|
|
408
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
409
|
+
const transferFee = vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
410
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
411
|
+
totalGas += transferFee;
|
|
412
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
413
|
+
totalGas += transferFee; //Also needs to pay out to fronter
|
|
414
|
+
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
415
|
+
totalGas += transferFee; //Also needs to pay out to caller
|
|
416
|
+
}
|
|
417
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
418
|
+
const transferFee = vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
419
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
420
|
+
totalGas += transferFee;
|
|
421
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
422
|
+
totalGas += transferFee; //Also needs to pay out to fronter
|
|
423
|
+
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
424
|
+
totalGas += transferFee; //Also needs to pay out to caller
|
|
425
|
+
}
|
|
426
|
+
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
427
|
+
totalGas += EVMSpvVaultContract.GasCosts.CLAIM_EXECUTION_SCHEDULE;
|
|
428
|
+
return totalGas;
|
|
429
|
+
}
|
|
430
|
+
getFrontGas(signer, vault, data) {
|
|
431
|
+
let totalGas = EVMSpvVaultContract.GasCosts.FRONT_BASE;
|
|
432
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
433
|
+
totalGas += vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
434
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
435
|
+
}
|
|
436
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
437
|
+
totalGas += vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
438
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
439
|
+
}
|
|
440
|
+
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
441
|
+
totalGas += EVMSpvVaultContract.GasCosts.FRONT_EXECUTION_SCHEDULE;
|
|
442
|
+
return totalGas;
|
|
443
|
+
}
|
|
444
|
+
async getClaimFee(signer, vault, withdrawalData, feeRate) {
|
|
396
445
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
397
|
-
return EVMFees_1.EVMFees.getGasFee(
|
|
446
|
+
return EVMFees_1.EVMFees.getGasFee(this.getClaimGas(signer, vault, withdrawalData), feeRate);
|
|
398
447
|
}
|
|
399
|
-
async getFrontFee(signer, withdrawalData, feeRate) {
|
|
448
|
+
async getFrontFee(signer, vault, withdrawalData, feeRate) {
|
|
449
|
+
vault ?? (vault = EVMSpvVaultData_1.EVMSpvVaultData.randomVault());
|
|
400
450
|
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
401
|
-
|
|
451
|
+
let totalFee = EVMFees_1.EVMFees.getGasFee(this.getFrontGas(signer, vault, withdrawalData), feeRate);
|
|
452
|
+
if (withdrawalData == null || (withdrawalData.rawAmounts[0] != null && withdrawalData.rawAmounts[0] > 0n)) {
|
|
453
|
+
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
454
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (withdrawalData == null || (withdrawalData.rawAmounts[1] != null && withdrawalData.rawAmounts[1] > 0n)) {
|
|
458
|
+
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
459
|
+
if (vault.token1.token.toLowerCase() !== vault.token0.token.toLowerCase() || withdrawalData == null || withdrawalData.rawAmounts[0] == null || withdrawalData.rawAmounts[0] === 0n) {
|
|
460
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return totalFee;
|
|
402
465
|
}
|
|
403
466
|
}
|
|
404
467
|
exports.EVMSpvVaultContract = EVMSpvVaultContract;
|
|
405
468
|
EVMSpvVaultContract.GasCosts = {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
469
|
+
DEPOSIT_BASE: 15000 + 21000,
|
|
470
|
+
DEPOSIT_ERC20: 40000,
|
|
471
|
+
OPEN: 80000 + 21000,
|
|
472
|
+
CLAIM_BASE: 85000 + 21000,
|
|
473
|
+
CLAIM_NATIVE_TRANSFER: 7500,
|
|
474
|
+
CLAIM_ERC20_TRANSFER: 40000,
|
|
475
|
+
CLAIM_EXECUTION_SCHEDULE: 30000,
|
|
476
|
+
FRONT_BASE: 75000 + 21000,
|
|
477
|
+
FRONT_NATIVE_TRANSFER: 7500,
|
|
478
|
+
FRONT_ERC20_TRANSFER: 40000,
|
|
479
|
+
FRONT_EXECUTION_SCHEDULE: 30000
|
|
410
480
|
};
|
|
@@ -35,4 +35,5 @@ export declare class EVMSpvVaultData extends SpvVaultData<EVMSpvWithdrawalData>
|
|
|
35
35
|
updateState(withdrawalTxOrEvent: SpvVaultClaimEvent | SpvVaultCloseEvent | SpvVaultOpenEvent | SpvVaultDepositEvent | EVMSpvWithdrawalData): void;
|
|
36
36
|
getDepositCount(): number;
|
|
37
37
|
getVaultParamsStruct(): SpvVaultParametersStruct;
|
|
38
|
+
static randomVault(): EVMSpvVaultData;
|
|
38
39
|
}
|
|
@@ -6,6 +6,7 @@ const buffer_1 = require("buffer");
|
|
|
6
6
|
const EVMSpvWithdrawalData_1 = require("./EVMSpvWithdrawalData");
|
|
7
7
|
const ethers_1 = require("ethers");
|
|
8
8
|
const ethers_2 = require("ethers");
|
|
9
|
+
const EVMAddresses_1 = require("../chain/modules/EVMAddresses");
|
|
9
10
|
function getVaultParamsCommitment(vaultParams) {
|
|
10
11
|
return (0, ethers_2.keccak256)(ethers_2.AbiCoder.defaultAbiCoder().encode(["address", "address", "address", "uint192", "uint192", "uint256"], [vaultParams.btcRelayContract, vaultParams.token0, vaultParams.token1, vaultParams.token0Multiplier, vaultParams.token1Multiplier, vaultParams.confirmations]));
|
|
11
12
|
}
|
|
@@ -154,6 +155,26 @@ class EVMSpvVaultData extends base_1.SpvVaultData {
|
|
|
154
155
|
confirmations: this.confirmations
|
|
155
156
|
};
|
|
156
157
|
}
|
|
158
|
+
static randomVault() {
|
|
159
|
+
const spvVaultParams = {
|
|
160
|
+
btcRelayContract: EVMAddresses_1.EVMAddresses.randomAddress(),
|
|
161
|
+
token0: EVMAddresses_1.EVMAddresses.randomAddress(),
|
|
162
|
+
token1: EVMAddresses_1.EVMAddresses.randomAddress(),
|
|
163
|
+
token0Multiplier: 1n,
|
|
164
|
+
token1Multiplier: 1n,
|
|
165
|
+
confirmations: 3n,
|
|
166
|
+
};
|
|
167
|
+
return new EVMSpvVaultData(EVMAddresses_1.EVMAddresses.randomAddress(), 0n, {
|
|
168
|
+
spvVaultParametersCommitment: getVaultParamsCommitment(spvVaultParams),
|
|
169
|
+
utxoTxHash: (0, ethers_1.randomBytes)(32),
|
|
170
|
+
utxoVout: 0n,
|
|
171
|
+
openBlockheight: 0n,
|
|
172
|
+
withdrawCount: 0n,
|
|
173
|
+
depositCount: 0n,
|
|
174
|
+
token0Amount: 0n,
|
|
175
|
+
token1Amount: 0n
|
|
176
|
+
}, spvVaultParams);
|
|
177
|
+
}
|
|
157
178
|
}
|
|
158
179
|
exports.EVMSpvVaultData = EVMSpvVaultData;
|
|
159
180
|
base_1.SpvVaultData.deserializers["EVM"] = EVMSpvVaultData;
|
|
@@ -182,11 +182,11 @@ export declare class EVMSwapContract<ChainId extends string = string> extends EV
|
|
|
182
182
|
getClaimFeeRate(signer: string, swapData: EVMSwapData): Promise<string>;
|
|
183
183
|
getClaimFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
184
184
|
/**
|
|
185
|
-
* Get the estimated
|
|
185
|
+
* Get the estimated fee of the commit transaction
|
|
186
186
|
*/
|
|
187
|
-
getCommitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
187
|
+
getCommitFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
188
188
|
/**
|
|
189
|
-
* Get the estimated
|
|
189
|
+
* Get the estimated transaction fee of the refund transaction
|
|
190
190
|
*/
|
|
191
|
-
getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
191
|
+
getRefundFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint>;
|
|
192
192
|
}
|
|
@@ -358,15 +358,15 @@ class EVMSwapContract extends EVMContractBase_1.EVMContractBase {
|
|
|
358
358
|
return this.Claim.getClaimFee(swapData, feeRate);
|
|
359
359
|
}
|
|
360
360
|
/**
|
|
361
|
-
* Get the estimated
|
|
361
|
+
* Get the estimated fee of the commit transaction
|
|
362
362
|
*/
|
|
363
|
-
getCommitFee(swapData, feeRate) {
|
|
363
|
+
getCommitFee(signer, swapData, feeRate) {
|
|
364
364
|
return this.Init.getInitFee(swapData, feeRate);
|
|
365
365
|
}
|
|
366
366
|
/**
|
|
367
|
-
* Get the estimated
|
|
367
|
+
* Get the estimated transaction fee of the refund transaction
|
|
368
368
|
*/
|
|
369
|
-
getRefundFee(swapData, feeRate) {
|
|
369
|
+
getRefundFee(signer, swapData, feeRate) {
|
|
370
370
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
371
371
|
}
|
|
372
372
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-evm",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.28",
|
|
4
4
|
"description": "EVM specific base implementation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types:": "./dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "adambor",
|
|
24
24
|
"license": "Apache-2.0",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@atomiqlabs/base": "^10.0.0-dev.
|
|
26
|
+
"@atomiqlabs/base": "^10.0.0-dev.2",
|
|
27
27
|
"@noble/hashes": "^1.8.0",
|
|
28
28
|
"@scure/btc-signer": "1.6.0",
|
|
29
29
|
"buffer": "6.0.3"
|
|
@@ -14,6 +14,7 @@ import {CitreaFees} from "./CitreaFees";
|
|
|
14
14
|
import {CitreaBtcRelay} from "./CitreaBtcRelay";
|
|
15
15
|
import {CitreaSwapContract} from "./CitreaSwapContract";
|
|
16
16
|
import {CitreaTokens} from "./CitreaTokens";
|
|
17
|
+
import {CitreaSpvVaultContract} from "./CitreaSpvVaultContract";
|
|
17
18
|
|
|
18
19
|
const CitreaChainIds = {
|
|
19
20
|
MAINNET: null,
|
|
@@ -147,7 +148,7 @@ export function initializeCitrea(
|
|
|
147
148
|
}
|
|
148
149
|
);
|
|
149
150
|
|
|
150
|
-
const spvVaultContract = new
|
|
151
|
+
const spvVaultContract = new CitreaSpvVaultContract(
|
|
151
152
|
chainInterface, btcRelay, bitcoinRpc, options.spvVaultContract ?? defaultContractAddresses.spvVaultContract,
|
|
152
153
|
options.spvVaultDeploymentHeight ?? defaultContractAddresses.spvVaultDeploymentHeight
|
|
153
154
|
)
|
|
@@ -1,18 +1,76 @@
|
|
|
1
1
|
import {EVMSpvVaultContract} from "../../evm/spv_swap/EVMSpvVaultContract";
|
|
2
2
|
import {EVMSpvWithdrawalData} from "../../evm/spv_swap/EVMSpvWithdrawalData";
|
|
3
|
-
import {
|
|
3
|
+
import {EVMSpvVaultData} from "../../evm/spv_swap/EVMSpvVaultData";
|
|
4
|
+
import {ZeroHash} from "ethers/lib.esm";
|
|
5
|
+
import {ZeroAddress} from "ethers";
|
|
6
|
+
import {CitreaFees} from "./CitreaFees";
|
|
7
|
+
import {EVMAddresses} from "../../evm/chain/modules/EVMAddresses";
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
export class CitreaSpvVaultContract extends EVMSpvVaultContract<"CITREA"> {
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
public static readonly StateDiffSize = {
|
|
13
|
+
BASE_DIFF_SIZE: 50,
|
|
14
|
+
ERC_20_TRANSFER_DIFF_SIZE: 50,
|
|
15
|
+
NATIVE_SELF_TRANSFER_DIFF_SIZE: 20,
|
|
16
|
+
NATIVE_TRANSFER_DIFF_SIZE: 30,
|
|
17
|
+
EXECUTION_SCHEDULE_DIFF_SIZE: 40
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
private calculateStateDiff(signer: string, tokenStateChanges: Set<string>): number {
|
|
21
|
+
let stateDiffSize = 0;
|
|
22
|
+
tokenStateChanges.forEach(val => {
|
|
23
|
+
const [address, token] = val.split(":");
|
|
24
|
+
if(token.toLowerCase()===this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
25
|
+
stateDiffSize += address.toLowerCase()===signer?.toLowerCase() ? CitreaSpvVaultContract.StateDiffSize.NATIVE_SELF_TRANSFER_DIFF_SIZE : CitreaSpvVaultContract.StateDiffSize.NATIVE_TRANSFER_DIFF_SIZE;
|
|
26
|
+
} else {
|
|
27
|
+
stateDiffSize += CitreaSpvVaultContract.StateDiffSize.ERC_20_TRANSFER_DIFF_SIZE;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return stateDiffSize;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getClaimFee(signer: string, vault?: EVMSpvVaultData, data?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
34
|
+
vault ??= EVMSpvVaultData.randomVault();
|
|
9
35
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
10
|
-
|
|
36
|
+
const tokenStateChanges: Set<string> = new Set();
|
|
37
|
+
|
|
38
|
+
let diffSize = CitreaSpvVaultContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
39
|
+
const recipient = data!=null ? data.recipient : EVMAddresses.randomAddress();
|
|
40
|
+
if (data==null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
41
|
+
tokenStateChanges.add(recipient.toLowerCase()+":"+vault.token0.token.toLowerCase());
|
|
42
|
+
if (data==null || data.frontingFeeRate > 0n) tokenStateChanges.add(ZeroAddress+":"+vault.token0.token.toLowerCase()); //Also needs to pay out to fronter
|
|
43
|
+
if (data==null || data.callerFeeRate > 0n) tokenStateChanges.add(signer+":"+vault.token0.token.toLowerCase()); //Also needs to pay out to caller
|
|
44
|
+
}
|
|
45
|
+
if (data==null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
46
|
+
tokenStateChanges.add(recipient.toLowerCase()+":"+vault.token1.token.toLowerCase());
|
|
47
|
+
if (data==null || data.frontingFeeRate > 0n) tokenStateChanges.add(ZeroAddress+":"+vault.token1.token.toLowerCase()); //Also needs to pay out to fronter
|
|
48
|
+
if (data==null || data.callerFeeRate > 0n) tokenStateChanges.add(signer+":"+vault.token1.token.toLowerCase()); //Also needs to pay out to caller
|
|
49
|
+
}
|
|
50
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
51
|
+
if (data==null || (data.executionHash != null && data.executionHash !== ZeroHash)) diffSize += CitreaSpvVaultContract.StateDiffSize.EXECUTION_SCHEDULE_DIFF_SIZE;
|
|
52
|
+
|
|
53
|
+
const gasFee = await super.getClaimFee(signer, vault, data, feeRate);
|
|
54
|
+
return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
11
55
|
}
|
|
12
56
|
|
|
13
|
-
async getFrontFee(signer: string,
|
|
57
|
+
async getFrontFee(signer: string, vault?: EVMSpvVaultData, data?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
58
|
+
vault ??= EVMSpvVaultData.randomVault();
|
|
14
59
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
15
|
-
|
|
60
|
+
const tokenStateChanges: Set<string> = new Set();
|
|
61
|
+
|
|
62
|
+
let diffSize = CitreaSpvVaultContract.StateDiffSize.BASE_DIFF_SIZE;
|
|
63
|
+
if (data==null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
64
|
+
tokenStateChanges.add(signer+":"+vault.token0.token.toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
if (data==null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
67
|
+
tokenStateChanges.add(signer+":"+vault.token1.token.toLowerCase());
|
|
68
|
+
}
|
|
69
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
70
|
+
if (data==null || (data.executionHash != null && data.executionHash !== ZeroHash)) diffSize += CitreaSpvVaultContract.StateDiffSize.EXECUTION_SCHEDULE_DIFF_SIZE;
|
|
71
|
+
|
|
72
|
+
const gasFee = await super.getFrontFee(signer, vault, data, feeRate);
|
|
73
|
+
return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
16
74
|
}
|
|
17
75
|
|
|
18
76
|
}
|
|
@@ -29,7 +29,7 @@ export class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
|
|
|
29
29
|
/**
|
|
30
30
|
* Get the estimated solana fee of the commit transaction
|
|
31
31
|
*/
|
|
32
|
-
async getCommitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
32
|
+
async getCommitFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
33
33
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
34
34
|
|
|
35
35
|
const tokenStateChanges: Set<string> = new Set();
|
|
@@ -40,11 +40,10 @@ export class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
|
|
|
40
40
|
} else {
|
|
41
41
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getToken().toLowerCase());
|
|
42
42
|
}
|
|
43
|
-
//TODO: Since we don't know who is sending the transaction, we calculate with the worst case
|
|
44
43
|
if(swapData.getTotalDeposit()>0n) {
|
|
45
|
-
tokenStateChanges.add(
|
|
44
|
+
tokenStateChanges.add(signer.toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
|
|
46
45
|
}
|
|
47
|
-
diffSize += this.calculateStateDiff(
|
|
46
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
48
47
|
|
|
49
48
|
const gasFee = await this.Init.getInitFee(swapData, feeRate);
|
|
50
49
|
return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
@@ -77,7 +76,7 @@ export class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
|
|
|
77
76
|
/**
|
|
78
77
|
* Get the estimated solana transaction fee of the refund transaction
|
|
79
78
|
*/
|
|
80
|
-
async getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
79
|
+
async getRefundFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
81
80
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
82
81
|
|
|
83
82
|
const tokenStateChanges: Set<string> = new Set();
|
|
@@ -89,14 +88,13 @@ export class CitreaSwapContract extends EVMSwapContract<"CITREA"> {
|
|
|
89
88
|
} else {
|
|
90
89
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getToken().toLowerCase());
|
|
91
90
|
}
|
|
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
91
|
if(swapData.getSecurityDeposit() > 0) {
|
|
94
92
|
tokenStateChanges.add(swapData.getOfferer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
|
|
95
93
|
}
|
|
96
94
|
if(swapData.getClaimerBounty() > swapData.getSecurityDeposit()) {
|
|
97
95
|
tokenStateChanges.add(swapData.getClaimer().toLowerCase()+":"+swapData.getDepositToken().toLowerCase());
|
|
98
96
|
}
|
|
99
|
-
diffSize += this.calculateStateDiff(
|
|
97
|
+
diffSize += this.calculateStateDiff(signer, tokenStateChanges);
|
|
100
98
|
|
|
101
99
|
const gasFee = await this.Refund.getRefundFee(swapData, feeRate);
|
|
102
100
|
return gasFee + CitreaFees.getGasFee(0, feeRate, diffSize);
|
|
@@ -100,8 +100,7 @@ export class EVMChainInterface<ChainId extends string = string, EVMChainId exten
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
randomAddress(): string {
|
|
103
|
-
|
|
104
|
-
return wallet.address;
|
|
103
|
+
return EVMAddresses.randomAddress();
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
randomSigner(): EVMSigner {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {EVMModule} from "../EVMModule";
|
|
2
2
|
import {isAddress} from "ethers";
|
|
3
|
+
import {Wallet} from "ethers/lib.esm";
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
export class EVMAddresses extends EVMModule<any> {
|
|
@@ -21,4 +22,9 @@ export class EVMAddresses extends EVMModule<any> {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
static randomAddress(): string {
|
|
26
|
+
const wallet = Wallet.createRandom();
|
|
27
|
+
return wallet.address;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
}
|
|
@@ -55,10 +55,20 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
55
55
|
>
|
|
56
56
|
{
|
|
57
57
|
public static readonly GasCosts = {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
DEPOSIT_BASE: 15_000 + 21_000,
|
|
59
|
+
DEPOSIT_ERC20: 40_000,
|
|
60
|
+
|
|
61
|
+
OPEN: 80_000 + 21_000,
|
|
62
|
+
|
|
63
|
+
CLAIM_BASE: 85_000 + 21_000,
|
|
64
|
+
CLAIM_NATIVE_TRANSFER: 7_500,
|
|
65
|
+
CLAIM_ERC20_TRANSFER: 40_000,
|
|
66
|
+
CLAIM_EXECUTION_SCHEDULE: 30_000,
|
|
67
|
+
|
|
68
|
+
FRONT_BASE: 75_000 + 21_000,
|
|
69
|
+
FRONT_NATIVE_TRANSFER: 7_500,
|
|
70
|
+
FRONT_ERC20_TRANSFER: 40_000,
|
|
71
|
+
FRONT_EXECUTION_SCHEDULE: 30_000
|
|
62
72
|
};
|
|
63
73
|
|
|
64
74
|
readonly chainId: ChainId;
|
|
@@ -96,18 +106,26 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
protected async Deposit(signer: string, vault: EVMSpvVaultData, rawAmounts: bigint[], feeRate: string): Promise<TransactionRequest> {
|
|
109
|
+
let totalGas = EVMSpvVaultContract.GasCosts.DEPOSIT_BASE;
|
|
99
110
|
let value = 0n;
|
|
100
|
-
if(vault.token0.token.toLowerCase()===this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
111
|
+
if(vault.token0.token.toLowerCase()===this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
101
112
|
value += rawAmounts[0] * vault.token0.multiplier;
|
|
102
|
-
|
|
113
|
+
} else {
|
|
114
|
+
if(rawAmounts[0] > 0n) totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
115
|
+
}
|
|
116
|
+
if(vault.token1.token.toLowerCase()===this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
103
117
|
value += (rawAmounts[1] ?? 0n) * vault.token1.multiplier;
|
|
118
|
+
} else {
|
|
119
|
+
if(rawAmounts[1]!=null && rawAmounts[1] > 0n && vault.token0.token.toLowerCase()!==vault.token1.token.toLowerCase())
|
|
120
|
+
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
121
|
+
}
|
|
104
122
|
|
|
105
123
|
const tx = await this.contract.deposit.populateTransaction(
|
|
106
124
|
vault.owner, vault.vaultId, vault.getVaultParamsStruct(),
|
|
107
125
|
rawAmounts[0], rawAmounts[1] ?? 0n, { value }
|
|
108
126
|
);
|
|
109
127
|
tx.from = signer;
|
|
110
|
-
EVMFees.applyFeeRate(tx,
|
|
128
|
+
EVMFees.applyFeeRate(tx, totalGas, feeRate);
|
|
111
129
|
|
|
112
130
|
return tx;
|
|
113
131
|
}
|
|
@@ -128,7 +146,7 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
128
146
|
{ value }
|
|
129
147
|
);
|
|
130
148
|
tx.from = signer;
|
|
131
|
-
EVMFees.applyFeeRate(tx,
|
|
149
|
+
EVMFees.applyFeeRate(tx, this.getFrontGas(signer, vault, data), feeRate);
|
|
132
150
|
|
|
133
151
|
return tx;
|
|
134
152
|
}
|
|
@@ -143,7 +161,7 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
143
161
|
)
|
|
144
162
|
|
|
145
163
|
tx.from = signer;
|
|
146
|
-
EVMFees.applyFeeRate(tx,
|
|
164
|
+
EVMFees.applyFeeRate(tx, this.getClaimGas(signer, vault, data), feeRate);
|
|
147
165
|
|
|
148
166
|
return tx;
|
|
149
167
|
}
|
|
@@ -520,14 +538,66 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
520
538
|
return [tx];
|
|
521
539
|
}
|
|
522
540
|
|
|
523
|
-
|
|
541
|
+
getClaimGas(signer: string, vault: EVMSpvVaultData, data?: EVMSpvWithdrawalData): number {
|
|
542
|
+
let totalGas = EVMSpvVaultContract.GasCosts.CLAIM_BASE;
|
|
543
|
+
|
|
544
|
+
if (data==null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
545
|
+
const transferFee = vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
546
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
547
|
+
totalGas += transferFee;
|
|
548
|
+
if (data==null || data.frontingFeeRate > 0n) totalGas += transferFee; //Also needs to pay out to fronter
|
|
549
|
+
if (data==null || (data.callerFeeRate > 0n && !data.isRecipient(signer))) totalGas += transferFee; //Also needs to pay out to caller
|
|
550
|
+
}
|
|
551
|
+
if (data==null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
552
|
+
const transferFee = vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
553
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
554
|
+
totalGas += transferFee;
|
|
555
|
+
if (data==null || data.frontingFeeRate > 0n) totalGas += transferFee; //Also needs to pay out to fronter
|
|
556
|
+
if (data==null || (data.callerFeeRate > 0n && !data.isRecipient(signer))) totalGas += transferFee; //Also needs to pay out to caller
|
|
557
|
+
}
|
|
558
|
+
if (data==null || (data.executionHash != null && data.executionHash !== ZeroHash)) totalGas += EVMSpvVaultContract.GasCosts.CLAIM_EXECUTION_SCHEDULE;
|
|
559
|
+
|
|
560
|
+
return totalGas;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
getFrontGas(signer: string, vault: EVMSpvVaultData, data?: EVMSpvWithdrawalData): number {
|
|
564
|
+
let totalGas = EVMSpvVaultContract.GasCosts.FRONT_BASE;
|
|
565
|
+
|
|
566
|
+
if (data==null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
567
|
+
totalGas += vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
568
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
569
|
+
}
|
|
570
|
+
if (data==null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
571
|
+
totalGas += vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
572
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
573
|
+
}
|
|
574
|
+
if (data==null || (data.executionHash != null && data.executionHash !== ZeroHash)) totalGas += EVMSpvVaultContract.GasCosts.FRONT_EXECUTION_SCHEDULE;
|
|
575
|
+
|
|
576
|
+
return totalGas;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async getClaimFee(signer: string, vault: EVMSpvVaultData, withdrawalData: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
524
580
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
525
|
-
return EVMFees.getGasFee(
|
|
581
|
+
return EVMFees.getGasFee(this.getClaimGas(signer, vault, withdrawalData), feeRate);
|
|
526
582
|
}
|
|
527
583
|
|
|
528
|
-
async getFrontFee(signer: string, withdrawalData
|
|
584
|
+
async getFrontFee(signer: string, vault?: EVMSpvVaultData, withdrawalData?: EVMSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
585
|
+
vault ??= EVMSpvVaultData.randomVault();
|
|
529
586
|
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
530
|
-
|
|
587
|
+
let totalFee = EVMFees.getGasFee(this.getFrontGas(signer, vault, withdrawalData), feeRate);
|
|
588
|
+
if(withdrawalData==null || (withdrawalData.rawAmounts[0]!=null && withdrawalData.rawAmounts[0]>0n)) {
|
|
589
|
+
if(vault.token0.token.toLowerCase()!==this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
590
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if(withdrawalData==null || (withdrawalData.rawAmounts[1]!=null && withdrawalData.rawAmounts[1]>0n)) {
|
|
594
|
+
if(vault.token1.token.toLowerCase()!==this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
595
|
+
if(vault.token1.token.toLowerCase()!==vault.token0.token.toLowerCase() || withdrawalData==null || withdrawalData.rawAmounts[0]==null || withdrawalData.rawAmounts[0]===0n) {
|
|
596
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return totalFee;
|
|
531
601
|
}
|
|
532
602
|
|
|
533
603
|
}
|
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
SpvVaultParametersStruct,
|
|
12
12
|
SpvVaultStateStruct
|
|
13
13
|
} from "./SpvVaultContractTypechain";
|
|
14
|
-
import {hexlify} from "ethers";
|
|
14
|
+
import {hexlify, randomBytes} from "ethers";
|
|
15
15
|
import {AbiCoder, keccak256} from "ethers";
|
|
16
|
+
import {EVMAddresses} from "../chain/modules/EVMAddresses";
|
|
17
|
+
import type {AddressLike, BigNumberish, BytesLike} from "ethers/lib.esm";
|
|
16
18
|
|
|
17
19
|
export function getVaultParamsCommitment(vaultParams: SpvVaultParametersStruct) {
|
|
18
20
|
return keccak256(AbiCoder.defaultAbiCoder().encode(
|
|
@@ -196,6 +198,27 @@ export class EVMSpvVaultData extends SpvVaultData<EVMSpvWithdrawalData> {
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
static randomVault(): EVMSpvVaultData {
|
|
202
|
+
const spvVaultParams = {
|
|
203
|
+
btcRelayContract: EVMAddresses.randomAddress(),
|
|
204
|
+
token0: EVMAddresses.randomAddress(),
|
|
205
|
+
token1: EVMAddresses.randomAddress(),
|
|
206
|
+
token0Multiplier: 1n,
|
|
207
|
+
token1Multiplier: 1n,
|
|
208
|
+
confirmations: 3n,
|
|
209
|
+
}
|
|
210
|
+
return new EVMSpvVaultData(EVMAddresses.randomAddress(), 0n, {
|
|
211
|
+
spvVaultParametersCommitment: getVaultParamsCommitment(spvVaultParams),
|
|
212
|
+
utxoTxHash: randomBytes(32),
|
|
213
|
+
utxoVout: 0n,
|
|
214
|
+
openBlockheight: 0n,
|
|
215
|
+
withdrawCount: 0n,
|
|
216
|
+
depositCount: 0n,
|
|
217
|
+
token0Amount: 0n,
|
|
218
|
+
token1Amount: 0n
|
|
219
|
+
}, spvVaultParams);
|
|
220
|
+
}
|
|
221
|
+
|
|
199
222
|
}
|
|
200
223
|
|
|
201
224
|
SpvVaultData.deserializers["EVM"] = EVMSpvVaultData;
|
|
@@ -574,16 +574,16 @@ export class EVMSwapContract<ChainId extends string = string>
|
|
|
574
574
|
}
|
|
575
575
|
|
|
576
576
|
/**
|
|
577
|
-
* Get the estimated
|
|
577
|
+
* Get the estimated fee of the commit transaction
|
|
578
578
|
*/
|
|
579
|
-
getCommitFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
579
|
+
getCommitFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
580
580
|
return this.Init.getInitFee(swapData, feeRate);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
/**
|
|
584
|
-
* Get the estimated
|
|
584
|
+
* Get the estimated transaction fee of the refund transaction
|
|
585
585
|
*/
|
|
586
|
-
getRefundFee(swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
586
|
+
getRefundFee(signer: string, swapData: EVMSwapData, feeRate?: string): Promise<bigint> {
|
|
587
587
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
588
588
|
}
|
|
589
589
|
|