@atomiqlabs/chain-starknet 3.0.0-dev.8 → 4.0.0-dev.3
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/index.d.ts +38 -38
- package/dist/index.js +54 -54
- package/dist/starknet/StarknetChainType.d.ts +13 -13
- package/dist/starknet/StarknetChainType.js +2 -2
- package/dist/starknet/StarknetInitializer.d.ts +27 -19
- package/dist/starknet/StarknetInitializer.js +69 -69
- package/dist/starknet/btcrelay/BtcRelayAbi.d.ts +250 -250
- package/dist/starknet/btcrelay/BtcRelayAbi.js +341 -341
- package/dist/starknet/btcrelay/StarknetBtcRelay.d.ts +186 -186
- package/dist/starknet/btcrelay/StarknetBtcRelay.js +379 -379
- package/dist/starknet/btcrelay/headers/StarknetBtcHeader.d.ts +31 -31
- package/dist/starknet/btcrelay/headers/StarknetBtcHeader.js +74 -74
- package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.d.ts +51 -51
- package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.js +113 -113
- package/dist/starknet/chain/StarknetAction.d.ts +19 -19
- package/dist/starknet/chain/StarknetAction.js +73 -73
- package/dist/starknet/chain/StarknetChainInterface.d.ts +52 -52
- package/dist/starknet/chain/StarknetChainInterface.js +91 -91
- package/dist/starknet/chain/StarknetModule.d.ts +14 -14
- package/dist/starknet/chain/StarknetModule.js +13 -13
- package/dist/starknet/chain/modules/ERC20Abi.d.ts +755 -755
- package/dist/starknet/chain/modules/ERC20Abi.js +1032 -1032
- package/dist/starknet/chain/modules/StarknetAccounts.d.ts +6 -6
- package/dist/starknet/chain/modules/StarknetAccounts.js +24 -24
- package/dist/starknet/chain/modules/StarknetAddresses.d.ts +9 -9
- package/dist/starknet/chain/modules/StarknetAddresses.js +26 -26
- package/dist/starknet/chain/modules/StarknetBlocks.d.ts +20 -19
- package/dist/starknet/chain/modules/StarknetBlocks.js +64 -49
- package/dist/starknet/chain/modules/StarknetEvents.d.ts +44 -44
- package/dist/starknet/chain/modules/StarknetEvents.js +88 -88
- package/dist/starknet/chain/modules/StarknetFees.d.ts +77 -77
- package/dist/starknet/chain/modules/StarknetFees.js +114 -114
- package/dist/starknet/chain/modules/StarknetSignatures.d.ts +29 -29
- package/dist/starknet/chain/modules/StarknetSignatures.js +72 -72
- package/dist/starknet/chain/modules/StarknetTokens.d.ts +69 -69
- package/dist/starknet/chain/modules/StarknetTokens.js +98 -98
- package/dist/starknet/chain/modules/StarknetTransactions.d.ts +93 -93
- package/dist/starknet/chain/modules/StarknetTransactions.js +255 -255
- package/dist/starknet/contract/StarknetContractBase.d.ts +13 -13
- package/dist/starknet/contract/StarknetContractBase.js +16 -16
- package/dist/starknet/contract/StarknetContractModule.d.ts +8 -8
- package/dist/starknet/contract/StarknetContractModule.js +11 -11
- package/dist/starknet/contract/modules/StarknetContractEvents.d.ts +51 -51
- package/dist/starknet/contract/modules/StarknetContractEvents.js +97 -97
- package/dist/starknet/events/StarknetChainEvents.d.ts +21 -21
- package/dist/starknet/events/StarknetChainEvents.js +52 -52
- package/dist/starknet/events/StarknetChainEventsBrowser.d.ts +90 -91
- package/dist/starknet/events/StarknetChainEventsBrowser.js +292 -294
- package/dist/starknet/provider/RpcProviderWithRetries.d.ts +21 -21
- package/dist/starknet/provider/RpcProviderWithRetries.js +32 -32
- package/dist/starknet/spv_swap/SpvVaultContractAbi.d.ts +488 -488
- package/dist/starknet/spv_swap/SpvVaultContractAbi.js +656 -656
- package/dist/starknet/spv_swap/StarknetSpvVaultContract.d.ts +66 -65
- package/dist/starknet/spv_swap/StarknetSpvVaultContract.js +382 -376
- package/dist/starknet/spv_swap/StarknetSpvVaultData.d.ts +49 -49
- package/dist/starknet/spv_swap/StarknetSpvVaultData.js +145 -144
- package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.d.ts +25 -24
- package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.js +72 -61
- package/dist/starknet/swaps/EscrowManagerAbi.d.ts +434 -434
- package/dist/starknet/swaps/EscrowManagerAbi.js +587 -587
- package/dist/starknet/swaps/StarknetSwapContract.d.ts +190 -196
- package/dist/starknet/swaps/StarknetSwapContract.js +409 -395
- package/dist/starknet/swaps/StarknetSwapData.d.ts +67 -67
- package/dist/starknet/swaps/StarknetSwapData.js +290 -290
- package/dist/starknet/swaps/StarknetSwapModule.d.ts +10 -10
- package/dist/starknet/swaps/StarknetSwapModule.js +11 -11
- package/dist/starknet/swaps/handlers/IHandler.d.ts +13 -13
- package/dist/starknet/swaps/handlers/IHandler.js +2 -2
- package/dist/starknet/swaps/handlers/claim/ClaimHandlers.d.ts +13 -13
- package/dist/starknet/swaps/handlers/claim/ClaimHandlers.js +13 -13
- package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.d.ts +21 -21
- package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.js +44 -44
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +48 -48
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +40 -40
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +20 -20
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +30 -30
- package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +45 -45
- package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +52 -52
- package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
- package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.js +27 -27
- package/dist/starknet/swaps/modules/StarknetLpVault.d.ts +69 -69
- package/dist/starknet/swaps/modules/StarknetLpVault.js +122 -122
- package/dist/starknet/swaps/modules/StarknetSwapClaim.d.ts +53 -53
- package/dist/starknet/swaps/modules/StarknetSwapClaim.js +100 -100
- package/dist/starknet/swaps/modules/StarknetSwapInit.d.ts +86 -84
- package/dist/starknet/swaps/modules/StarknetSwapInit.js +224 -200
- package/dist/starknet/swaps/modules/StarknetSwapRefund.d.ts +62 -62
- package/dist/starknet/swaps/modules/StarknetSwapRefund.js +128 -128
- package/dist/starknet/wallet/StarknetKeypairWallet.d.ts +7 -7
- package/dist/starknet/wallet/StarknetKeypairWallet.js +30 -30
- package/dist/starknet/wallet/StarknetSigner.d.ts +12 -12
- package/dist/starknet/wallet/StarknetSigner.js +46 -46
- package/dist/utils/Utils.d.ts +37 -37
- package/dist/utils/Utils.js +261 -257
- package/package.json +37 -37
- package/src/index.ts +47 -47
- package/src/starknet/StarknetChainType.ts +28 -28
- package/src/starknet/StarknetInitializer.ts +108 -100
- package/src/starknet/btcrelay/BtcRelayAbi.ts +338 -338
- package/src/starknet/btcrelay/StarknetBtcRelay.ts +494 -494
- package/src/starknet/btcrelay/headers/StarknetBtcHeader.ts +100 -100
- package/src/starknet/btcrelay/headers/StarknetBtcStoredHeader.ts +141 -141
- package/src/starknet/chain/StarknetAction.ts +85 -85
- package/src/starknet/chain/StarknetChainInterface.ts +149 -149
- package/src/starknet/chain/StarknetModule.ts +19 -19
- package/src/starknet/chain/modules/ERC20Abi.ts +1029 -1029
- package/src/starknet/chain/modules/StarknetAccounts.ts +25 -25
- package/src/starknet/chain/modules/StarknetAddresses.ts +22 -22
- package/src/starknet/chain/modules/StarknetBlocks.ts +74 -58
- package/src/starknet/chain/modules/StarknetEvents.ts +104 -104
- package/src/starknet/chain/modules/StarknetFees.ts +154 -154
- package/src/starknet/chain/modules/StarknetSignatures.ts +91 -91
- package/src/starknet/chain/modules/StarknetTokens.ts +116 -116
- package/src/starknet/chain/modules/StarknetTransactions.ts +277 -277
- package/src/starknet/contract/StarknetContractBase.ts +26 -26
- package/src/starknet/contract/StarknetContractModule.ts +16 -16
- package/src/starknet/contract/modules/StarknetContractEvents.ts +134 -134
- package/src/starknet/events/StarknetChainEvents.ts +67 -67
- package/src/starknet/events/StarknetChainEventsBrowser.ts +410 -412
- package/src/starknet/provider/RpcProviderWithRetries.ts +43 -43
- package/src/starknet/spv_swap/SpvVaultContractAbi.ts +656 -656
- package/src/starknet/spv_swap/StarknetSpvVaultContract.ts +483 -477
- package/src/starknet/spv_swap/StarknetSpvVaultData.ts +195 -194
- package/src/starknet/spv_swap/StarknetSpvWithdrawalData.ts +79 -68
- package/src/starknet/swaps/EscrowManagerAbi.ts +586 -586
- package/src/starknet/swaps/StarknetSwapContract.ts +628 -605
- package/src/starknet/swaps/StarknetSwapData.ts +403 -403
- package/src/starknet/swaps/StarknetSwapModule.ts +17 -17
- package/src/starknet/swaps/handlers/IHandler.ts +20 -20
- package/src/starknet/swaps/handlers/claim/ClaimHandlers.ts +23 -23
- package/src/starknet/swaps/handlers/claim/HashlockClaimHandler.ts +53 -53
- package/src/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +73 -73
- package/src/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +67 -67
- package/src/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +50 -50
- package/src/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +102 -102
- package/src/starknet/swaps/handlers/refund/TimelockRefundHandler.ts +38 -38
- package/src/starknet/swaps/modules/StarknetLpVault.ts +147 -147
- package/src/starknet/swaps/modules/StarknetSwapClaim.ts +141 -141
- package/src/starknet/swaps/modules/StarknetSwapInit.ts +286 -260
- package/src/starknet/swaps/modules/StarknetSwapRefund.ts +196 -196
- package/src/starknet/wallet/StarknetKeypairWallet.ts +39 -39
- package/src/starknet/wallet/StarknetSigner.ts +55 -55
- package/src/utils/Utils.ts +252 -248
|
@@ -1,477 +1,483 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BitcoinRpc,
|
|
3
|
-
BtcTx,
|
|
4
|
-
RelaySynchronizer,
|
|
5
|
-
SpvVaultContract,
|
|
6
|
-
SpvVaultTokenData,
|
|
7
|
-
SpvWithdrawalState,
|
|
8
|
-
SpvWithdrawalStateType,
|
|
9
|
-
SpvWithdrawalTransactionData,
|
|
10
|
-
TransactionConfirmationOptions
|
|
11
|
-
} from "@atomiqlabs/base";
|
|
12
|
-
import {Buffer} from "buffer";
|
|
13
|
-
import {StarknetTx} from "../chain/modules/StarknetTransactions";
|
|
14
|
-
import {StarknetContractBase} from "../contract/StarknetContractBase";
|
|
15
|
-
import {StarknetChainInterface} from "../chain/StarknetChainInterface";
|
|
16
|
-
import {StarknetBtcRelay} from "../btcrelay/StarknetBtcRelay";
|
|
17
|
-
import {cairo, constants, merkle} from "starknet";
|
|
18
|
-
import {StarknetAction} from "../chain/StarknetAction";
|
|
19
|
-
import {SpvVaultContractAbi} from "./SpvVaultContractAbi";
|
|
20
|
-
import {StarknetSigner} from "../wallet/StarknetSigner";
|
|
21
|
-
import {StarknetSpvVaultData} from "./StarknetSpvVaultData";
|
|
22
|
-
import {StarknetSpvWithdrawalData} from "./StarknetSpvWithdrawalData";
|
|
23
|
-
import {bigNumberishToBuffer, bufferToByteArray, bufferToU32Array, getLogger, toBigInt, toHex} from "../../utils/Utils";
|
|
24
|
-
import {StarknetBtcStoredHeader} from "../btcrelay/headers/StarknetBtcStoredHeader";
|
|
25
|
-
import {StarknetAddresses} from "../chain/modules/StarknetAddresses";
|
|
26
|
-
import {StarknetFees} from "../chain/modules/StarknetFees";
|
|
27
|
-
|
|
28
|
-
const spvVaultContractAddreses = {
|
|
29
|
-
[constants.StarknetChainId.SN_SEPOLIA]: "0x047961ea0687a2e3207478d386779bd5ec22aa8abc234319ccd723e2d7191a0c",
|
|
30
|
-
[constants.StarknetChainId.SN_MAIN]: "0x06ee5228af01baa443657ccda27b80637a609d43a97ed2f8fd478313e10abf4e"
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const STARK_PRIME_MOD: bigint = 2n**251n + 17n * 2n**192n + 1n;
|
|
34
|
-
|
|
35
|
-
function decodeUtxo(utxo: string): {txHash: bigint, vout: bigint} {
|
|
36
|
-
const [txId, vout] = utxo.split(":");
|
|
37
|
-
return {
|
|
38
|
-
txHash: BigInt("0x"+Buffer.from(txId, "hex").reverse().toString("hex")),
|
|
39
|
-
vout: BigInt(vout)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class StarknetSpvVaultContract
|
|
44
|
-
extends StarknetContractBase<typeof SpvVaultContractAbi>
|
|
45
|
-
implements SpvVaultContract<
|
|
46
|
-
StarknetTx,
|
|
47
|
-
StarknetSigner,
|
|
48
|
-
"STARKNET",
|
|
49
|
-
StarknetSpvVaultData,
|
|
50
|
-
StarknetSpvWithdrawalData
|
|
51
|
-
>
|
|
52
|
-
{
|
|
53
|
-
private static readonly GasCosts = {
|
|
54
|
-
DEPOSIT: {l1DataGas: 400, l2Gas: 4_000_000, l1Gas: 0},
|
|
55
|
-
OPEN: {l1DataGas: 1200, l2Gas: 3_200_000, l1Gas: 0},
|
|
56
|
-
FRONT: {l1DataGas: 800, l2Gas: 12_000_000, l1Gas: 0},
|
|
57
|
-
CLAIM: {l1DataGas: 1000, l2Gas: 400_000_000, l1Gas: 0}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
readonly chainId = "STARKNET";
|
|
61
|
-
|
|
62
|
-
readonly btcRelay: StarknetBtcRelay<any>;
|
|
63
|
-
readonly bitcoinRpc: BitcoinRpc<any>;
|
|
64
|
-
readonly claimTimeout: number = 180;
|
|
65
|
-
readonly maxClaimsPerTx: number = 10;
|
|
66
|
-
|
|
67
|
-
readonly logger = getLogger("StarknetSpvVaultContract: ");
|
|
68
|
-
|
|
69
|
-
constructor(
|
|
70
|
-
chainInterface: StarknetChainInterface,
|
|
71
|
-
btcRelay: StarknetBtcRelay<any>,
|
|
72
|
-
bitcoinRpc: BitcoinRpc<any>,
|
|
73
|
-
contractAddress: string = spvVaultContractAddreses[chainInterface.starknetChainId]
|
|
74
|
-
) {
|
|
75
|
-
super(chainInterface, contractAddress, SpvVaultContractAbi);
|
|
76
|
-
this.btcRelay = btcRelay;
|
|
77
|
-
this.bitcoinRpc = bitcoinRpc;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
//StarknetActions
|
|
81
|
-
protected Open(signer: string, vault: StarknetSpvVaultData): StarknetAction {
|
|
82
|
-
const {txHash, vout} = decodeUtxo(vault.getUtxo());
|
|
83
|
-
|
|
84
|
-
const tokens = vault.getTokenData();
|
|
85
|
-
if(tokens.length!==2) throw new Error("Must specify exactly 2 tokens for vault!");
|
|
86
|
-
|
|
87
|
-
return new StarknetAction(signer, this.Chain,
|
|
88
|
-
this.contract.populateTransaction.open(
|
|
89
|
-
vault.getVaultId(), this.btcRelay.contract.address,
|
|
90
|
-
cairo.tuple(cairo.uint256(txHash), vout), vault.getConfirmations(),
|
|
91
|
-
tokens[0].token, tokens[1].token, tokens[0].multiplier, tokens[1].multiplier
|
|
92
|
-
),
|
|
93
|
-
StarknetSpvVaultContract.GasCosts.OPEN
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
protected Deposit(signer: string, vault: StarknetSpvVaultData, rawAmounts: bigint[]): StarknetAction {
|
|
98
|
-
return new StarknetAction(signer, this.Chain,
|
|
99
|
-
this.contract.populateTransaction.deposit(vault.getOwner(), vault.getVaultId(), rawAmounts[0], rawAmounts[1] ?? 0n),
|
|
100
|
-
StarknetSpvVaultContract.GasCosts.DEPOSIT
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected Front(signer: string, vault: StarknetSpvVaultData, data: StarknetSpvWithdrawalData, withdrawalSequence: number) {
|
|
105
|
-
return new StarknetAction(signer, this.Chain,
|
|
106
|
-
this.contract.populateTransaction.front(
|
|
107
|
-
vault.getOwner(), vault.getVaultId(), BigInt(withdrawalSequence),
|
|
108
|
-
data.getTxHash(), data.serializeToStruct()
|
|
109
|
-
),
|
|
110
|
-
StarknetSpvVaultContract.GasCosts.FRONT
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
protected Claim(
|
|
115
|
-
signer: string, vault: StarknetSpvVaultData, data: StarknetSpvWithdrawalData,
|
|
116
|
-
blockheader: StarknetBtcStoredHeader, merkle: Buffer[], position: number
|
|
117
|
-
) {
|
|
118
|
-
return new StarknetAction(signer, this.Chain,
|
|
119
|
-
{
|
|
120
|
-
contractAddress: this.contract.address,
|
|
121
|
-
entrypoint: "claim",
|
|
122
|
-
calldata: [
|
|
123
|
-
vault.getOwner(),
|
|
124
|
-
vault.getVaultId(),
|
|
125
|
-
...bufferToByteArray(Buffer.from(data.btcTx.hex, "hex")),
|
|
126
|
-
...blockheader.serialize(),
|
|
127
|
-
merkle.length,
|
|
128
|
-
...merkle.map(bufferToU32Array).flat(),
|
|
129
|
-
position,
|
|
130
|
-
].map(val => toHex(val, 0))
|
|
131
|
-
},
|
|
132
|
-
StarknetSpvVaultContract.GasCosts.CLAIM
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async checkWithdrawalTx(tx: SpvWithdrawalTransactionData): Promise<void> {
|
|
137
|
-
const result = await this.Chain.provider.callContract({
|
|
138
|
-
contractAddress: this.contract.address,
|
|
139
|
-
entrypoint: "parse_bitcoin_tx",
|
|
140
|
-
calldata: bufferToByteArray(Buffer.from(tx.btcTx.hex, "hex"))
|
|
141
|
-
});
|
|
142
|
-
if(result==null) throw new Error("Failed to parse transaction!");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
createVaultData(owner: string, vaultId: bigint, utxo: string, confirmations: number, tokenData: SpvVaultTokenData[]): Promise<StarknetSpvVaultData> {
|
|
146
|
-
if(tokenData.length!==2) throw new Error("Must specify 2 tokens in tokenData!");
|
|
147
|
-
return Promise.resolve(new StarknetSpvVaultData(owner, vaultId, {
|
|
148
|
-
relay_contract: this.btcRelay.contract.address,
|
|
149
|
-
token_0: tokenData[0].token,
|
|
150
|
-
token_1: tokenData[1].token,
|
|
151
|
-
token_0_multiplier: tokenData[0].multiplier,
|
|
152
|
-
token_1_multiplier: tokenData[1].multiplier,
|
|
153
|
-
utxo: cairo.tuple(cairo.uint256(0), 0),
|
|
154
|
-
confirmations: confirmations,
|
|
155
|
-
withdraw_count: 0,
|
|
156
|
-
deposit_count: 0,
|
|
157
|
-
token_0_amount: 0n,
|
|
158
|
-
token_1_amount: 0n
|
|
159
|
-
}, utxo));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
//Getters
|
|
163
|
-
async getVaultData(owner: string, vaultId: bigint): Promise<StarknetSpvVaultData> {
|
|
164
|
-
const struct = await this.contract.get_vault(owner, vaultId);
|
|
165
|
-
if(toHex(struct.relay_contract)!==toHex(this.btcRelay.contract.address)) return null;
|
|
166
|
-
return new StarknetSpvVaultData(owner, vaultId, struct);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async getAllVaults(owner?: string): Promise<StarknetSpvVaultData[]> {
|
|
170
|
-
const openedVaults = new Set<string>();
|
|
171
|
-
await this.Events.findInContractEventsForward(
|
|
172
|
-
["spv_swap_vault::events::Opened", "spv_swap_vault::events::Closed"],
|
|
173
|
-
owner==null ? null : [null, owner],
|
|
174
|
-
(event) => {
|
|
175
|
-
const owner = toHex(event.keys[2]);
|
|
176
|
-
const vaultId = toBigInt(event.keys[3]);
|
|
177
|
-
const vaultIdentifier = owner+":"+vaultId.toString(10);
|
|
178
|
-
if(event.name==="spv_swap_vault::events::Opened") {
|
|
179
|
-
openedVaults.add(vaultIdentifier);
|
|
180
|
-
} else {
|
|
181
|
-
openedVaults.delete(vaultIdentifier);
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
);
|
|
186
|
-
const vaults: StarknetSpvVaultData[] = [];
|
|
187
|
-
for(let identifier of openedVaults.keys()) {
|
|
188
|
-
const [owner, vaultIdStr] = identifier.split(":");
|
|
189
|
-
const vaultData = await this.getVaultData(owner, BigInt(vaultIdStr));
|
|
190
|
-
if(vaultData!=null) vaults.push(vaultData);
|
|
191
|
-
}
|
|
192
|
-
return vaults;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
} else if(data.length===
|
|
266
|
-
rawAmount0 = data.readBigInt64LE(32).valueOf();
|
|
267
|
-
rawAmount1 = data.readBigInt64LE(40).valueOf();
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
async
|
|
321
|
-
const result = await this.
|
|
322
|
-
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
323
|
-
return signature;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async
|
|
327
|
-
const result = await this.
|
|
328
|
-
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
329
|
-
return signature;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
async
|
|
333
|
-
const result = await this.
|
|
334
|
-
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
335
|
-
return signature;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
signer
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
1
|
+
import {
|
|
2
|
+
BitcoinRpc,
|
|
3
|
+
BtcTx,
|
|
4
|
+
RelaySynchronizer,
|
|
5
|
+
SpvVaultContract,
|
|
6
|
+
SpvVaultTokenData,
|
|
7
|
+
SpvWithdrawalState,
|
|
8
|
+
SpvWithdrawalStateType,
|
|
9
|
+
SpvWithdrawalTransactionData,
|
|
10
|
+
TransactionConfirmationOptions
|
|
11
|
+
} from "@atomiqlabs/base";
|
|
12
|
+
import {Buffer} from "buffer";
|
|
13
|
+
import {StarknetTx} from "../chain/modules/StarknetTransactions";
|
|
14
|
+
import {StarknetContractBase} from "../contract/StarknetContractBase";
|
|
15
|
+
import {StarknetChainInterface} from "../chain/StarknetChainInterface";
|
|
16
|
+
import {StarknetBtcRelay} from "../btcrelay/StarknetBtcRelay";
|
|
17
|
+
import {cairo, constants, merkle} from "starknet";
|
|
18
|
+
import {StarknetAction} from "../chain/StarknetAction";
|
|
19
|
+
import {SpvVaultContractAbi} from "./SpvVaultContractAbi";
|
|
20
|
+
import {StarknetSigner} from "../wallet/StarknetSigner";
|
|
21
|
+
import {StarknetSpvVaultData} from "./StarknetSpvVaultData";
|
|
22
|
+
import {StarknetSpvWithdrawalData} from "./StarknetSpvWithdrawalData";
|
|
23
|
+
import {bigNumberishToBuffer, bufferToByteArray, bufferToU32Array, getLogger, toBigInt, toHex} from "../../utils/Utils";
|
|
24
|
+
import {StarknetBtcStoredHeader} from "../btcrelay/headers/StarknetBtcStoredHeader";
|
|
25
|
+
import {StarknetAddresses} from "../chain/modules/StarknetAddresses";
|
|
26
|
+
import {StarknetFees} from "../chain/modules/StarknetFees";
|
|
27
|
+
|
|
28
|
+
const spvVaultContractAddreses = {
|
|
29
|
+
[constants.StarknetChainId.SN_SEPOLIA]: "0x047961ea0687a2e3207478d386779bd5ec22aa8abc234319ccd723e2d7191a0c",
|
|
30
|
+
[constants.StarknetChainId.SN_MAIN]: "0x06ee5228af01baa443657ccda27b80637a609d43a97ed2f8fd478313e10abf4e"
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const STARK_PRIME_MOD: bigint = 2n**251n + 17n * 2n**192n + 1n;
|
|
34
|
+
|
|
35
|
+
function decodeUtxo(utxo: string): {txHash: bigint, vout: bigint} {
|
|
36
|
+
const [txId, vout] = utxo.split(":");
|
|
37
|
+
return {
|
|
38
|
+
txHash: BigInt("0x"+Buffer.from(txId, "hex").reverse().toString("hex")),
|
|
39
|
+
vout: BigInt(vout)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class StarknetSpvVaultContract
|
|
44
|
+
extends StarknetContractBase<typeof SpvVaultContractAbi>
|
|
45
|
+
implements SpvVaultContract<
|
|
46
|
+
StarknetTx,
|
|
47
|
+
StarknetSigner,
|
|
48
|
+
"STARKNET",
|
|
49
|
+
StarknetSpvVaultData,
|
|
50
|
+
StarknetSpvWithdrawalData
|
|
51
|
+
>
|
|
52
|
+
{
|
|
53
|
+
private static readonly GasCosts = {
|
|
54
|
+
DEPOSIT: {l1DataGas: 400, l2Gas: 4_000_000, l1Gas: 0},
|
|
55
|
+
OPEN: {l1DataGas: 1200, l2Gas: 3_200_000, l1Gas: 0},
|
|
56
|
+
FRONT: {l1DataGas: 800, l2Gas: 12_000_000, l1Gas: 0},
|
|
57
|
+
CLAIM: {l1DataGas: 1000, l2Gas: 400_000_000, l1Gas: 0}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
readonly chainId = "STARKNET";
|
|
61
|
+
|
|
62
|
+
readonly btcRelay: StarknetBtcRelay<any>;
|
|
63
|
+
readonly bitcoinRpc: BitcoinRpc<any>;
|
|
64
|
+
readonly claimTimeout: number = 180;
|
|
65
|
+
readonly maxClaimsPerTx: number = 10;
|
|
66
|
+
|
|
67
|
+
readonly logger = getLogger("StarknetSpvVaultContract: ");
|
|
68
|
+
|
|
69
|
+
constructor(
|
|
70
|
+
chainInterface: StarknetChainInterface,
|
|
71
|
+
btcRelay: StarknetBtcRelay<any>,
|
|
72
|
+
bitcoinRpc: BitcoinRpc<any>,
|
|
73
|
+
contractAddress: string = spvVaultContractAddreses[chainInterface.starknetChainId]
|
|
74
|
+
) {
|
|
75
|
+
super(chainInterface, contractAddress, SpvVaultContractAbi);
|
|
76
|
+
this.btcRelay = btcRelay;
|
|
77
|
+
this.bitcoinRpc = bitcoinRpc;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//StarknetActions
|
|
81
|
+
protected Open(signer: string, vault: StarknetSpvVaultData): StarknetAction {
|
|
82
|
+
const {txHash, vout} = decodeUtxo(vault.getUtxo());
|
|
83
|
+
|
|
84
|
+
const tokens = vault.getTokenData();
|
|
85
|
+
if(tokens.length!==2) throw new Error("Must specify exactly 2 tokens for vault!");
|
|
86
|
+
|
|
87
|
+
return new StarknetAction(signer, this.Chain,
|
|
88
|
+
this.contract.populateTransaction.open(
|
|
89
|
+
vault.getVaultId(), this.btcRelay.contract.address,
|
|
90
|
+
cairo.tuple(cairo.uint256(txHash), vout), vault.getConfirmations(),
|
|
91
|
+
tokens[0].token, tokens[1].token, tokens[0].multiplier, tokens[1].multiplier
|
|
92
|
+
),
|
|
93
|
+
StarknetSpvVaultContract.GasCosts.OPEN
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected Deposit(signer: string, vault: StarknetSpvVaultData, rawAmounts: bigint[]): StarknetAction {
|
|
98
|
+
return new StarknetAction(signer, this.Chain,
|
|
99
|
+
this.contract.populateTransaction.deposit(vault.getOwner(), vault.getVaultId(), rawAmounts[0], rawAmounts[1] ?? 0n),
|
|
100
|
+
StarknetSpvVaultContract.GasCosts.DEPOSIT
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected Front(signer: string, vault: StarknetSpvVaultData, data: StarknetSpvWithdrawalData, withdrawalSequence: number) {
|
|
105
|
+
return new StarknetAction(signer, this.Chain,
|
|
106
|
+
this.contract.populateTransaction.front(
|
|
107
|
+
vault.getOwner(), vault.getVaultId(), BigInt(withdrawalSequence),
|
|
108
|
+
data.getTxHash(), data.serializeToStruct()
|
|
109
|
+
),
|
|
110
|
+
StarknetSpvVaultContract.GasCosts.FRONT
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected Claim(
|
|
115
|
+
signer: string, vault: StarknetSpvVaultData, data: StarknetSpvWithdrawalData,
|
|
116
|
+
blockheader: StarknetBtcStoredHeader, merkle: Buffer[], position: number
|
|
117
|
+
) {
|
|
118
|
+
return new StarknetAction(signer, this.Chain,
|
|
119
|
+
{
|
|
120
|
+
contractAddress: this.contract.address,
|
|
121
|
+
entrypoint: "claim",
|
|
122
|
+
calldata: [
|
|
123
|
+
vault.getOwner(),
|
|
124
|
+
vault.getVaultId(),
|
|
125
|
+
...bufferToByteArray(Buffer.from(data.btcTx.hex, "hex")),
|
|
126
|
+
...blockheader.serialize(),
|
|
127
|
+
merkle.length,
|
|
128
|
+
...merkle.map(bufferToU32Array).flat(),
|
|
129
|
+
position,
|
|
130
|
+
].map(val => toHex(val, 0))
|
|
131
|
+
},
|
|
132
|
+
StarknetSpvVaultContract.GasCosts.CLAIM
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async checkWithdrawalTx(tx: SpvWithdrawalTransactionData): Promise<void> {
|
|
137
|
+
const result = await this.Chain.provider.callContract({
|
|
138
|
+
contractAddress: this.contract.address,
|
|
139
|
+
entrypoint: "parse_bitcoin_tx",
|
|
140
|
+
calldata: bufferToByteArray(Buffer.from(tx.btcTx.hex, "hex"))
|
|
141
|
+
});
|
|
142
|
+
if(result==null) throw new Error("Failed to parse transaction!");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
createVaultData(owner: string, vaultId: bigint, utxo: string, confirmations: number, tokenData: SpvVaultTokenData[]): Promise<StarknetSpvVaultData> {
|
|
146
|
+
if(tokenData.length!==2) throw new Error("Must specify 2 tokens in tokenData!");
|
|
147
|
+
return Promise.resolve(new StarknetSpvVaultData(owner, vaultId, {
|
|
148
|
+
relay_contract: this.btcRelay.contract.address,
|
|
149
|
+
token_0: tokenData[0].token,
|
|
150
|
+
token_1: tokenData[1].token,
|
|
151
|
+
token_0_multiplier: tokenData[0].multiplier,
|
|
152
|
+
token_1_multiplier: tokenData[1].multiplier,
|
|
153
|
+
utxo: cairo.tuple(cairo.uint256(0), 0),
|
|
154
|
+
confirmations: confirmations,
|
|
155
|
+
withdraw_count: 0,
|
|
156
|
+
deposit_count: 0,
|
|
157
|
+
token_0_amount: 0n,
|
|
158
|
+
token_1_amount: 0n
|
|
159
|
+
}, utxo));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//Getters
|
|
163
|
+
async getVaultData(owner: string, vaultId: bigint): Promise<StarknetSpvVaultData> {
|
|
164
|
+
const struct = await this.contract.get_vault(owner, vaultId);
|
|
165
|
+
if(toHex(struct.relay_contract)!==toHex(this.btcRelay.contract.address)) return null;
|
|
166
|
+
return new StarknetSpvVaultData(owner, vaultId, struct);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async getAllVaults(owner?: string): Promise<StarknetSpvVaultData[]> {
|
|
170
|
+
const openedVaults = new Set<string>();
|
|
171
|
+
await this.Events.findInContractEventsForward(
|
|
172
|
+
["spv_swap_vault::events::Opened", "spv_swap_vault::events::Closed"],
|
|
173
|
+
owner==null ? null : [null, owner],
|
|
174
|
+
(event) => {
|
|
175
|
+
const owner = toHex(event.keys[2]);
|
|
176
|
+
const vaultId = toBigInt(event.keys[3]);
|
|
177
|
+
const vaultIdentifier = owner+":"+vaultId.toString(10);
|
|
178
|
+
if(event.name==="spv_swap_vault::events::Opened") {
|
|
179
|
+
openedVaults.add(vaultIdentifier);
|
|
180
|
+
} else {
|
|
181
|
+
openedVaults.delete(vaultIdentifier);
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
const vaults: StarknetSpvVaultData[] = [];
|
|
187
|
+
for(let identifier of openedVaults.keys()) {
|
|
188
|
+
const [owner, vaultIdStr] = identifier.split(":");
|
|
189
|
+
const vaultData = await this.getVaultData(owner, BigInt(vaultIdStr));
|
|
190
|
+
if(vaultData!=null) vaults.push(vaultData);
|
|
191
|
+
}
|
|
192
|
+
return vaults;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async getFronterAddress(owner: string, vaultId: bigint, withdrawal: StarknetSpvWithdrawalData): Promise<string | null> {
|
|
196
|
+
const fronterAddress = await this.contract.get_fronter_address_by_id(owner, vaultId, "0x"+withdrawal.getFrontingId());
|
|
197
|
+
if(toHex(fronterAddress, 64)==="0x0000000000000000000000000000000000000000000000000000000000000000") return null;
|
|
198
|
+
return fronterAddress;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async getWithdrawalState(btcTxId: string): Promise<SpvWithdrawalState> {
|
|
202
|
+
const txHash = Buffer.from(btcTxId, "hex").reverse();
|
|
203
|
+
const txHashU256 = cairo.uint256("0x"+txHash.toString("hex"));
|
|
204
|
+
let result: SpvWithdrawalState = {
|
|
205
|
+
type: SpvWithdrawalStateType.NOT_FOUND
|
|
206
|
+
};
|
|
207
|
+
await this.Events.findInContractEventsForward(
|
|
208
|
+
["spv_swap_vault::events::Fronted", "spv_swap_vault::events::Claimed", "spv_swap_vault::events::Closed"],
|
|
209
|
+
[
|
|
210
|
+
toHex(txHashU256.low),
|
|
211
|
+
toHex(txHashU256.high)
|
|
212
|
+
],
|
|
213
|
+
async (event) => {
|
|
214
|
+
switch(event.name) {
|
|
215
|
+
case "spv_swap_vault::events::Fronted":
|
|
216
|
+
result = {
|
|
217
|
+
type: SpvWithdrawalStateType.FRONTED,
|
|
218
|
+
txId: event.txHash,
|
|
219
|
+
owner: toHex(event.keys[2]),
|
|
220
|
+
vaultId: toBigInt(event.keys[3]),
|
|
221
|
+
recipient: toHex(event.keys[4]),
|
|
222
|
+
fronter: toHex(event.keys[6])
|
|
223
|
+
};
|
|
224
|
+
break;
|
|
225
|
+
case "spv_swap_vault::events::Claimed":
|
|
226
|
+
result = {
|
|
227
|
+
type: SpvWithdrawalStateType.CLAIMED,
|
|
228
|
+
txId: event.txHash,
|
|
229
|
+
owner: toHex(event.keys[2]),
|
|
230
|
+
vaultId: toBigInt(event.keys[3]),
|
|
231
|
+
recipient: toHex(event.keys[4]),
|
|
232
|
+
claimer: toHex(event.keys[6]),
|
|
233
|
+
fronter: toHex(event.data[2])
|
|
234
|
+
};
|
|
235
|
+
break;
|
|
236
|
+
case "spv_swap_vault::events::Closed":
|
|
237
|
+
result = {
|
|
238
|
+
type: SpvWithdrawalStateType.CLOSED,
|
|
239
|
+
txId: event.txHash,
|
|
240
|
+
owner: toHex(event.keys[2]),
|
|
241
|
+
vaultId: toBigInt(event.keys[3]),
|
|
242
|
+
error: bigNumberishToBuffer(event.data[0]).toString()
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getWithdrawalData(btcTx: BtcTx): Promise<StarknetSpvWithdrawalData> {
|
|
252
|
+
return Promise.resolve(new StarknetSpvWithdrawalData(btcTx));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//OP_RETURN data encoding/decoding
|
|
256
|
+
fromOpReturnData(data: Buffer): { recipient: string; rawAmounts: bigint[]; executionHash: string } {
|
|
257
|
+
return StarknetSpvVaultContract.fromOpReturnData(data);
|
|
258
|
+
}
|
|
259
|
+
static fromOpReturnData(data: Buffer): { recipient: string; rawAmounts: bigint[]; executionHash: string } {
|
|
260
|
+
let rawAmount0: bigint = 0n;
|
|
261
|
+
let rawAmount1: bigint = 0n;
|
|
262
|
+
let executionHash: string = null;
|
|
263
|
+
if(data.length===40) {
|
|
264
|
+
rawAmount0 = data.readBigInt64LE(32).valueOf();
|
|
265
|
+
} else if(data.length===48) {
|
|
266
|
+
rawAmount0 = data.readBigInt64LE(32).valueOf();
|
|
267
|
+
rawAmount1 = data.readBigInt64LE(40).valueOf();
|
|
268
|
+
} else if(data.length===72) {
|
|
269
|
+
rawAmount0 = data.readBigInt64LE(32).valueOf();
|
|
270
|
+
executionHash = data.slice(40, 72).toString("hex");
|
|
271
|
+
} else if(data.length===80) {
|
|
272
|
+
rawAmount0 = data.readBigInt64LE(32).valueOf();
|
|
273
|
+
rawAmount1 = data.readBigInt64LE(40).valueOf();
|
|
274
|
+
executionHash = data.slice(48, 80).toString("hex");
|
|
275
|
+
} else {
|
|
276
|
+
throw new Error("Invalid OP_RETURN data length!");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if(executionHash!=null) {
|
|
280
|
+
const executionHashValue = BigInt("0x"+executionHash);
|
|
281
|
+
if(executionHashValue >= STARK_PRIME_MOD) throw new Error("Execution hash not in range of starknet prime");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const recipient = "0x"+data.slice(0, 32).toString("hex");
|
|
285
|
+
if(!StarknetAddresses.isValidAddress(recipient)) throw new Error("Invalid recipient specified");
|
|
286
|
+
|
|
287
|
+
return {executionHash, rawAmounts: [rawAmount0, rawAmount1], recipient};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
toOpReturnData(recipient: string, rawAmounts: bigint[], executionHash?: string): Buffer {
|
|
291
|
+
return StarknetSpvVaultContract.toOpReturnData(recipient, rawAmounts, executionHash);
|
|
292
|
+
}
|
|
293
|
+
static toOpReturnData(recipient: string, rawAmounts: bigint[], executionHash?: string): Buffer {
|
|
294
|
+
if(!StarknetAddresses.isValidAddress(recipient)) throw new Error("Invalid recipient specified");
|
|
295
|
+
if(rawAmounts.length < 1) throw new Error("At least 1 amount needs to be specified");
|
|
296
|
+
if(rawAmounts.length > 2) throw new Error("At most 2 amounts need to be specified");
|
|
297
|
+
rawAmounts.forEach(val => {
|
|
298
|
+
if(val < 0n) throw new Error("Negative raw amount specified");
|
|
299
|
+
if(val >= 2n**64n) throw new Error("Raw amount overflow");
|
|
300
|
+
});
|
|
301
|
+
if(executionHash!=null) {
|
|
302
|
+
const executionHashValue = toBigInt(executionHash);
|
|
303
|
+
if(executionHashValue < 0n) throw new Error("Execution hash negative");
|
|
304
|
+
if(executionHashValue >= STARK_PRIME_MOD) throw new Error("Execution hash not in range of starknet prime");
|
|
305
|
+
}
|
|
306
|
+
const recipientBuffer = Buffer.from(recipient.substring(2).padStart(64, "0"), "hex");
|
|
307
|
+
const amount0Buffer = Buffer.from(rawAmounts[0].toString(16).padStart(16, "0"), "hex");
|
|
308
|
+
const amount1Buffer = rawAmounts[1]==null || rawAmounts[1]===0n ? Buffer.alloc(0) : Buffer.from(rawAmounts[1].toString(16).padStart(16, "0"), "hex");
|
|
309
|
+
const executionHashBuffer = executionHash==null ? Buffer.alloc(0) : Buffer.from(executionHash.substring(2).padStart(64, "0"), "hex");
|
|
310
|
+
|
|
311
|
+
return Buffer.concat([
|
|
312
|
+
recipientBuffer,
|
|
313
|
+
amount0Buffer.reverse(),
|
|
314
|
+
amount1Buffer.reverse(),
|
|
315
|
+
executionHashBuffer
|
|
316
|
+
]);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
//Actions
|
|
320
|
+
async claim(signer: StarknetSigner, vault: StarknetSpvVaultData, txs: {tx: StarknetSpvWithdrawalData, storedHeader?: StarknetBtcStoredHeader}[], synchronizer?: RelaySynchronizer<any, any, any>, initAta?: boolean, txOptions?: TransactionConfirmationOptions): Promise<string> {
|
|
321
|
+
const result = await this.txsClaim(signer.getAddress(), vault, txs, synchronizer, initAta, txOptions?.feeRate);
|
|
322
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
323
|
+
return signature;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async deposit(signer: StarknetSigner, vault: StarknetSpvVaultData, rawAmounts: bigint[], txOptions?: TransactionConfirmationOptions): Promise<string> {
|
|
327
|
+
const result = await this.txsDeposit(signer.getAddress(), vault, rawAmounts, txOptions?.feeRate);
|
|
328
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
329
|
+
return signature;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async frontLiquidity(signer: StarknetSigner, vault: StarknetSpvVaultData, realWithdrawalTx: StarknetSpvWithdrawalData, withdrawSequence: number, txOptions?: TransactionConfirmationOptions): Promise<string> {
|
|
333
|
+
const result = await this.txsFrontLiquidity(signer.getAddress(), vault, realWithdrawalTx, withdrawSequence, txOptions?.feeRate);
|
|
334
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
335
|
+
return signature;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async open(signer: StarknetSigner, vault: StarknetSpvVaultData, txOptions?: TransactionConfirmationOptions): Promise<string> {
|
|
339
|
+
const result = await this.txsOpen(signer.getAddress(), vault, txOptions?.feeRate);
|
|
340
|
+
const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
|
|
341
|
+
return signature;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
//Transactions
|
|
345
|
+
async txsClaim(
|
|
346
|
+
signer: string, vault: StarknetSpvVaultData, txs: {
|
|
347
|
+
tx: StarknetSpvWithdrawalData,
|
|
348
|
+
storedHeader?: StarknetBtcStoredHeader
|
|
349
|
+
}[], synchronizer?: RelaySynchronizer<any, any, any>,
|
|
350
|
+
initAta?: boolean, feeRate?: string
|
|
351
|
+
): Promise<StarknetTx[]> {
|
|
352
|
+
if(!vault.isOpened()) throw new Error("Cannot claim from a closed vault!");
|
|
353
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
354
|
+
|
|
355
|
+
const txsWithMerkleProofs: {
|
|
356
|
+
tx: StarknetSpvWithdrawalData,
|
|
357
|
+
reversedTxId: Buffer,
|
|
358
|
+
pos: number,
|
|
359
|
+
blockheight: number,
|
|
360
|
+
merkle: Buffer[],
|
|
361
|
+
storedHeader?: StarknetBtcStoredHeader
|
|
362
|
+
}[] = [];
|
|
363
|
+
for(let tx of txs) {
|
|
364
|
+
const merkleProof = await this.bitcoinRpc.getMerkleProof(tx.tx.btcTx.txid, tx.tx.btcTx.blockhash);
|
|
365
|
+
this.logger.debug("txsClaim(): merkle proof computed: ", merkleProof);
|
|
366
|
+
txsWithMerkleProofs.push({
|
|
367
|
+
...merkleProof,
|
|
368
|
+
...tx
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const starknetTxs: StarknetTx[] = [];
|
|
373
|
+
const storedHeaders: {[blockhash: string]: StarknetBtcStoredHeader} = await StarknetBtcRelay.getCommitedHeadersAndSynchronize(
|
|
374
|
+
signer, this.btcRelay, txsWithMerkleProofs.filter(tx => tx.storedHeader==null).map(tx => {
|
|
375
|
+
return {
|
|
376
|
+
blockhash: tx.tx.btcTx.blockhash,
|
|
377
|
+
blockheight: tx.blockheight,
|
|
378
|
+
requiredConfirmations: vault.getConfirmations()
|
|
379
|
+
}
|
|
380
|
+
}), starknetTxs, synchronizer, feeRate
|
|
381
|
+
);
|
|
382
|
+
if(storedHeaders==null) throw new Error("Cannot fetch committed header!");
|
|
383
|
+
|
|
384
|
+
const actions = txsWithMerkleProofs.map(tx => {
|
|
385
|
+
return this.Claim(signer, vault, tx.tx, tx.storedHeader ?? storedHeaders[tx.tx.btcTx.blockhash], tx.merkle, tx.pos);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
let starknetAction = new StarknetAction(signer, this.Chain);
|
|
389
|
+
for(let action of actions) {
|
|
390
|
+
starknetAction.add(action);
|
|
391
|
+
if(starknetAction.ixsLength() >= this.maxClaimsPerTx) {
|
|
392
|
+
await starknetAction.addToTxs(starknetTxs, feeRate);
|
|
393
|
+
starknetAction = new StarknetAction(signer, this.Chain);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if(starknetAction.ixsLength() > 0) {
|
|
397
|
+
await starknetAction.addToTxs(starknetTxs, feeRate);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.logger.debug("txsClaim(): "+starknetTxs.length+" claim TXs created claiming "+txs.length+" txs, owner: "+vault.getOwner()+
|
|
401
|
+
" vaultId: "+vault.getVaultId().toString(10));
|
|
402
|
+
|
|
403
|
+
return starknetTxs;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async txsDeposit(signer: string, vault: StarknetSpvVaultData, rawAmounts: bigint[], feeRate?: string): Promise<StarknetTx[]> {
|
|
407
|
+
if(!vault.isOpened()) throw new Error("Cannot deposit to a closed vault!");
|
|
408
|
+
//Approve first
|
|
409
|
+
const vaultTokens = vault.getTokenData();
|
|
410
|
+
const action = new StarknetAction(signer, this.Chain);
|
|
411
|
+
let realAmount0 = 0n;
|
|
412
|
+
let realAmount1 = 0n;
|
|
413
|
+
if(rawAmounts[0]!=null && rawAmounts[0]!==0n) {
|
|
414
|
+
realAmount0 = rawAmounts[0] * vaultTokens[0].multiplier;
|
|
415
|
+
action.add(this.Chain.Tokens.Approve(signer, this.contract.address, vaultTokens[0].token, realAmount0));
|
|
416
|
+
}
|
|
417
|
+
if(rawAmounts[1]!=null && rawAmounts[1]!==0n) {
|
|
418
|
+
realAmount1 = rawAmounts[1] * vaultTokens[1].multiplier;
|
|
419
|
+
action.add(this.Chain.Tokens.Approve(signer, this.contract.address, vaultTokens[1].token, realAmount1));
|
|
420
|
+
}
|
|
421
|
+
action.add(this.Deposit(signer, vault, rawAmounts));
|
|
422
|
+
|
|
423
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
424
|
+
|
|
425
|
+
this.logger.debug("txsDeposit(): deposit TX created,"+
|
|
426
|
+
" token0: "+vaultTokens[0].token+" rawAmount0: "+rawAmounts[0].toString(10)+" amount0: "+realAmount0.toString(10)+
|
|
427
|
+
" token1: "+vaultTokens[1].token+" rawAmount1: "+(rawAmounts[1] ?? 0n).toString(10)+" amount1: "+realAmount1.toString(10));
|
|
428
|
+
|
|
429
|
+
return [await action.tx(feeRate)];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async txsFrontLiquidity(signer: string, vault: StarknetSpvVaultData, realWithdrawalTx: StarknetSpvWithdrawalData, withdrawSequence: number, feeRate?: string): Promise<StarknetTx[]> {
|
|
433
|
+
if(!vault.isOpened()) throw new Error("Cannot front on a closed vault!");
|
|
434
|
+
|
|
435
|
+
//Approve first
|
|
436
|
+
const vaultTokens = vault.getTokenData();
|
|
437
|
+
const action = new StarknetAction(signer, this.Chain);
|
|
438
|
+
const rawAmounts = realWithdrawalTx.getFrontingAmount();
|
|
439
|
+
let realAmount0 = 0n;
|
|
440
|
+
let realAmount1 = 0n;
|
|
441
|
+
if(rawAmounts[0]!=null && rawAmounts[0]!==0n) {
|
|
442
|
+
realAmount0 = rawAmounts[0] * vaultTokens[0].multiplier;
|
|
443
|
+
action.add(this.Chain.Tokens.Approve(signer, this.contract.address, vaultTokens[0].token, realAmount0));
|
|
444
|
+
}
|
|
445
|
+
if(rawAmounts[1]!=null && rawAmounts[1]!==0n) {
|
|
446
|
+
realAmount1 = rawAmounts[1] * vaultTokens[1].multiplier;
|
|
447
|
+
action.add(this.Chain.Tokens.Approve(signer, this.contract.address, vaultTokens[1].token, realAmount1));
|
|
448
|
+
}
|
|
449
|
+
action.add(this.Front(signer, vault, realWithdrawalTx, withdrawSequence));
|
|
450
|
+
|
|
451
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
452
|
+
|
|
453
|
+
this.logger.debug("txsFrontLiquidity(): front TX created,"+
|
|
454
|
+
" token0: "+vaultTokens[0].token+" rawAmount0: "+rawAmounts[0].toString(10)+" amount0: "+realAmount0.toString(10)+
|
|
455
|
+
" token1: "+vaultTokens[1].token+" rawAmount1: "+(rawAmounts[1] ?? 0n).toString(10)+" amount1: "+realAmount1.toString(10));
|
|
456
|
+
|
|
457
|
+
return [await action.tx(feeRate)];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async txsOpen(signer: string, vault: StarknetSpvVaultData, feeRate?: string): Promise<StarknetTx[]> {
|
|
461
|
+
if(vault.isOpened()) throw new Error("Cannot open an already opened vault!");
|
|
462
|
+
|
|
463
|
+
const action = this.Open(signer, vault);
|
|
464
|
+
|
|
465
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
466
|
+
|
|
467
|
+
this.logger.debug("txsOpen(): open TX created, owner: "+vault.getOwner()+
|
|
468
|
+
" vaultId: "+vault.getVaultId().toString(10));
|
|
469
|
+
|
|
470
|
+
return [await action.tx(feeRate)];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async getClaimFee(signer: string, vault: StarknetSpvVaultData, withdrawalData: StarknetSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
474
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
475
|
+
return StarknetFees.getGasFee(StarknetSpvVaultContract.GasCosts.CLAIM, feeRate);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async getFrontFee(signer: string, vault: StarknetSpvVaultData, withdrawalData: StarknetSpvWithdrawalData, feeRate?: string): Promise<bigint> {
|
|
479
|
+
feeRate ??= await this.Chain.Fees.getFeeRate();
|
|
480
|
+
return StarknetFees.getGasFee(StarknetSpvVaultContract.GasCosts.FRONT, feeRate);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
}
|