@atomiqlabs/chain-evm 1.0.0-dev.75 → 1.0.0-dev.77
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/LICENSE +201 -201
- package/dist/chains/botanix/BotanixChainType.d.ts +13 -13
- package/dist/chains/botanix/BotanixChainType.js +2 -2
- package/dist/chains/botanix/BotanixInitializer.d.ts +30 -30
- package/dist/chains/botanix/BotanixInitializer.js +122 -122
- package/dist/chains/citrea/CitreaBtcRelay.d.ts +21 -21
- package/dist/chains/citrea/CitreaBtcRelay.js +43 -43
- package/dist/chains/citrea/CitreaChainType.d.ts +13 -13
- package/dist/chains/citrea/CitreaChainType.js +2 -2
- package/dist/chains/citrea/CitreaFees.d.ts +29 -29
- package/dist/chains/citrea/CitreaFees.js +67 -67
- package/dist/chains/citrea/CitreaInitializer.d.ts +30 -30
- package/dist/chains/citrea/CitreaInitializer.js +129 -129
- package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +15 -15
- package/dist/chains/citrea/CitreaSpvVaultContract.js +74 -74
- package/dist/chains/citrea/CitreaSwapContract.d.ts +22 -22
- package/dist/chains/citrea/CitreaSwapContract.js +96 -96
- package/dist/chains/citrea/CitreaTokens.d.ts +9 -9
- package/dist/chains/citrea/CitreaTokens.js +20 -20
- package/dist/evm/btcrelay/BtcRelayAbi.d.ts +198 -198
- package/dist/evm/btcrelay/BtcRelayAbi.js +261 -261
- package/dist/evm/btcrelay/BtcRelayTypechain.d.ts +172 -172
- package/dist/evm/btcrelay/BtcRelayTypechain.js +2 -2
- package/dist/evm/btcrelay/EVMBtcRelay.d.ts +197 -197
- package/dist/evm/btcrelay/EVMBtcRelay.js +435 -435
- package/dist/evm/btcrelay/headers/EVMBtcHeader.d.ts +33 -33
- package/dist/evm/btcrelay/headers/EVMBtcHeader.js +84 -84
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.d.ts +56 -56
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.js +123 -123
- package/dist/evm/chain/EVMChainInterface.d.ts +51 -51
- package/dist/evm/chain/EVMChainInterface.js +89 -89
- package/dist/evm/chain/EVMModule.d.ts +9 -9
- package/dist/evm/chain/EVMModule.js +13 -13
- package/dist/evm/chain/modules/ERC20Abi.d.ts +168 -168
- package/dist/evm/chain/modules/ERC20Abi.js +225 -225
- package/dist/evm/chain/modules/EVMAddresses.d.ts +10 -10
- package/dist/evm/chain/modules/EVMAddresses.js +30 -30
- package/dist/evm/chain/modules/EVMBlocks.d.ts +20 -20
- package/dist/evm/chain/modules/EVMBlocks.js +64 -64
- package/dist/evm/chain/modules/EVMEvents.d.ts +36 -36
- package/dist/evm/chain/modules/EVMEvents.js +122 -122
- package/dist/evm/chain/modules/EVMFees.d.ts +36 -36
- package/dist/evm/chain/modules/EVMFees.js +74 -74
- package/dist/evm/chain/modules/EVMSignatures.d.ts +29 -29
- package/dist/evm/chain/modules/EVMSignatures.js +68 -68
- package/dist/evm/chain/modules/EVMTokens.d.ts +70 -70
- package/dist/evm/chain/modules/EVMTokens.js +142 -142
- package/dist/evm/chain/modules/EVMTransactions.d.ts +94 -94
- package/dist/evm/chain/modules/EVMTransactions.js +286 -286
- package/dist/evm/contract/EVMContractBase.d.ts +22 -22
- package/dist/evm/contract/EVMContractBase.js +34 -34
- package/dist/evm/contract/EVMContractModule.d.ts +8 -8
- package/dist/evm/contract/EVMContractModule.js +11 -11
- package/dist/evm/contract/modules/EVMContractEvents.d.ts +42 -42
- package/dist/evm/contract/modules/EVMContractEvents.js +75 -75
- package/dist/evm/events/EVMChainEvents.d.ts +22 -22
- package/dist/evm/events/EVMChainEvents.js +69 -69
- package/dist/evm/events/EVMChainEventsBrowser.d.ts +102 -102
- package/dist/evm/events/EVMChainEventsBrowser.js +413 -404
- package/dist/evm/providers/JsonRpcProviderWithRetries.d.ts +15 -15
- package/dist/evm/providers/JsonRpcProviderWithRetries.js +19 -19
- package/dist/evm/providers/ReconnectingWebSocketProvider.d.ts +22 -22
- package/dist/evm/providers/ReconnectingWebSocketProvider.js +87 -87
- package/dist/evm/providers/SocketProvider.d.ts +111 -111
- package/dist/evm/providers/SocketProvider.js +334 -334
- package/dist/evm/providers/WebSocketProviderWithRetries.d.ts +17 -17
- package/dist/evm/providers/WebSocketProviderWithRetries.js +19 -19
- package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +79 -79
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +482 -482
- package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +39 -39
- package/dist/evm/spv_swap/EVMSpvVaultData.js +0 -180
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.d.ts +19 -19
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.js +55 -55
- package/dist/evm/spv_swap/SpvVaultContractAbi.d.ts +91 -91
- package/dist/evm/spv_swap/SpvVaultContractAbi.js +849 -849
- package/dist/evm/spv_swap/SpvVaultContractTypechain.d.ts +450 -450
- package/dist/evm/spv_swap/SpvVaultContractTypechain.js +2 -2
- package/dist/evm/swaps/EVMSwapContract.d.ts +193 -193
- package/dist/evm/swaps/EVMSwapContract.js +378 -378
- package/dist/evm/swaps/EVMSwapData.d.ts +66 -66
- package/dist/evm/swaps/EVMSwapData.js +260 -260
- package/dist/evm/swaps/EVMSwapModule.d.ts +9 -9
- package/dist/evm/swaps/EVMSwapModule.js +11 -11
- package/dist/evm/swaps/EscrowManagerAbi.d.ts +120 -120
- package/dist/evm/swaps/EscrowManagerAbi.js +985 -985
- package/dist/evm/swaps/EscrowManagerTypechain.d.ts +475 -475
- package/dist/evm/swaps/EscrowManagerTypechain.js +2 -2
- package/dist/evm/swaps/handlers/IHandler.d.ts +13 -13
- package/dist/evm/swaps/handlers/IHandler.js +2 -2
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.d.ts +10 -10
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.js +13 -13
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.d.ts +20 -20
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.js +39 -39
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +59 -59
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +51 -51
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +21 -21
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +28 -28
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +48 -48
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +63 -63
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.js +28 -28
- package/dist/evm/swaps/modules/EVMLpVault.d.ts +69 -69
- package/dist/evm/swaps/modules/EVMLpVault.js +134 -134
- package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +54 -54
- package/dist/evm/swaps/modules/EVMSwapClaim.js +137 -137
- package/dist/evm/swaps/modules/EVMSwapInit.d.ts +88 -88
- package/dist/evm/swaps/modules/EVMSwapInit.js +274 -274
- package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +62 -62
- package/dist/evm/swaps/modules/EVMSwapRefund.js +167 -167
- package/dist/evm/typechain/common.d.ts +50 -50
- package/dist/evm/typechain/common.js +2 -2
- package/dist/evm/wallet/EVMBrowserSigner.d.ts +5 -5
- package/dist/evm/wallet/EVMBrowserSigner.js +11 -11
- package/dist/evm/wallet/EVMPersistentSigner.d.ts +29 -29
- package/dist/evm/wallet/EVMPersistentSigner.js +222 -222
- package/dist/evm/wallet/EVMSigner.d.ts +11 -11
- package/dist/evm/wallet/EVMSigner.js +24 -24
- package/dist/index.d.ts +44 -44
- package/dist/index.js +60 -60
- package/dist/utils/Utils.d.ts +17 -17
- package/dist/utils/Utils.js +81 -81
- package/package.json +39 -39
- package/src/chains/botanix/BotanixChainType.ts +28 -28
- package/src/chains/botanix/BotanixInitializer.ts +171 -171
- package/src/chains/citrea/CitreaBtcRelay.ts +57 -57
- package/src/chains/citrea/CitreaChainType.ts +28 -28
- package/src/chains/citrea/CitreaFees.ts +77 -77
- package/src/chains/citrea/CitreaInitializer.ts +178 -178
- package/src/chains/citrea/CitreaSpvVaultContract.ts +75 -75
- package/src/chains/citrea/CitreaSwapContract.ts +102 -102
- package/src/chains/citrea/CitreaTokens.ts +21 -21
- package/src/evm/btcrelay/BtcRelayAbi.ts +258 -258
- package/src/evm/btcrelay/BtcRelayTypechain.ts +371 -371
- package/src/evm/btcrelay/EVMBtcRelay.ts +537 -537
- package/src/evm/btcrelay/headers/EVMBtcHeader.ts +109 -109
- package/src/evm/btcrelay/headers/EVMBtcStoredHeader.ts +152 -152
- package/src/evm/chain/EVMChainInterface.ts +155 -155
- package/src/evm/chain/EVMModule.ts +21 -21
- package/src/evm/chain/modules/ERC20Abi.ts +222 -222
- package/src/evm/chain/modules/EVMAddresses.ts +28 -28
- package/src/evm/chain/modules/EVMBlocks.ts +75 -75
- package/src/evm/chain/modules/EVMEvents.ts +139 -139
- package/src/evm/chain/modules/EVMFees.ts +104 -104
- package/src/evm/chain/modules/EVMSignatures.ts +76 -76
- package/src/evm/chain/modules/EVMTokens.ts +155 -155
- package/src/evm/chain/modules/EVMTransactions.ts +325 -325
- package/src/evm/contract/EVMContractBase.ts +63 -63
- package/src/evm/contract/EVMContractModule.ts +16 -16
- package/src/evm/contract/modules/EVMContractEvents.ts +102 -102
- package/src/evm/events/EVMChainEvents.ts +82 -82
- package/src/evm/events/EVMChainEventsBrowser.ts +534 -525
- package/src/evm/providers/JsonRpcProviderWithRetries.ts +24 -24
- package/src/evm/providers/ReconnectingWebSocketProvider.ts +101 -101
- package/src/evm/providers/SocketProvider.ts +368 -368
- package/src/evm/providers/WebSocketProviderWithRetries.ts +27 -27
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +615 -615
- package/src/evm/spv_swap/EVMSpvVaultData.ts +224 -224
- package/src/evm/spv_swap/EVMSpvWithdrawalData.ts +70 -70
- package/src/evm/spv_swap/SpvVaultContractAbi.ts +846 -846
- package/src/evm/spv_swap/SpvVaultContractTypechain.ts +685 -685
- package/src/evm/swaps/EVMSwapContract.ts +600 -600
- package/src/evm/swaps/EVMSwapData.ts +378 -378
- package/src/evm/swaps/EVMSwapModule.ts +16 -16
- package/src/evm/swaps/EscrowManagerAbi.ts +982 -982
- package/src/evm/swaps/EscrowManagerTypechain.ts +723 -723
- package/src/evm/swaps/handlers/IHandler.ts +17 -17
- package/src/evm/swaps/handlers/claim/ClaimHandlers.ts +20 -20
- package/src/evm/swaps/handlers/claim/HashlockClaimHandler.ts +46 -46
- package/src/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +82 -82
- package/src/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +76 -76
- package/src/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +46 -46
- package/src/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +115 -115
- package/src/evm/swaps/handlers/refund/TimelockRefundHandler.ts +37 -37
- package/src/evm/swaps/modules/EVMLpVault.ts +154 -154
- package/src/evm/swaps/modules/EVMSwapClaim.ts +172 -172
- package/src/evm/swaps/modules/EVMSwapInit.ts +328 -328
- package/src/evm/swaps/modules/EVMSwapRefund.ts +229 -229
- package/src/evm/typechain/common.ts +131 -131
- package/src/evm/wallet/EVMBrowserSigner.ts +11 -11
- package/src/evm/wallet/EVMPersistentSigner.ts +298 -298
- package/src/evm/wallet/EVMSigner.ts +31 -31
- package/src/index.ts +53 -53
- package/src/utils/Utils.ts +92 -92
|
@@ -1,482 +1,482 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EVMSpvVaultContract = exports.unpackOwnerAndVaultId = exports.packOwnerAndVaultId = void 0;
|
|
4
|
-
const base_1 = require("@atomiqlabs/base");
|
|
5
|
-
const buffer_1 = require("buffer");
|
|
6
|
-
const EVMContractBase_1 = require("../contract/EVMContractBase");
|
|
7
|
-
const SpvVaultContractAbi_1 = require("./SpvVaultContractAbi");
|
|
8
|
-
const EVMBtcRelay_1 = require("../btcrelay/EVMBtcRelay");
|
|
9
|
-
const Utils_1 = require("../../utils/Utils");
|
|
10
|
-
const ethers_1 = require("ethers");
|
|
11
|
-
const EVMAddresses_1 = require("../chain/modules/EVMAddresses");
|
|
12
|
-
const EVMSpvVaultData_1 = require("./EVMSpvVaultData");
|
|
13
|
-
const EVMSpvWithdrawalData_1 = require("./EVMSpvWithdrawalData");
|
|
14
|
-
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
15
|
-
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
16
|
-
function decodeUtxo(utxo) {
|
|
17
|
-
const [txId, vout] = utxo.split(":");
|
|
18
|
-
return {
|
|
19
|
-
txHash: "0x" + buffer_1.Buffer.from(txId, "hex").reverse().toString("hex"),
|
|
20
|
-
vout: BigInt(vout)
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function packOwnerAndVaultId(owner, vaultId) {
|
|
24
|
-
if (owner.length !== 42)
|
|
25
|
-
throw new Error("Invalid owner address");
|
|
26
|
-
return owner.toLowerCase() + base_1.BigIntBufferUtils.toBuffer(vaultId, "be", 12).toString("hex");
|
|
27
|
-
}
|
|
28
|
-
exports.packOwnerAndVaultId = packOwnerAndVaultId;
|
|
29
|
-
function unpackOwnerAndVaultId(data) {
|
|
30
|
-
return [(0, ethers_1.getAddress)(data.substring(0, 42)), BigInt("0x" + data.substring(42, 66))];
|
|
31
|
-
}
|
|
32
|
-
exports.unpackOwnerAndVaultId = unpackOwnerAndVaultId;
|
|
33
|
-
class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
34
|
-
constructor(chainInterface, btcRelay, bitcoinRpc, contractAddress, contractDeploymentHeight) {
|
|
35
|
-
super(chainInterface, contractAddress, SpvVaultContractAbi_1.SpvVaultContractAbi, contractDeploymentHeight);
|
|
36
|
-
this.claimTimeout = 180;
|
|
37
|
-
this.logger = (0, Utils_1.getLogger)("EVMSpvVaultContract: ");
|
|
38
|
-
this.vaultParamsCache = new promise_cache_ts_1.PromiseLruCache(5000);
|
|
39
|
-
this.btcRelay = btcRelay;
|
|
40
|
-
this.bitcoinRpc = bitcoinRpc;
|
|
41
|
-
}
|
|
42
|
-
//Transactions
|
|
43
|
-
async Open(signer, vault, feeRate) {
|
|
44
|
-
const { txHash, vout } = decodeUtxo(vault.getUtxo());
|
|
45
|
-
const tokens = vault.getTokenData();
|
|
46
|
-
if (tokens.length !== 2)
|
|
47
|
-
throw new Error("Must specify exactly 2 tokens for vault!");
|
|
48
|
-
const tx = await this.contract.open.populateTransaction(vault.vaultId, vault.getVaultParamsStruct(), txHash, vout);
|
|
49
|
-
tx.from = signer;
|
|
50
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, EVMSpvVaultContract.GasCosts.OPEN, feeRate);
|
|
51
|
-
return tx;
|
|
52
|
-
}
|
|
53
|
-
async Deposit(signer, vault, rawAmounts, feeRate) {
|
|
54
|
-
let totalGas = EVMSpvVaultContract.GasCosts.DEPOSIT_BASE;
|
|
55
|
-
let value = 0n;
|
|
56
|
-
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
57
|
-
value += rawAmounts[0] * vault.token0.multiplier;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
if (rawAmounts[0] > 0n)
|
|
61
|
-
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
62
|
-
}
|
|
63
|
-
if (vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
64
|
-
value += (rawAmounts[1] ?? 0n) * vault.token1.multiplier;
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
if (rawAmounts[1] != null && rawAmounts[1] > 0n && vault.token0.token.toLowerCase() !== vault.token1.token.toLowerCase())
|
|
68
|
-
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
69
|
-
}
|
|
70
|
-
const tx = await this.contract.deposit.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), rawAmounts[0], rawAmounts[1] ?? 0n, { value });
|
|
71
|
-
tx.from = signer;
|
|
72
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, totalGas, feeRate);
|
|
73
|
-
return tx;
|
|
74
|
-
}
|
|
75
|
-
async Front(signer, vault, data, withdrawalSequence, feeRate) {
|
|
76
|
-
let value = 0n;
|
|
77
|
-
const frontingAmount = data.getFrontingAmount();
|
|
78
|
-
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
79
|
-
value += frontingAmount[0] * vault.token0.multiplier;
|
|
80
|
-
if (vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
81
|
-
value += (frontingAmount[1] ?? 0n) * vault.token1.multiplier;
|
|
82
|
-
const tx = await this.contract.front.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), withdrawalSequence, data.getTxHash(), data.serializeToStruct(), { value });
|
|
83
|
-
tx.from = signer;
|
|
84
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, this.getFrontGas(signer, vault, data), feeRate);
|
|
85
|
-
return tx;
|
|
86
|
-
}
|
|
87
|
-
async Claim(signer, vault, data, blockheader, merkle, position, feeRate) {
|
|
88
|
-
const tx = await this.contract.claim.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), "0x" + data.btcTx.hex, blockheader.serializeToStruct(), merkle, position);
|
|
89
|
-
tx.from = signer;
|
|
90
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, this.getClaimGas(signer, vault, data), feeRate);
|
|
91
|
-
return tx;
|
|
92
|
-
}
|
|
93
|
-
async checkWithdrawalTx(tx) {
|
|
94
|
-
const result = await this.contract.parseBitcoinTx(buffer_1.Buffer.from(tx.btcTx.hex, "hex"));
|
|
95
|
-
if (result == null)
|
|
96
|
-
throw new Error("Failed to parse transaction!");
|
|
97
|
-
}
|
|
98
|
-
createVaultData(owner, vaultId, utxo, confirmations, tokenData) {
|
|
99
|
-
if (tokenData.length !== 2)
|
|
100
|
-
throw new Error("Must specify 2 tokens in tokenData!");
|
|
101
|
-
const vaultParams = {
|
|
102
|
-
btcRelayContract: this.btcRelay.contractAddress,
|
|
103
|
-
token0: tokenData[0].token,
|
|
104
|
-
token1: tokenData[1].token,
|
|
105
|
-
token0Multiplier: tokenData[0].multiplier,
|
|
106
|
-
token1Multiplier: tokenData[1].multiplier,
|
|
107
|
-
confirmations: BigInt(confirmations)
|
|
108
|
-
};
|
|
109
|
-
const spvVaultParametersCommitment = (0, ethers_1.keccak256)(ethers_1.AbiCoder.defaultAbiCoder().encode(["address", "address", "address", "uint192", "uint192", "uint256"], [vaultParams.btcRelayContract, vaultParams.token0, vaultParams.token1, vaultParams.token0Multiplier, vaultParams.token1Multiplier, vaultParams.confirmations]));
|
|
110
|
-
return Promise.resolve(new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, {
|
|
111
|
-
spvVaultParametersCommitment,
|
|
112
|
-
utxoTxHash: ethers_1.ZeroHash,
|
|
113
|
-
utxoVout: 0n,
|
|
114
|
-
openBlockheight: 0n,
|
|
115
|
-
withdrawCount: 0n,
|
|
116
|
-
depositCount: 0n,
|
|
117
|
-
token0Amount: 0n,
|
|
118
|
-
token1Amount: 0n
|
|
119
|
-
}, vaultParams, utxo));
|
|
120
|
-
}
|
|
121
|
-
//Getters
|
|
122
|
-
async getFronterAddress(owner, vaultId, withdrawal) {
|
|
123
|
-
const frontingAddress = await this.contract.getFronterById(owner, vaultId, "0x" + withdrawal.getFrontingId());
|
|
124
|
-
if (frontingAddress === ethers_1.ZeroAddress)
|
|
125
|
-
return null;
|
|
126
|
-
return frontingAddress;
|
|
127
|
-
}
|
|
128
|
-
async getVaultData(owner, vaultId) {
|
|
129
|
-
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
130
|
-
const vaultParams = await this.vaultParamsCache.getOrComputeAsync(vaultState.spvVaultParametersCommitment, async () => {
|
|
131
|
-
const blockheight = Number(vaultState.openBlockheight);
|
|
132
|
-
const events = await this.Events.getContractBlockEvents(["Opened"], [
|
|
133
|
-
"0x" + owner.substring(2).padStart(64, "0"),
|
|
134
|
-
(0, ethers_1.hexlify)(base_1.BigIntBufferUtils.toBuffer(vaultId, "be", 32))
|
|
135
|
-
], blockheight);
|
|
136
|
-
const foundEvent = events.find(event => (0, EVMSpvVaultData_1.getVaultParamsCommitment)(event.args.params) === vaultState.spvVaultParametersCommitment);
|
|
137
|
-
if (foundEvent == null)
|
|
138
|
-
throw new Error("Valid open event not found!");
|
|
139
|
-
return foundEvent.args.params;
|
|
140
|
-
});
|
|
141
|
-
if (vaultParams.btcRelayContract.toLowerCase() !== this.btcRelay.contractAddress.toLowerCase())
|
|
142
|
-
return null;
|
|
143
|
-
return new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, vaultState, vaultParams);
|
|
144
|
-
}
|
|
145
|
-
async getAllVaults(owner) {
|
|
146
|
-
const openedVaults = new Map();
|
|
147
|
-
await this.Events.findInContractEventsForward(["Opened", "Closed"], owner == null ? null : [
|
|
148
|
-
"0x" + owner.substring(2).padStart(64, "0")
|
|
149
|
-
], (event) => {
|
|
150
|
-
const vaultIdentifier = event.args.owner + ":" + event.args.vaultId.toString(10);
|
|
151
|
-
if (event.eventName === "Opened") {
|
|
152
|
-
const _event = event;
|
|
153
|
-
openedVaults.set(vaultIdentifier, _event.args.params);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
openedVaults.delete(vaultIdentifier);
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
});
|
|
160
|
-
const vaults = [];
|
|
161
|
-
for (let [identifier, vaultParams] of openedVaults.entries()) {
|
|
162
|
-
const [owner, vaultIdStr] = identifier.split(":");
|
|
163
|
-
const vaultState = await this.contract.getVault(owner, BigInt(vaultIdStr));
|
|
164
|
-
if (vaultState.spvVaultParametersCommitment === (0, EVMSpvVaultData_1.getVaultParamsCommitment)(vaultParams)) {
|
|
165
|
-
vaults.push(new EVMSpvVaultData_1.EVMSpvVaultData(owner, BigInt(vaultIdStr), vaultState, vaultParams));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return vaults;
|
|
169
|
-
}
|
|
170
|
-
async getWithdrawalState(btcTxId) {
|
|
171
|
-
const txHash = buffer_1.Buffer.from(btcTxId, "hex").reverse();
|
|
172
|
-
let result = await this.Events.findInContractEvents(["Fronted", "Claimed", "Closed"], [
|
|
173
|
-
null,
|
|
174
|
-
null,
|
|
175
|
-
(0, ethers_1.hexlify)(txHash)
|
|
176
|
-
], async (event) => {
|
|
177
|
-
switch (event.eventName) {
|
|
178
|
-
case "Fronted":
|
|
179
|
-
const frontedEvent = event;
|
|
180
|
-
const [ownerFront, vaultIdFront] = unpackOwnerAndVaultId(frontedEvent.args.ownerAndVaultId);
|
|
181
|
-
return {
|
|
182
|
-
type: base_1.SpvWithdrawalStateType.FRONTED,
|
|
183
|
-
txId: event.transactionHash,
|
|
184
|
-
owner: ownerFront,
|
|
185
|
-
vaultId: vaultIdFront,
|
|
186
|
-
recipient: frontedEvent.args.recipient,
|
|
187
|
-
fronter: frontedEvent.args.caller
|
|
188
|
-
};
|
|
189
|
-
case "Claimed":
|
|
190
|
-
const claimedEvent = event;
|
|
191
|
-
const [ownerClaim, vaultIdClaim] = unpackOwnerAndVaultId(claimedEvent.args.ownerAndVaultId);
|
|
192
|
-
return {
|
|
193
|
-
type: base_1.SpvWithdrawalStateType.CLAIMED,
|
|
194
|
-
txId: event.transactionHash,
|
|
195
|
-
owner: ownerClaim,
|
|
196
|
-
vaultId: vaultIdClaim,
|
|
197
|
-
recipient: claimedEvent.args.recipient,
|
|
198
|
-
claimer: claimedEvent.args.caller,
|
|
199
|
-
fronter: claimedEvent.args.frontingAddress
|
|
200
|
-
};
|
|
201
|
-
case "Closed":
|
|
202
|
-
const closedEvent = event;
|
|
203
|
-
return {
|
|
204
|
-
type: base_1.SpvWithdrawalStateType.CLOSED,
|
|
205
|
-
txId: event.transactionHash,
|
|
206
|
-
owner: closedEvent.args.owner,
|
|
207
|
-
vaultId: closedEvent.args.vaultId,
|
|
208
|
-
error: closedEvent.args.error
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
result ?? (result = {
|
|
213
|
-
type: base_1.SpvWithdrawalStateType.NOT_FOUND
|
|
214
|
-
});
|
|
215
|
-
return result;
|
|
216
|
-
}
|
|
217
|
-
getWithdrawalData(btcTx) {
|
|
218
|
-
return Promise.resolve(new EVMSpvWithdrawalData_1.EVMSpvWithdrawalData(btcTx));
|
|
219
|
-
}
|
|
220
|
-
//OP_RETURN data encoding/decoding
|
|
221
|
-
fromOpReturnData(data) {
|
|
222
|
-
return EVMSpvVaultContract.fromOpReturnData(data);
|
|
223
|
-
}
|
|
224
|
-
static fromOpReturnData(data) {
|
|
225
|
-
let rawAmount0 = 0n;
|
|
226
|
-
let rawAmount1 = 0n;
|
|
227
|
-
let executionHash = null;
|
|
228
|
-
if (data.length === 28) {
|
|
229
|
-
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
230
|
-
}
|
|
231
|
-
else if (data.length === 36) {
|
|
232
|
-
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
233
|
-
rawAmount1 = data.readBigInt64BE(28).valueOf();
|
|
234
|
-
}
|
|
235
|
-
else if (data.length === 60) {
|
|
236
|
-
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
237
|
-
executionHash = data.slice(28, 60).toString("hex");
|
|
238
|
-
}
|
|
239
|
-
else if (data.length === 68) {
|
|
240
|
-
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
241
|
-
rawAmount1 = data.readBigInt64BE(28).valueOf();
|
|
242
|
-
executionHash = data.slice(36, 68).toString("hex");
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
throw new Error("Invalid OP_RETURN data length!");
|
|
246
|
-
}
|
|
247
|
-
const recipient = "0x" + data.slice(0, 20).toString("hex");
|
|
248
|
-
if (!EVMAddresses_1.EVMAddresses.isValidAddress(recipient))
|
|
249
|
-
throw new Error("Invalid recipient specified");
|
|
250
|
-
return { executionHash, rawAmounts: [rawAmount0, rawAmount1], recipient: (0, ethers_1.getAddress)(recipient) };
|
|
251
|
-
}
|
|
252
|
-
toOpReturnData(recipient, rawAmounts, executionHash) {
|
|
253
|
-
return EVMSpvVaultContract.toOpReturnData(recipient, rawAmounts, executionHash);
|
|
254
|
-
}
|
|
255
|
-
static toOpReturnData(recipient, rawAmounts, executionHash) {
|
|
256
|
-
if (!EVMAddresses_1.EVMAddresses.isValidAddress(recipient))
|
|
257
|
-
throw new Error("Invalid recipient specified");
|
|
258
|
-
if (rawAmounts.length < 1)
|
|
259
|
-
throw new Error("At least 1 amount needs to be specified");
|
|
260
|
-
if (rawAmounts.length > 2)
|
|
261
|
-
throw new Error("At most 2 amounts need to be specified");
|
|
262
|
-
rawAmounts.forEach(val => {
|
|
263
|
-
if (val < 0n)
|
|
264
|
-
throw new Error("Negative raw amount specified");
|
|
265
|
-
if (val >= 2n ** 64n)
|
|
266
|
-
throw new Error("Raw amount overflow");
|
|
267
|
-
});
|
|
268
|
-
if (executionHash != null) {
|
|
269
|
-
if (buffer_1.Buffer.from(executionHash, "hex").length !== 32)
|
|
270
|
-
throw new Error("Invalid execution hash");
|
|
271
|
-
}
|
|
272
|
-
const recipientBuffer = buffer_1.Buffer.from(recipient.substring(2).padStart(40, "0"), "hex");
|
|
273
|
-
const amount0Buffer = base_1.BigIntBufferUtils.toBuffer(rawAmounts[0], "be", 8);
|
|
274
|
-
const amount1Buffer = rawAmounts[1] == null || rawAmounts[1] === 0n ? buffer_1.Buffer.alloc(0) : base_1.BigIntBufferUtils.toBuffer(rawAmounts[1], "be", 8);
|
|
275
|
-
const executionHashBuffer = executionHash == null ? buffer_1.Buffer.alloc(0) : buffer_1.Buffer.from(executionHash, "hex");
|
|
276
|
-
return buffer_1.Buffer.concat([
|
|
277
|
-
recipientBuffer,
|
|
278
|
-
amount0Buffer,
|
|
279
|
-
amount1Buffer,
|
|
280
|
-
executionHashBuffer
|
|
281
|
-
]);
|
|
282
|
-
}
|
|
283
|
-
//Actions
|
|
284
|
-
async claim(signer, vault, txs, synchronizer, initAta, txOptions) {
|
|
285
|
-
const result = await this.txsClaim(signer.getAddress(), vault, txs, synchronizer, initAta, txOptions?.feeRate);
|
|
286
|
-
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
287
|
-
return signature;
|
|
288
|
-
}
|
|
289
|
-
async deposit(signer, vault, rawAmounts, txOptions) {
|
|
290
|
-
const result = await this.txsDeposit(signer.getAddress(), vault, rawAmounts, txOptions?.feeRate);
|
|
291
|
-
const txHashes = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
292
|
-
return txHashes[txHashes.length - 1];
|
|
293
|
-
}
|
|
294
|
-
async frontLiquidity(signer, vault, realWithdrawalTx, withdrawSequence, txOptions) {
|
|
295
|
-
const result = await this.txsFrontLiquidity(signer.getAddress(), vault, realWithdrawalTx, withdrawSequence, txOptions?.feeRate);
|
|
296
|
-
const txHashes = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
297
|
-
return txHashes[txHashes.length - 1];
|
|
298
|
-
}
|
|
299
|
-
async open(signer, vault, txOptions) {
|
|
300
|
-
const result = await this.txsOpen(signer.getAddress(), vault, txOptions?.feeRate);
|
|
301
|
-
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
302
|
-
return signature;
|
|
303
|
-
}
|
|
304
|
-
//Transactions
|
|
305
|
-
async txsClaim(signer, vault, txs, synchronizer, initAta, feeRate) {
|
|
306
|
-
if (!vault.isOpened())
|
|
307
|
-
throw new Error("Cannot claim from a closed vault!");
|
|
308
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
309
|
-
const txsWithMerkleProofs = [];
|
|
310
|
-
for (let tx of txs) {
|
|
311
|
-
const merkleProof = await this.bitcoinRpc.getMerkleProof(tx.tx.btcTx.txid, tx.tx.btcTx.blockhash);
|
|
312
|
-
this.logger.debug("txsClaim(): merkle proof computed: ", merkleProof);
|
|
313
|
-
txsWithMerkleProofs.push({
|
|
314
|
-
...merkleProof,
|
|
315
|
-
...tx
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
const evmTxs = [];
|
|
319
|
-
const storedHeaders = await EVMBtcRelay_1.EVMBtcRelay.getCommitedHeadersAndSynchronize(signer, this.btcRelay, txsWithMerkleProofs.filter(tx => tx.storedHeader == null).map(tx => {
|
|
320
|
-
return {
|
|
321
|
-
blockhash: tx.tx.btcTx.blockhash,
|
|
322
|
-
blockheight: tx.blockheight,
|
|
323
|
-
requiredConfirmations: vault.getConfirmations()
|
|
324
|
-
};
|
|
325
|
-
}), evmTxs, synchronizer, feeRate);
|
|
326
|
-
if (storedHeaders == null)
|
|
327
|
-
throw new Error("Cannot fetch committed header!");
|
|
328
|
-
for (let tx of txsWithMerkleProofs) {
|
|
329
|
-
evmTxs.push(await this.Claim(signer, vault, tx.tx, tx.storedHeader ?? storedHeaders[tx.tx.btcTx.blockhash], tx.merkle, tx.pos, feeRate));
|
|
330
|
-
}
|
|
331
|
-
this.logger.debug("txsClaim(): " + evmTxs.length + " claim TXs created claiming " + txs.length + " txs, owner: " + vault.getOwner() +
|
|
332
|
-
" vaultId: " + vault.getVaultId().toString(10));
|
|
333
|
-
return evmTxs;
|
|
334
|
-
}
|
|
335
|
-
async txsDeposit(signer, vault, rawAmounts, feeRate) {
|
|
336
|
-
var _a;
|
|
337
|
-
if (!vault.isOpened())
|
|
338
|
-
throw new Error("Cannot deposit to a closed vault!");
|
|
339
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
340
|
-
const txs = [];
|
|
341
|
-
let realAmount0 = 0n;
|
|
342
|
-
let realAmount1 = 0n;
|
|
343
|
-
//Approve first
|
|
344
|
-
const requiredApprovals = {};
|
|
345
|
-
if (rawAmounts[0] != null && rawAmounts[0] !== 0n) {
|
|
346
|
-
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
347
|
-
realAmount0 = rawAmounts[0] * vault.token0.multiplier;
|
|
348
|
-
requiredApprovals[vault.token0.token.toLowerCase()] = realAmount0;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
if (rawAmounts[1] != null && rawAmounts[1] !== 0n) {
|
|
352
|
-
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
353
|
-
realAmount1 = rawAmounts[1] * vault.token1.multiplier;
|
|
354
|
-
requiredApprovals[_a = vault.token1.token.toLowerCase()] ?? (requiredApprovals[_a] = 0n);
|
|
355
|
-
requiredApprovals[vault.token1.token.toLowerCase()] += realAmount1;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
const requiredApprovalTxns = await Promise.all(Object.keys(requiredApprovals).map(token => this.Chain.Tokens.checkAndGetApproveTx(signer, token, requiredApprovals[token], this.contractAddress, feeRate)));
|
|
359
|
-
requiredApprovalTxns.forEach(tx => tx != null && txs.push(tx));
|
|
360
|
-
txs.push(await this.Deposit(signer, vault, rawAmounts, feeRate));
|
|
361
|
-
this.logger.debug("txsDeposit(): deposit TX created," +
|
|
362
|
-
" token0: " + vault.token0.token + " rawAmount0: " + rawAmounts[0].toString(10) + " amount0: " + realAmount0.toString(10) +
|
|
363
|
-
" token1: " + vault.token1.token + " rawAmount1: " + (rawAmounts[1] ?? 0n).toString(10) + " amount1: " + realAmount1.toString(10));
|
|
364
|
-
return txs;
|
|
365
|
-
}
|
|
366
|
-
async txsFrontLiquidity(signer, vault, realWithdrawalTx, withdrawSequence, feeRate) {
|
|
367
|
-
var _a;
|
|
368
|
-
if (!vault.isOpened())
|
|
369
|
-
throw new Error("Cannot front on a closed vault!");
|
|
370
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
371
|
-
const txs = [];
|
|
372
|
-
let realAmount0 = 0n;
|
|
373
|
-
let realAmount1 = 0n;
|
|
374
|
-
//Approve first
|
|
375
|
-
const rawAmounts = realWithdrawalTx.getFrontingAmount();
|
|
376
|
-
//Approve first
|
|
377
|
-
const requiredApprovals = {};
|
|
378
|
-
if (rawAmounts[0] != null && rawAmounts[0] !== 0n) {
|
|
379
|
-
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
380
|
-
realAmount0 = rawAmounts[0] * vault.token0.multiplier;
|
|
381
|
-
requiredApprovals[vault.token0.token.toLowerCase()] = realAmount0;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (rawAmounts[1] != null && rawAmounts[1] !== 0n) {
|
|
385
|
-
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
386
|
-
realAmount1 = rawAmounts[1] * vault.token1.multiplier;
|
|
387
|
-
requiredApprovals[_a = vault.token1.token.toLowerCase()] ?? (requiredApprovals[_a] = 0n);
|
|
388
|
-
requiredApprovals[vault.token1.token.toLowerCase()] += realAmount1;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
const requiredApprovalTxns = await Promise.all(Object.keys(requiredApprovals).map(token => this.Chain.Tokens.checkAndGetApproveTx(signer, token, requiredApprovals[token], this.contractAddress, feeRate)));
|
|
392
|
-
requiredApprovalTxns.forEach(tx => tx != null && txs.push(tx));
|
|
393
|
-
txs.push(await this.Front(signer, vault, realWithdrawalTx, withdrawSequence, feeRate));
|
|
394
|
-
this.logger.debug("txsFrontLiquidity(): front TX created," +
|
|
395
|
-
" token0: " + vault.token0.token + " rawAmount0: " + rawAmounts[0].toString(10) + " amount0: " + realAmount0.toString(10) +
|
|
396
|
-
" token1: " + vault.token1.token + " rawAmount1: " + (rawAmounts[1] ?? 0n).toString(10) + " amount1: " + realAmount1.toString(10));
|
|
397
|
-
return txs;
|
|
398
|
-
}
|
|
399
|
-
async txsOpen(signer, vault, feeRate) {
|
|
400
|
-
if (vault.isOpened())
|
|
401
|
-
throw new Error("Cannot open an already opened vault!");
|
|
402
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
403
|
-
const tx = await this.Open(signer, vault, feeRate);
|
|
404
|
-
this.logger.debug("txsOpen(): open TX created, owner: " + vault.getOwner() +
|
|
405
|
-
" vaultId: " + vault.getVaultId().toString(10));
|
|
406
|
-
return [tx];
|
|
407
|
-
}
|
|
408
|
-
getClaimGas(signer, vault, data) {
|
|
409
|
-
let totalGas = EVMSpvVaultContract.GasCosts.CLAIM_BASE;
|
|
410
|
-
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
411
|
-
const transferFee = vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
412
|
-
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
413
|
-
totalGas += transferFee;
|
|
414
|
-
if (data == null || data.frontingFeeRate > 0n)
|
|
415
|
-
totalGas += transferFee; //Also needs to pay out to fronter
|
|
416
|
-
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
417
|
-
totalGas += transferFee; //Also needs to pay out to caller
|
|
418
|
-
}
|
|
419
|
-
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
420
|
-
const transferFee = vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
421
|
-
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
422
|
-
totalGas += transferFee;
|
|
423
|
-
if (data == null || data.frontingFeeRate > 0n)
|
|
424
|
-
totalGas += transferFee; //Also needs to pay out to fronter
|
|
425
|
-
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
426
|
-
totalGas += transferFee; //Also needs to pay out to caller
|
|
427
|
-
}
|
|
428
|
-
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
429
|
-
totalGas += EVMSpvVaultContract.GasCosts.CLAIM_EXECUTION_SCHEDULE;
|
|
430
|
-
return totalGas;
|
|
431
|
-
}
|
|
432
|
-
getFrontGas(signer, vault, data) {
|
|
433
|
-
let totalGas = EVMSpvVaultContract.GasCosts.FRONT_BASE;
|
|
434
|
-
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
435
|
-
totalGas += vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
436
|
-
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
437
|
-
}
|
|
438
|
-
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
439
|
-
totalGas += vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
440
|
-
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
441
|
-
}
|
|
442
|
-
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
443
|
-
totalGas += EVMSpvVaultContract.GasCosts.FRONT_EXECUTION_SCHEDULE;
|
|
444
|
-
return totalGas;
|
|
445
|
-
}
|
|
446
|
-
async getClaimFee(signer, vault, withdrawalData, feeRate) {
|
|
447
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
448
|
-
return EVMFees_1.EVMFees.getGasFee(this.getClaimGas(signer, vault, withdrawalData), feeRate);
|
|
449
|
-
}
|
|
450
|
-
async getFrontFee(signer, vault, withdrawalData, feeRate) {
|
|
451
|
-
vault ?? (vault = EVMSpvVaultData_1.EVMSpvVaultData.randomVault());
|
|
452
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
453
|
-
let totalFee = EVMFees_1.EVMFees.getGasFee(this.getFrontGas(signer, vault, withdrawalData), feeRate);
|
|
454
|
-
if (withdrawalData == null || (withdrawalData.rawAmounts[0] != null && withdrawalData.rawAmounts[0] > 0n)) {
|
|
455
|
-
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
456
|
-
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
if (withdrawalData == null || (withdrawalData.rawAmounts[1] != null && withdrawalData.rawAmounts[1] > 0n)) {
|
|
460
|
-
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
461
|
-
if (vault.token1.token.toLowerCase() !== vault.token0.token.toLowerCase() || withdrawalData == null || withdrawalData.rawAmounts[0] == null || withdrawalData.rawAmounts[0] === 0n) {
|
|
462
|
-
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return totalFee;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
exports.EVMSpvVaultContract = EVMSpvVaultContract;
|
|
470
|
-
EVMSpvVaultContract.GasCosts = {
|
|
471
|
-
DEPOSIT_BASE: 15000 + 21000,
|
|
472
|
-
DEPOSIT_ERC20: 40000,
|
|
473
|
-
OPEN: 80000 + 21000,
|
|
474
|
-
CLAIM_BASE: 85000 + 21000,
|
|
475
|
-
CLAIM_NATIVE_TRANSFER: 35000,
|
|
476
|
-
CLAIM_ERC20_TRANSFER: 40000,
|
|
477
|
-
CLAIM_EXECUTION_SCHEDULE: 30000,
|
|
478
|
-
FRONT_BASE: 75000 + 21000,
|
|
479
|
-
FRONT_NATIVE_TRANSFER: 35000,
|
|
480
|
-
FRONT_ERC20_TRANSFER: 40000,
|
|
481
|
-
FRONT_EXECUTION_SCHEDULE: 30000
|
|
482
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVMSpvVaultContract = exports.unpackOwnerAndVaultId = exports.packOwnerAndVaultId = void 0;
|
|
4
|
+
const base_1 = require("@atomiqlabs/base");
|
|
5
|
+
const buffer_1 = require("buffer");
|
|
6
|
+
const EVMContractBase_1 = require("../contract/EVMContractBase");
|
|
7
|
+
const SpvVaultContractAbi_1 = require("./SpvVaultContractAbi");
|
|
8
|
+
const EVMBtcRelay_1 = require("../btcrelay/EVMBtcRelay");
|
|
9
|
+
const Utils_1 = require("../../utils/Utils");
|
|
10
|
+
const ethers_1 = require("ethers");
|
|
11
|
+
const EVMAddresses_1 = require("../chain/modules/EVMAddresses");
|
|
12
|
+
const EVMSpvVaultData_1 = require("./EVMSpvVaultData");
|
|
13
|
+
const EVMSpvWithdrawalData_1 = require("./EVMSpvWithdrawalData");
|
|
14
|
+
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
15
|
+
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
16
|
+
function decodeUtxo(utxo) {
|
|
17
|
+
const [txId, vout] = utxo.split(":");
|
|
18
|
+
return {
|
|
19
|
+
txHash: "0x" + buffer_1.Buffer.from(txId, "hex").reverse().toString("hex"),
|
|
20
|
+
vout: BigInt(vout)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function packOwnerAndVaultId(owner, vaultId) {
|
|
24
|
+
if (owner.length !== 42)
|
|
25
|
+
throw new Error("Invalid owner address");
|
|
26
|
+
return owner.toLowerCase() + base_1.BigIntBufferUtils.toBuffer(vaultId, "be", 12).toString("hex");
|
|
27
|
+
}
|
|
28
|
+
exports.packOwnerAndVaultId = packOwnerAndVaultId;
|
|
29
|
+
function unpackOwnerAndVaultId(data) {
|
|
30
|
+
return [(0, ethers_1.getAddress)(data.substring(0, 42)), BigInt("0x" + data.substring(42, 66))];
|
|
31
|
+
}
|
|
32
|
+
exports.unpackOwnerAndVaultId = unpackOwnerAndVaultId;
|
|
33
|
+
class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
34
|
+
constructor(chainInterface, btcRelay, bitcoinRpc, contractAddress, contractDeploymentHeight) {
|
|
35
|
+
super(chainInterface, contractAddress, SpvVaultContractAbi_1.SpvVaultContractAbi, contractDeploymentHeight);
|
|
36
|
+
this.claimTimeout = 180;
|
|
37
|
+
this.logger = (0, Utils_1.getLogger)("EVMSpvVaultContract: ");
|
|
38
|
+
this.vaultParamsCache = new promise_cache_ts_1.PromiseLruCache(5000);
|
|
39
|
+
this.btcRelay = btcRelay;
|
|
40
|
+
this.bitcoinRpc = bitcoinRpc;
|
|
41
|
+
}
|
|
42
|
+
//Transactions
|
|
43
|
+
async Open(signer, vault, feeRate) {
|
|
44
|
+
const { txHash, vout } = decodeUtxo(vault.getUtxo());
|
|
45
|
+
const tokens = vault.getTokenData();
|
|
46
|
+
if (tokens.length !== 2)
|
|
47
|
+
throw new Error("Must specify exactly 2 tokens for vault!");
|
|
48
|
+
const tx = await this.contract.open.populateTransaction(vault.vaultId, vault.getVaultParamsStruct(), txHash, vout);
|
|
49
|
+
tx.from = signer;
|
|
50
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, EVMSpvVaultContract.GasCosts.OPEN, feeRate);
|
|
51
|
+
return tx;
|
|
52
|
+
}
|
|
53
|
+
async Deposit(signer, vault, rawAmounts, feeRate) {
|
|
54
|
+
let totalGas = EVMSpvVaultContract.GasCosts.DEPOSIT_BASE;
|
|
55
|
+
let value = 0n;
|
|
56
|
+
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
57
|
+
value += rawAmounts[0] * vault.token0.multiplier;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (rawAmounts[0] > 0n)
|
|
61
|
+
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
62
|
+
}
|
|
63
|
+
if (vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
64
|
+
value += (rawAmounts[1] ?? 0n) * vault.token1.multiplier;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (rawAmounts[1] != null && rawAmounts[1] > 0n && vault.token0.token.toLowerCase() !== vault.token1.token.toLowerCase())
|
|
68
|
+
totalGas += EVMSpvVaultContract.GasCosts.DEPOSIT_ERC20;
|
|
69
|
+
}
|
|
70
|
+
const tx = await this.contract.deposit.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), rawAmounts[0], rawAmounts[1] ?? 0n, { value });
|
|
71
|
+
tx.from = signer;
|
|
72
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, totalGas, feeRate);
|
|
73
|
+
return tx;
|
|
74
|
+
}
|
|
75
|
+
async Front(signer, vault, data, withdrawalSequence, feeRate) {
|
|
76
|
+
let value = 0n;
|
|
77
|
+
const frontingAmount = data.getFrontingAmount();
|
|
78
|
+
if (vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
79
|
+
value += frontingAmount[0] * vault.token0.multiplier;
|
|
80
|
+
if (vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress().toLowerCase())
|
|
81
|
+
value += (frontingAmount[1] ?? 0n) * vault.token1.multiplier;
|
|
82
|
+
const tx = await this.contract.front.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), withdrawalSequence, data.getTxHash(), data.serializeToStruct(), { value });
|
|
83
|
+
tx.from = signer;
|
|
84
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, this.getFrontGas(signer, vault, data), feeRate);
|
|
85
|
+
return tx;
|
|
86
|
+
}
|
|
87
|
+
async Claim(signer, vault, data, blockheader, merkle, position, feeRate) {
|
|
88
|
+
const tx = await this.contract.claim.populateTransaction(vault.owner, vault.vaultId, vault.getVaultParamsStruct(), "0x" + data.btcTx.hex, blockheader.serializeToStruct(), merkle, position);
|
|
89
|
+
tx.from = signer;
|
|
90
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, this.getClaimGas(signer, vault, data), feeRate);
|
|
91
|
+
return tx;
|
|
92
|
+
}
|
|
93
|
+
async checkWithdrawalTx(tx) {
|
|
94
|
+
const result = await this.contract.parseBitcoinTx(buffer_1.Buffer.from(tx.btcTx.hex, "hex"));
|
|
95
|
+
if (result == null)
|
|
96
|
+
throw new Error("Failed to parse transaction!");
|
|
97
|
+
}
|
|
98
|
+
createVaultData(owner, vaultId, utxo, confirmations, tokenData) {
|
|
99
|
+
if (tokenData.length !== 2)
|
|
100
|
+
throw new Error("Must specify 2 tokens in tokenData!");
|
|
101
|
+
const vaultParams = {
|
|
102
|
+
btcRelayContract: this.btcRelay.contractAddress,
|
|
103
|
+
token0: tokenData[0].token,
|
|
104
|
+
token1: tokenData[1].token,
|
|
105
|
+
token0Multiplier: tokenData[0].multiplier,
|
|
106
|
+
token1Multiplier: tokenData[1].multiplier,
|
|
107
|
+
confirmations: BigInt(confirmations)
|
|
108
|
+
};
|
|
109
|
+
const spvVaultParametersCommitment = (0, ethers_1.keccak256)(ethers_1.AbiCoder.defaultAbiCoder().encode(["address", "address", "address", "uint192", "uint192", "uint256"], [vaultParams.btcRelayContract, vaultParams.token0, vaultParams.token1, vaultParams.token0Multiplier, vaultParams.token1Multiplier, vaultParams.confirmations]));
|
|
110
|
+
return Promise.resolve(new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, {
|
|
111
|
+
spvVaultParametersCommitment,
|
|
112
|
+
utxoTxHash: ethers_1.ZeroHash,
|
|
113
|
+
utxoVout: 0n,
|
|
114
|
+
openBlockheight: 0n,
|
|
115
|
+
withdrawCount: 0n,
|
|
116
|
+
depositCount: 0n,
|
|
117
|
+
token0Amount: 0n,
|
|
118
|
+
token1Amount: 0n
|
|
119
|
+
}, vaultParams, utxo));
|
|
120
|
+
}
|
|
121
|
+
//Getters
|
|
122
|
+
async getFronterAddress(owner, vaultId, withdrawal) {
|
|
123
|
+
const frontingAddress = await this.contract.getFronterById(owner, vaultId, "0x" + withdrawal.getFrontingId());
|
|
124
|
+
if (frontingAddress === ethers_1.ZeroAddress)
|
|
125
|
+
return null;
|
|
126
|
+
return frontingAddress;
|
|
127
|
+
}
|
|
128
|
+
async getVaultData(owner, vaultId) {
|
|
129
|
+
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
130
|
+
const vaultParams = await this.vaultParamsCache.getOrComputeAsync(vaultState.spvVaultParametersCommitment, async () => {
|
|
131
|
+
const blockheight = Number(vaultState.openBlockheight);
|
|
132
|
+
const events = await this.Events.getContractBlockEvents(["Opened"], [
|
|
133
|
+
"0x" + owner.substring(2).padStart(64, "0"),
|
|
134
|
+
(0, ethers_1.hexlify)(base_1.BigIntBufferUtils.toBuffer(vaultId, "be", 32))
|
|
135
|
+
], blockheight);
|
|
136
|
+
const foundEvent = events.find(event => (0, EVMSpvVaultData_1.getVaultParamsCommitment)(event.args.params) === vaultState.spvVaultParametersCommitment);
|
|
137
|
+
if (foundEvent == null)
|
|
138
|
+
throw new Error("Valid open event not found!");
|
|
139
|
+
return foundEvent.args.params;
|
|
140
|
+
});
|
|
141
|
+
if (vaultParams.btcRelayContract.toLowerCase() !== this.btcRelay.contractAddress.toLowerCase())
|
|
142
|
+
return null;
|
|
143
|
+
return new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, vaultState, vaultParams);
|
|
144
|
+
}
|
|
145
|
+
async getAllVaults(owner) {
|
|
146
|
+
const openedVaults = new Map();
|
|
147
|
+
await this.Events.findInContractEventsForward(["Opened", "Closed"], owner == null ? null : [
|
|
148
|
+
"0x" + owner.substring(2).padStart(64, "0")
|
|
149
|
+
], (event) => {
|
|
150
|
+
const vaultIdentifier = event.args.owner + ":" + event.args.vaultId.toString(10);
|
|
151
|
+
if (event.eventName === "Opened") {
|
|
152
|
+
const _event = event;
|
|
153
|
+
openedVaults.set(vaultIdentifier, _event.args.params);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
openedVaults.delete(vaultIdentifier);
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
});
|
|
160
|
+
const vaults = [];
|
|
161
|
+
for (let [identifier, vaultParams] of openedVaults.entries()) {
|
|
162
|
+
const [owner, vaultIdStr] = identifier.split(":");
|
|
163
|
+
const vaultState = await this.contract.getVault(owner, BigInt(vaultIdStr));
|
|
164
|
+
if (vaultState.spvVaultParametersCommitment === (0, EVMSpvVaultData_1.getVaultParamsCommitment)(vaultParams)) {
|
|
165
|
+
vaults.push(new EVMSpvVaultData_1.EVMSpvVaultData(owner, BigInt(vaultIdStr), vaultState, vaultParams));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return vaults;
|
|
169
|
+
}
|
|
170
|
+
async getWithdrawalState(btcTxId) {
|
|
171
|
+
const txHash = buffer_1.Buffer.from(btcTxId, "hex").reverse();
|
|
172
|
+
let result = await this.Events.findInContractEvents(["Fronted", "Claimed", "Closed"], [
|
|
173
|
+
null,
|
|
174
|
+
null,
|
|
175
|
+
(0, ethers_1.hexlify)(txHash)
|
|
176
|
+
], async (event) => {
|
|
177
|
+
switch (event.eventName) {
|
|
178
|
+
case "Fronted":
|
|
179
|
+
const frontedEvent = event;
|
|
180
|
+
const [ownerFront, vaultIdFront] = unpackOwnerAndVaultId(frontedEvent.args.ownerAndVaultId);
|
|
181
|
+
return {
|
|
182
|
+
type: base_1.SpvWithdrawalStateType.FRONTED,
|
|
183
|
+
txId: event.transactionHash,
|
|
184
|
+
owner: ownerFront,
|
|
185
|
+
vaultId: vaultIdFront,
|
|
186
|
+
recipient: frontedEvent.args.recipient,
|
|
187
|
+
fronter: frontedEvent.args.caller
|
|
188
|
+
};
|
|
189
|
+
case "Claimed":
|
|
190
|
+
const claimedEvent = event;
|
|
191
|
+
const [ownerClaim, vaultIdClaim] = unpackOwnerAndVaultId(claimedEvent.args.ownerAndVaultId);
|
|
192
|
+
return {
|
|
193
|
+
type: base_1.SpvWithdrawalStateType.CLAIMED,
|
|
194
|
+
txId: event.transactionHash,
|
|
195
|
+
owner: ownerClaim,
|
|
196
|
+
vaultId: vaultIdClaim,
|
|
197
|
+
recipient: claimedEvent.args.recipient,
|
|
198
|
+
claimer: claimedEvent.args.caller,
|
|
199
|
+
fronter: claimedEvent.args.frontingAddress
|
|
200
|
+
};
|
|
201
|
+
case "Closed":
|
|
202
|
+
const closedEvent = event;
|
|
203
|
+
return {
|
|
204
|
+
type: base_1.SpvWithdrawalStateType.CLOSED,
|
|
205
|
+
txId: event.transactionHash,
|
|
206
|
+
owner: closedEvent.args.owner,
|
|
207
|
+
vaultId: closedEvent.args.vaultId,
|
|
208
|
+
error: closedEvent.args.error
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
result ?? (result = {
|
|
213
|
+
type: base_1.SpvWithdrawalStateType.NOT_FOUND
|
|
214
|
+
});
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
getWithdrawalData(btcTx) {
|
|
218
|
+
return Promise.resolve(new EVMSpvWithdrawalData_1.EVMSpvWithdrawalData(btcTx));
|
|
219
|
+
}
|
|
220
|
+
//OP_RETURN data encoding/decoding
|
|
221
|
+
fromOpReturnData(data) {
|
|
222
|
+
return EVMSpvVaultContract.fromOpReturnData(data);
|
|
223
|
+
}
|
|
224
|
+
static fromOpReturnData(data) {
|
|
225
|
+
let rawAmount0 = 0n;
|
|
226
|
+
let rawAmount1 = 0n;
|
|
227
|
+
let executionHash = null;
|
|
228
|
+
if (data.length === 28) {
|
|
229
|
+
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
230
|
+
}
|
|
231
|
+
else if (data.length === 36) {
|
|
232
|
+
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
233
|
+
rawAmount1 = data.readBigInt64BE(28).valueOf();
|
|
234
|
+
}
|
|
235
|
+
else if (data.length === 60) {
|
|
236
|
+
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
237
|
+
executionHash = data.slice(28, 60).toString("hex");
|
|
238
|
+
}
|
|
239
|
+
else if (data.length === 68) {
|
|
240
|
+
rawAmount0 = data.readBigInt64BE(20).valueOf();
|
|
241
|
+
rawAmount1 = data.readBigInt64BE(28).valueOf();
|
|
242
|
+
executionHash = data.slice(36, 68).toString("hex");
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
throw new Error("Invalid OP_RETURN data length!");
|
|
246
|
+
}
|
|
247
|
+
const recipient = "0x" + data.slice(0, 20).toString("hex");
|
|
248
|
+
if (!EVMAddresses_1.EVMAddresses.isValidAddress(recipient))
|
|
249
|
+
throw new Error("Invalid recipient specified");
|
|
250
|
+
return { executionHash, rawAmounts: [rawAmount0, rawAmount1], recipient: (0, ethers_1.getAddress)(recipient) };
|
|
251
|
+
}
|
|
252
|
+
toOpReturnData(recipient, rawAmounts, executionHash) {
|
|
253
|
+
return EVMSpvVaultContract.toOpReturnData(recipient, rawAmounts, executionHash);
|
|
254
|
+
}
|
|
255
|
+
static toOpReturnData(recipient, rawAmounts, executionHash) {
|
|
256
|
+
if (!EVMAddresses_1.EVMAddresses.isValidAddress(recipient))
|
|
257
|
+
throw new Error("Invalid recipient specified");
|
|
258
|
+
if (rawAmounts.length < 1)
|
|
259
|
+
throw new Error("At least 1 amount needs to be specified");
|
|
260
|
+
if (rawAmounts.length > 2)
|
|
261
|
+
throw new Error("At most 2 amounts need to be specified");
|
|
262
|
+
rawAmounts.forEach(val => {
|
|
263
|
+
if (val < 0n)
|
|
264
|
+
throw new Error("Negative raw amount specified");
|
|
265
|
+
if (val >= 2n ** 64n)
|
|
266
|
+
throw new Error("Raw amount overflow");
|
|
267
|
+
});
|
|
268
|
+
if (executionHash != null) {
|
|
269
|
+
if (buffer_1.Buffer.from(executionHash, "hex").length !== 32)
|
|
270
|
+
throw new Error("Invalid execution hash");
|
|
271
|
+
}
|
|
272
|
+
const recipientBuffer = buffer_1.Buffer.from(recipient.substring(2).padStart(40, "0"), "hex");
|
|
273
|
+
const amount0Buffer = base_1.BigIntBufferUtils.toBuffer(rawAmounts[0], "be", 8);
|
|
274
|
+
const amount1Buffer = rawAmounts[1] == null || rawAmounts[1] === 0n ? buffer_1.Buffer.alloc(0) : base_1.BigIntBufferUtils.toBuffer(rawAmounts[1], "be", 8);
|
|
275
|
+
const executionHashBuffer = executionHash == null ? buffer_1.Buffer.alloc(0) : buffer_1.Buffer.from(executionHash, "hex");
|
|
276
|
+
return buffer_1.Buffer.concat([
|
|
277
|
+
recipientBuffer,
|
|
278
|
+
amount0Buffer,
|
|
279
|
+
amount1Buffer,
|
|
280
|
+
executionHashBuffer
|
|
281
|
+
]);
|
|
282
|
+
}
|
|
283
|
+
//Actions
|
|
284
|
+
async claim(signer, vault, txs, synchronizer, initAta, txOptions) {
|
|
285
|
+
const result = await this.txsClaim(signer.getAddress(), vault, txs, synchronizer, initAta, txOptions?.feeRate);
|
|
286
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
287
|
+
return signature;
|
|
288
|
+
}
|
|
289
|
+
async deposit(signer, vault, rawAmounts, txOptions) {
|
|
290
|
+
const result = await this.txsDeposit(signer.getAddress(), vault, rawAmounts, txOptions?.feeRate);
|
|
291
|
+
const txHashes = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
292
|
+
return txHashes[txHashes.length - 1];
|
|
293
|
+
}
|
|
294
|
+
async frontLiquidity(signer, vault, realWithdrawalTx, withdrawSequence, txOptions) {
|
|
295
|
+
const result = await this.txsFrontLiquidity(signer.getAddress(), vault, realWithdrawalTx, withdrawSequence, txOptions?.feeRate);
|
|
296
|
+
const txHashes = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
297
|
+
return txHashes[txHashes.length - 1];
|
|
298
|
+
}
|
|
299
|
+
async open(signer, vault, txOptions) {
|
|
300
|
+
const result = await this.txsOpen(signer.getAddress(), vault, txOptions?.feeRate);
|
|
301
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
302
|
+
return signature;
|
|
303
|
+
}
|
|
304
|
+
//Transactions
|
|
305
|
+
async txsClaim(signer, vault, txs, synchronizer, initAta, feeRate) {
|
|
306
|
+
if (!vault.isOpened())
|
|
307
|
+
throw new Error("Cannot claim from a closed vault!");
|
|
308
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
309
|
+
const txsWithMerkleProofs = [];
|
|
310
|
+
for (let tx of txs) {
|
|
311
|
+
const merkleProof = await this.bitcoinRpc.getMerkleProof(tx.tx.btcTx.txid, tx.tx.btcTx.blockhash);
|
|
312
|
+
this.logger.debug("txsClaim(): merkle proof computed: ", merkleProof);
|
|
313
|
+
txsWithMerkleProofs.push({
|
|
314
|
+
...merkleProof,
|
|
315
|
+
...tx
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const evmTxs = [];
|
|
319
|
+
const storedHeaders = await EVMBtcRelay_1.EVMBtcRelay.getCommitedHeadersAndSynchronize(signer, this.btcRelay, txsWithMerkleProofs.filter(tx => tx.storedHeader == null).map(tx => {
|
|
320
|
+
return {
|
|
321
|
+
blockhash: tx.tx.btcTx.blockhash,
|
|
322
|
+
blockheight: tx.blockheight,
|
|
323
|
+
requiredConfirmations: vault.getConfirmations()
|
|
324
|
+
};
|
|
325
|
+
}), evmTxs, synchronizer, feeRate);
|
|
326
|
+
if (storedHeaders == null)
|
|
327
|
+
throw new Error("Cannot fetch committed header!");
|
|
328
|
+
for (let tx of txsWithMerkleProofs) {
|
|
329
|
+
evmTxs.push(await this.Claim(signer, vault, tx.tx, tx.storedHeader ?? storedHeaders[tx.tx.btcTx.blockhash], tx.merkle, tx.pos, feeRate));
|
|
330
|
+
}
|
|
331
|
+
this.logger.debug("txsClaim(): " + evmTxs.length + " claim TXs created claiming " + txs.length + " txs, owner: " + vault.getOwner() +
|
|
332
|
+
" vaultId: " + vault.getVaultId().toString(10));
|
|
333
|
+
return evmTxs;
|
|
334
|
+
}
|
|
335
|
+
async txsDeposit(signer, vault, rawAmounts, feeRate) {
|
|
336
|
+
var _a;
|
|
337
|
+
if (!vault.isOpened())
|
|
338
|
+
throw new Error("Cannot deposit to a closed vault!");
|
|
339
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
340
|
+
const txs = [];
|
|
341
|
+
let realAmount0 = 0n;
|
|
342
|
+
let realAmount1 = 0n;
|
|
343
|
+
//Approve first
|
|
344
|
+
const requiredApprovals = {};
|
|
345
|
+
if (rawAmounts[0] != null && rawAmounts[0] !== 0n) {
|
|
346
|
+
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
347
|
+
realAmount0 = rawAmounts[0] * vault.token0.multiplier;
|
|
348
|
+
requiredApprovals[vault.token0.token.toLowerCase()] = realAmount0;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (rawAmounts[1] != null && rawAmounts[1] !== 0n) {
|
|
352
|
+
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
353
|
+
realAmount1 = rawAmounts[1] * vault.token1.multiplier;
|
|
354
|
+
requiredApprovals[_a = vault.token1.token.toLowerCase()] ?? (requiredApprovals[_a] = 0n);
|
|
355
|
+
requiredApprovals[vault.token1.token.toLowerCase()] += realAmount1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const requiredApprovalTxns = await Promise.all(Object.keys(requiredApprovals).map(token => this.Chain.Tokens.checkAndGetApproveTx(signer, token, requiredApprovals[token], this.contractAddress, feeRate)));
|
|
359
|
+
requiredApprovalTxns.forEach(tx => tx != null && txs.push(tx));
|
|
360
|
+
txs.push(await this.Deposit(signer, vault, rawAmounts, feeRate));
|
|
361
|
+
this.logger.debug("txsDeposit(): deposit TX created," +
|
|
362
|
+
" token0: " + vault.token0.token + " rawAmount0: " + rawAmounts[0].toString(10) + " amount0: " + realAmount0.toString(10) +
|
|
363
|
+
" token1: " + vault.token1.token + " rawAmount1: " + (rawAmounts[1] ?? 0n).toString(10) + " amount1: " + realAmount1.toString(10));
|
|
364
|
+
return txs;
|
|
365
|
+
}
|
|
366
|
+
async txsFrontLiquidity(signer, vault, realWithdrawalTx, withdrawSequence, feeRate) {
|
|
367
|
+
var _a;
|
|
368
|
+
if (!vault.isOpened())
|
|
369
|
+
throw new Error("Cannot front on a closed vault!");
|
|
370
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
371
|
+
const txs = [];
|
|
372
|
+
let realAmount0 = 0n;
|
|
373
|
+
let realAmount1 = 0n;
|
|
374
|
+
//Approve first
|
|
375
|
+
const rawAmounts = realWithdrawalTx.getFrontingAmount();
|
|
376
|
+
//Approve first
|
|
377
|
+
const requiredApprovals = {};
|
|
378
|
+
if (rawAmounts[0] != null && rawAmounts[0] !== 0n) {
|
|
379
|
+
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
380
|
+
realAmount0 = rawAmounts[0] * vault.token0.multiplier;
|
|
381
|
+
requiredApprovals[vault.token0.token.toLowerCase()] = realAmount0;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (rawAmounts[1] != null && rawAmounts[1] !== 0n) {
|
|
385
|
+
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
386
|
+
realAmount1 = rawAmounts[1] * vault.token1.multiplier;
|
|
387
|
+
requiredApprovals[_a = vault.token1.token.toLowerCase()] ?? (requiredApprovals[_a] = 0n);
|
|
388
|
+
requiredApprovals[vault.token1.token.toLowerCase()] += realAmount1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const requiredApprovalTxns = await Promise.all(Object.keys(requiredApprovals).map(token => this.Chain.Tokens.checkAndGetApproveTx(signer, token, requiredApprovals[token], this.contractAddress, feeRate)));
|
|
392
|
+
requiredApprovalTxns.forEach(tx => tx != null && txs.push(tx));
|
|
393
|
+
txs.push(await this.Front(signer, vault, realWithdrawalTx, withdrawSequence, feeRate));
|
|
394
|
+
this.logger.debug("txsFrontLiquidity(): front TX created," +
|
|
395
|
+
" token0: " + vault.token0.token + " rawAmount0: " + rawAmounts[0].toString(10) + " amount0: " + realAmount0.toString(10) +
|
|
396
|
+
" token1: " + vault.token1.token + " rawAmount1: " + (rawAmounts[1] ?? 0n).toString(10) + " amount1: " + realAmount1.toString(10));
|
|
397
|
+
return txs;
|
|
398
|
+
}
|
|
399
|
+
async txsOpen(signer, vault, feeRate) {
|
|
400
|
+
if (vault.isOpened())
|
|
401
|
+
throw new Error("Cannot open an already opened vault!");
|
|
402
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
403
|
+
const tx = await this.Open(signer, vault, feeRate);
|
|
404
|
+
this.logger.debug("txsOpen(): open TX created, owner: " + vault.getOwner() +
|
|
405
|
+
" vaultId: " + vault.getVaultId().toString(10));
|
|
406
|
+
return [tx];
|
|
407
|
+
}
|
|
408
|
+
getClaimGas(signer, vault, data) {
|
|
409
|
+
let totalGas = EVMSpvVaultContract.GasCosts.CLAIM_BASE;
|
|
410
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
411
|
+
const transferFee = vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
412
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
413
|
+
totalGas += transferFee;
|
|
414
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
415
|
+
totalGas += transferFee; //Also needs to pay out to fronter
|
|
416
|
+
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
417
|
+
totalGas += transferFee; //Also needs to pay out to caller
|
|
418
|
+
}
|
|
419
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
420
|
+
const transferFee = vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
421
|
+
EVMSpvVaultContract.GasCosts.CLAIM_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.CLAIM_ERC20_TRANSFER;
|
|
422
|
+
totalGas += transferFee;
|
|
423
|
+
if (data == null || data.frontingFeeRate > 0n)
|
|
424
|
+
totalGas += transferFee; //Also needs to pay out to fronter
|
|
425
|
+
if (data == null || (data.callerFeeRate > 0n && !data.isRecipient(signer)))
|
|
426
|
+
totalGas += transferFee; //Also needs to pay out to caller
|
|
427
|
+
}
|
|
428
|
+
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
429
|
+
totalGas += EVMSpvVaultContract.GasCosts.CLAIM_EXECUTION_SCHEDULE;
|
|
430
|
+
return totalGas;
|
|
431
|
+
}
|
|
432
|
+
getFrontGas(signer, vault, data) {
|
|
433
|
+
let totalGas = EVMSpvVaultContract.GasCosts.FRONT_BASE;
|
|
434
|
+
if (data == null || (data.rawAmounts[0] != null && data.rawAmounts[0] > 0n)) {
|
|
435
|
+
totalGas += vault.token0.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
436
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
437
|
+
}
|
|
438
|
+
if (data == null || (data.rawAmounts[1] != null && data.rawAmounts[1] > 0n)) {
|
|
439
|
+
totalGas += vault.token1.token.toLowerCase() === this.Chain.getNativeCurrencyAddress() ?
|
|
440
|
+
EVMSpvVaultContract.GasCosts.FRONT_NATIVE_TRANSFER : EVMSpvVaultContract.GasCosts.FRONT_ERC20_TRANSFER;
|
|
441
|
+
}
|
|
442
|
+
if (data == null || (data.executionHash != null && data.executionHash !== ethers_1.ZeroHash))
|
|
443
|
+
totalGas += EVMSpvVaultContract.GasCosts.FRONT_EXECUTION_SCHEDULE;
|
|
444
|
+
return totalGas;
|
|
445
|
+
}
|
|
446
|
+
async getClaimFee(signer, vault, withdrawalData, feeRate) {
|
|
447
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
448
|
+
return EVMFees_1.EVMFees.getGasFee(this.getClaimGas(signer, vault, withdrawalData), feeRate);
|
|
449
|
+
}
|
|
450
|
+
async getFrontFee(signer, vault, withdrawalData, feeRate) {
|
|
451
|
+
vault ?? (vault = EVMSpvVaultData_1.EVMSpvVaultData.randomVault());
|
|
452
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
453
|
+
let totalFee = EVMFees_1.EVMFees.getGasFee(this.getFrontGas(signer, vault, withdrawalData), feeRate);
|
|
454
|
+
if (withdrawalData == null || (withdrawalData.rawAmounts[0] != null && withdrawalData.rawAmounts[0] > 0n)) {
|
|
455
|
+
if (vault.token0.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
456
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (withdrawalData == null || (withdrawalData.rawAmounts[1] != null && withdrawalData.rawAmounts[1] > 0n)) {
|
|
460
|
+
if (vault.token1.token.toLowerCase() !== this.Chain.getNativeCurrencyAddress().toLowerCase()) {
|
|
461
|
+
if (vault.token1.token.toLowerCase() !== vault.token0.token.toLowerCase() || withdrawalData == null || withdrawalData.rawAmounts[0] == null || withdrawalData.rawAmounts[0] === 0n) {
|
|
462
|
+
totalFee += await this.Chain.Tokens.getApproveFee(feeRate);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return totalFee;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
exports.EVMSpvVaultContract = EVMSpvVaultContract;
|
|
470
|
+
EVMSpvVaultContract.GasCosts = {
|
|
471
|
+
DEPOSIT_BASE: 15000 + 21000,
|
|
472
|
+
DEPOSIT_ERC20: 40000,
|
|
473
|
+
OPEN: 80000 + 21000,
|
|
474
|
+
CLAIM_BASE: 85000 + 21000,
|
|
475
|
+
CLAIM_NATIVE_TRANSFER: 35000,
|
|
476
|
+
CLAIM_ERC20_TRANSFER: 40000,
|
|
477
|
+
CLAIM_EXECUTION_SCHEDULE: 30000,
|
|
478
|
+
FRONT_BASE: 75000 + 21000,
|
|
479
|
+
FRONT_NATIVE_TRANSFER: 35000,
|
|
480
|
+
FRONT_ERC20_TRANSFER: 40000,
|
|
481
|
+
FRONT_EXECUTION_SCHEDULE: 30000
|
|
482
|
+
};
|