@atomiqlabs/chain-evm 1.0.0-dev.89 → 1.0.0-dev.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chains/botanix/BotanixInitializer.d.ts +2 -2
- package/dist/chains/botanix/BotanixInitializer.js +5 -1
- package/dist/chains/citrea/CitreaInitializer.d.ts +2 -2
- package/dist/chains/citrea/CitreaInitializer.js +5 -1
- package/dist/evm/chain/EVMChainInterface.d.ts +3 -0
- package/dist/evm/chain/modules/EVMEvents.js +36 -22
- package/dist/evm/contract/modules/EVMContractEvents.d.ts +2 -2
- package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +28 -0
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +130 -34
- package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +1 -0
- package/dist/evm/spv_swap/EVMSpvVaultData.js +7 -3
- package/dist/evm/swaps/EVMSwapContract.d.ts +6 -0
- package/dist/evm/swaps/EVMSwapContract.js +16 -0
- package/dist/utils/Utils.js +1 -1
- package/package.json +2 -2
- package/src/chains/botanix/BotanixInitializer.ts +9 -4
- package/src/chains/citrea/CitreaInitializer.ts +9 -4
- package/src/evm/chain/EVMChainInterface.ts +4 -1
- package/src/evm/chain/modules/EVMEvents.ts +43 -17
- package/src/evm/contract/modules/EVMContractEvents.ts +3 -3
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +143 -35
- package/src/evm/spv_swap/EVMSpvVaultData.ts +6 -2
- package/src/evm/swaps/EVMSwapContract.ts +21 -0
- package/src/utils/Utils.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType } from "@atomiqlabs/base";
|
|
2
2
|
import { JsonRpcApiProvider } from "ethers";
|
|
3
|
-
import { EVMRetryPolicy } from "../../evm/chain/EVMChainInterface";
|
|
3
|
+
import { EVMConfiguration, EVMRetryPolicy } from "../../evm/chain/EVMChainInterface";
|
|
4
4
|
import { EVMFees } from "../../evm/chain/modules/EVMFees";
|
|
5
5
|
import { BotanixChainType } from "./BotanixChainType";
|
|
6
6
|
export type BotanixAssetsType = BaseTokenType<"BBTC">;
|
|
@@ -9,7 +9,6 @@ export type BotanixOptions = {
|
|
|
9
9
|
rpcUrl: string | JsonRpcApiProvider;
|
|
10
10
|
retryPolicy?: EVMRetryPolicy;
|
|
11
11
|
chainType?: "MAINNET" | "TESTNET";
|
|
12
|
-
maxLogsBlockRange?: number;
|
|
13
12
|
swapContract?: string;
|
|
14
13
|
btcRelayContract?: string;
|
|
15
14
|
btcRelayDeploymentHeight?: number;
|
|
@@ -24,6 +23,7 @@ export type BotanixOptions = {
|
|
|
24
23
|
};
|
|
25
24
|
};
|
|
26
25
|
fees?: EVMFees;
|
|
26
|
+
evmConfig?: Omit<EVMConfiguration, "safeBlockTag">;
|
|
27
27
|
};
|
|
28
28
|
export declare function initializeBotanix(options: BotanixOptions, bitcoinRpc: BitcoinRpc<any>, network: BitcoinNetwork): ChainData<BotanixChainType>;
|
|
29
29
|
export type BotanixInitializerType = ChainInitializer<BotanixOptions, BotanixChainType, BotanixAssetsType>;
|
|
@@ -84,7 +84,11 @@ function initializeBotanix(options, bitcoinRpc, network) {
|
|
|
84
84
|
const Fees = options.fees ?? new EVMFees_1.EVMFees(provider, 2n * 1000000000n, 1000000n);
|
|
85
85
|
const chainInterface = new EVMChainInterface_1.EVMChainInterface("BOTANIX", chainId, provider, {
|
|
86
86
|
safeBlockTag: "finalized",
|
|
87
|
-
maxLogsBlockRange:
|
|
87
|
+
maxLogsBlockRange: 950,
|
|
88
|
+
maxLogTopics: 64,
|
|
89
|
+
maxParallelLogRequests: 5,
|
|
90
|
+
maxParallelCalls: 5,
|
|
91
|
+
...options?.evmConfig
|
|
88
92
|
}, options.retryPolicy, Fees);
|
|
89
93
|
const btcRelay = new EVMBtcRelay_1.EVMBtcRelay(chainInterface, bitcoinRpc, network, options.btcRelayContract ?? defaultContractAddresses.btcRelayContract, options.btcRelayDeploymentHeight ?? defaultContractAddresses.btcRelayDeploymentHeight);
|
|
90
94
|
const swapContract = new EVMSwapContract_1.EVMSwapContract(chainInterface, btcRelay, options.swapContract ?? defaultContractAddresses.swapContract, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType } from "@atomiqlabs/base";
|
|
2
2
|
import { JsonRpcApiProvider } from "ethers";
|
|
3
|
-
import { EVMRetryPolicy } from "../../evm/chain/EVMChainInterface";
|
|
3
|
+
import { EVMConfiguration, EVMRetryPolicy } from "../../evm/chain/EVMChainInterface";
|
|
4
4
|
import { CitreaChainType } from "./CitreaChainType";
|
|
5
5
|
import { CitreaFees } from "./CitreaFees";
|
|
6
6
|
export type CitreaAssetsType = BaseTokenType<"CBTC" | "USDC">;
|
|
@@ -9,7 +9,6 @@ export type CitreaOptions = {
|
|
|
9
9
|
rpcUrl: string | JsonRpcApiProvider;
|
|
10
10
|
retryPolicy?: EVMRetryPolicy;
|
|
11
11
|
chainType?: "MAINNET" | "TESTNET4";
|
|
12
|
-
maxLogsBlockRange?: number;
|
|
13
12
|
swapContract?: string;
|
|
14
13
|
btcRelayContract?: string;
|
|
15
14
|
btcRelayDeploymentHeight?: number;
|
|
@@ -24,6 +23,7 @@ export type CitreaOptions = {
|
|
|
24
23
|
};
|
|
25
24
|
};
|
|
26
25
|
fees?: CitreaFees;
|
|
26
|
+
evmConfig?: Omit<EVMConfiguration, "safeBlockTag">;
|
|
27
27
|
};
|
|
28
28
|
export declare function initializeCitrea(options: CitreaOptions, bitcoinRpc: BitcoinRpc<any>, network: BitcoinNetwork): ChainData<CitreaChainType>;
|
|
29
29
|
export type CitreaInitializerType = ChainInitializer<CitreaOptions, CitreaChainType, CitreaAssetsType>;
|
|
@@ -90,7 +90,11 @@ function initializeCitrea(options, bitcoinRpc, network) {
|
|
|
90
90
|
const Fees = options.fees ?? new CitreaFees_1.CitreaFees(provider, 2n * 1000000000n, 1000000n);
|
|
91
91
|
const chainInterface = new EVMChainInterface_1.EVMChainInterface("CITREA", chainId, provider, {
|
|
92
92
|
safeBlockTag: "latest",
|
|
93
|
-
maxLogsBlockRange:
|
|
93
|
+
maxLogsBlockRange: 950,
|
|
94
|
+
maxLogTopics: 64,
|
|
95
|
+
maxParallelLogRequests: 5,
|
|
96
|
+
maxParallelCalls: 5,
|
|
97
|
+
...options?.evmConfig
|
|
94
98
|
}, options.retryPolicy, Fees);
|
|
95
99
|
chainInterface.Tokens = new CitreaTokens_1.CitreaTokens(chainInterface); //Override with custom token module allowing l1 state diff based fee calculation
|
|
96
100
|
const btcRelay = new CitreaBtcRelay_1.CitreaBtcRelay(chainInterface, bitcoinRpc, network, options.btcRelayContract ?? defaultContractAddresses.btcRelayContract, options.btcRelayDeploymentHeight ?? defaultContractAddresses.btcRelayDeploymentHeight);
|
|
@@ -16,6 +16,9 @@ export type EVMRetryPolicy = {
|
|
|
16
16
|
export type EVMConfiguration = {
|
|
17
17
|
safeBlockTag: EVMBlockTag;
|
|
18
18
|
maxLogsBlockRange: number;
|
|
19
|
+
maxParallelLogRequests: number;
|
|
20
|
+
maxParallelCalls: number;
|
|
21
|
+
maxLogTopics: number;
|
|
19
22
|
};
|
|
20
23
|
export declare class EVMChainInterface<ChainId extends string = string> implements ChainInterface {
|
|
21
24
|
readonly chainId: ChainId;
|
|
@@ -91,19 +91,27 @@ class EVMEvents extends EVMModule_1.EVMModule {
|
|
|
91
91
|
*/
|
|
92
92
|
async findInEvents(contract, topics, processor, abortSignal, genesisHeight) {
|
|
93
93
|
const { number: latestBlockNumber } = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
94
|
+
let promises = [];
|
|
94
95
|
for (let blockNumber = latestBlockNumber; blockNumber >= (genesisHeight ?? 0); blockNumber -= this.root.config.maxLogsBlockRange) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
promises.push(this.getLogs(contract, topics, Math.max(blockNumber - this.root.config.maxLogsBlockRange, 0), blockNumber));
|
|
97
|
+
if (promises.length >= this.root.config.maxParallelLogRequests) {
|
|
98
|
+
const eventsResult = (await Promise.all(promises)).map(arr => arr.reverse() //Oldest events first
|
|
99
|
+
).flat();
|
|
100
|
+
promises = [];
|
|
101
|
+
if (abortSignal != null)
|
|
102
|
+
abortSignal.throwIfAborted();
|
|
103
|
+
const result = await processor(eventsResult);
|
|
104
|
+
if (result != null)
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
106
107
|
}
|
|
108
|
+
const eventsResult = (await Promise.all(promises)).map(arr => arr.reverse() //Oldest events first
|
|
109
|
+
).flat();
|
|
110
|
+
if (abortSignal != null)
|
|
111
|
+
abortSignal.throwIfAborted();
|
|
112
|
+
const result = await processor(eventsResult); //Oldest events first
|
|
113
|
+
if (result != null)
|
|
114
|
+
return result;
|
|
107
115
|
return null;
|
|
108
116
|
}
|
|
109
117
|
/**
|
|
@@ -118,19 +126,25 @@ class EVMEvents extends EVMModule_1.EVMModule {
|
|
|
118
126
|
*/
|
|
119
127
|
async findInEventsForward(contract, topics, processor, abortSignal, startHeight) {
|
|
120
128
|
const { number: latestBlockNumber } = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
129
|
+
let promises = [];
|
|
121
130
|
for (let blockNumber = startHeight ?? 0; blockNumber < latestBlockNumber; blockNumber += this.root.config.maxLogsBlockRange) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return result;
|
|
131
|
+
promises.push(this.getLogs(contract, topics, blockNumber, Math.min(blockNumber + this.root.config.maxLogsBlockRange, latestBlockNumber)));
|
|
132
|
+
if (promises.length >= this.root.config.maxParallelLogRequests) {
|
|
133
|
+
const eventsResult = (await Promise.all(promises)).flat();
|
|
134
|
+
promises = [];
|
|
135
|
+
if (abortSignal != null)
|
|
136
|
+
abortSignal.throwIfAborted();
|
|
137
|
+
const result = await processor(eventsResult); //Oldest events first
|
|
138
|
+
if (result != null)
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
133
141
|
}
|
|
142
|
+
const eventsResult = (await Promise.all(promises)).flat();
|
|
143
|
+
if (abortSignal != null)
|
|
144
|
+
abortSignal.throwIfAborted();
|
|
145
|
+
const result = await processor(eventsResult); //Oldest events first
|
|
146
|
+
if (result != null)
|
|
147
|
+
return result;
|
|
134
148
|
return null;
|
|
135
149
|
}
|
|
136
150
|
}
|
|
@@ -28,7 +28,7 @@ export declare class EVMContractEvents<T extends BaseContract> extends EVMEvents
|
|
|
28
28
|
* if the search should continue
|
|
29
29
|
* @param abortSignal
|
|
30
30
|
*/
|
|
31
|
-
findInContractEvents<TResult, TEventName extends keyof T["filters"]>(events: TEventName[], keys: string[], processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>, abortSignal?: AbortSignal): Promise<TResult>;
|
|
31
|
+
findInContractEvents<TResult, TEventName extends keyof T["filters"]>(events: TEventName[], keys: (string | string[])[], processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>, abortSignal?: AbortSignal): Promise<TResult>;
|
|
32
32
|
/**
|
|
33
33
|
* Runs a search forwards in time, processing the events for a specific topic
|
|
34
34
|
*
|
|
@@ -38,5 +38,5 @@ export declare class EVMContractEvents<T extends BaseContract> extends EVMEvents
|
|
|
38
38
|
* if the search should continue
|
|
39
39
|
* @param abortSignal
|
|
40
40
|
*/
|
|
41
|
-
findInContractEventsForward<TResult, TEventName extends keyof T["filters"]>(events: TEventName[], keys: string[], processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>, abortSignal?: AbortSignal): Promise<TResult>;
|
|
41
|
+
findInContractEventsForward<TResult, TEventName extends keyof T["filters"]>(events: TEventName[], keys: (string | string[])[], processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>, abortSignal?: AbortSignal): Promise<TResult>;
|
|
42
42
|
}
|
|
@@ -41,10 +41,38 @@ export declare class EVMSpvVaultContract<ChainId extends string> extends EVMCont
|
|
|
41
41
|
checkWithdrawalTx(tx: SpvWithdrawalTransactionData): Promise<void>;
|
|
42
42
|
createVaultData(owner: string, vaultId: bigint, utxo: string, confirmations: number, tokenData: SpvVaultTokenData[]): Promise<EVMSpvVaultData>;
|
|
43
43
|
getFronterAddress(owner: string, vaultId: bigint, withdrawal: EVMSpvWithdrawalData): Promise<string>;
|
|
44
|
+
getFronterAddresses(withdrawals: {
|
|
45
|
+
owner: string;
|
|
46
|
+
vaultId: bigint;
|
|
47
|
+
withdrawal: EVMSpvWithdrawalData;
|
|
48
|
+
}[]): Promise<{
|
|
49
|
+
[btcTxId: string]: string | null;
|
|
50
|
+
}>;
|
|
44
51
|
private vaultParamsCache;
|
|
45
52
|
getVaultData(owner: string, vaultId: bigint): Promise<EVMSpvVaultData>;
|
|
53
|
+
getMultipleVaultData(vaults: {
|
|
54
|
+
owner: string;
|
|
55
|
+
vaultId: bigint;
|
|
56
|
+
}[]): Promise<{
|
|
57
|
+
[owner: string]: {
|
|
58
|
+
[vaultId: string]: EVMSpvVaultData;
|
|
59
|
+
};
|
|
60
|
+
}>;
|
|
61
|
+
getVaultLatestUtxo(owner: string, vaultId: bigint): Promise<string | null>;
|
|
62
|
+
getVaultLatestUtxos(vaults: {
|
|
63
|
+
owner: string;
|
|
64
|
+
vaultId: bigint;
|
|
65
|
+
}[]): Promise<{
|
|
66
|
+
[owner: string]: {
|
|
67
|
+
[vaultId: string]: string | null;
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
46
70
|
getAllVaults(owner?: string): Promise<EVMSpvVaultData[]>;
|
|
71
|
+
private parseWithdrawalEvent;
|
|
47
72
|
getWithdrawalState(btcTxId: string): Promise<SpvWithdrawalState>;
|
|
73
|
+
getWithdrawalStates(btcTxIds: string[]): Promise<{
|
|
74
|
+
[btcTxId: string]: SpvWithdrawalState;
|
|
75
|
+
}>;
|
|
48
76
|
getWithdrawalData(btcTx: BtcTx): Promise<EVMSpvWithdrawalData>;
|
|
49
77
|
fromOpReturnData(data: Buffer): {
|
|
50
78
|
recipient: string;
|
|
@@ -125,6 +125,22 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
125
125
|
return null;
|
|
126
126
|
return frontingAddress;
|
|
127
127
|
}
|
|
128
|
+
async getFronterAddresses(withdrawals) {
|
|
129
|
+
const result = {};
|
|
130
|
+
let promises = [];
|
|
131
|
+
//TODO: We can upgrade this to use multicall
|
|
132
|
+
for (let { owner, vaultId, withdrawal } of withdrawals) {
|
|
133
|
+
promises.push(this.getFronterAddress(owner, vaultId, withdrawal).then(val => {
|
|
134
|
+
result[withdrawal.getTxId()] = val;
|
|
135
|
+
}));
|
|
136
|
+
if (promises.length >= this.Chain.config.maxParallelCalls) {
|
|
137
|
+
await Promise.all(promises);
|
|
138
|
+
promises = [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
await Promise.all(promises);
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
128
144
|
async getVaultData(owner, vaultId) {
|
|
129
145
|
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
130
146
|
const vaultParams = await this.vaultParamsCache.getOrComputeAsync(vaultState.spvVaultParametersCommitment, async () => {
|
|
@@ -142,6 +158,47 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
142
158
|
return null;
|
|
143
159
|
return new EVMSpvVaultData_1.EVMSpvVaultData(owner, vaultId, vaultState, vaultParams);
|
|
144
160
|
}
|
|
161
|
+
async getMultipleVaultData(vaults) {
|
|
162
|
+
const result = {};
|
|
163
|
+
let promises = [];
|
|
164
|
+
//TODO: We can upgrade this to use multicall
|
|
165
|
+
for (let { owner, vaultId } of vaults) {
|
|
166
|
+
promises.push(this.getVaultData(owner, vaultId).then(val => {
|
|
167
|
+
result[owner] ?? (result[owner] = {});
|
|
168
|
+
result[owner][vaultId.toString(10)] = val;
|
|
169
|
+
}));
|
|
170
|
+
if (promises.length >= this.Chain.config.maxParallelCalls) {
|
|
171
|
+
await Promise.all(promises);
|
|
172
|
+
promises = [];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
await Promise.all(promises);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
async getVaultLatestUtxo(owner, vaultId) {
|
|
179
|
+
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
180
|
+
const utxo = (0, EVMSpvVaultData_1.getVaultUtxoFromState)(vaultState);
|
|
181
|
+
if (utxo === "0000000000000000000000000000000000000000000000000000000000000000:0")
|
|
182
|
+
return null;
|
|
183
|
+
return utxo;
|
|
184
|
+
}
|
|
185
|
+
async getVaultLatestUtxos(vaults) {
|
|
186
|
+
const result = {};
|
|
187
|
+
let promises = [];
|
|
188
|
+
//TODO: We can upgrade this to use multicall
|
|
189
|
+
for (let { owner, vaultId } of vaults) {
|
|
190
|
+
promises.push(this.getVaultLatestUtxo(owner, vaultId).then(val => {
|
|
191
|
+
result[owner] ?? (result[owner] = {});
|
|
192
|
+
result[owner][vaultId.toString(10)] = val;
|
|
193
|
+
}));
|
|
194
|
+
if (promises.length >= this.Chain.config.maxParallelCalls) {
|
|
195
|
+
await Promise.all(promises);
|
|
196
|
+
promises = [];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
await Promise.all(promises);
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
145
202
|
async getAllVaults(owner) {
|
|
146
203
|
const openedVaults = new Map();
|
|
147
204
|
await this.Events.findInContractEventsForward(["Opened", "Closed"], owner == null ? null : [
|
|
@@ -167,6 +224,44 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
167
224
|
}
|
|
168
225
|
return vaults;
|
|
169
226
|
}
|
|
227
|
+
parseWithdrawalEvent(event) {
|
|
228
|
+
switch (event.eventName) {
|
|
229
|
+
case "Fronted":
|
|
230
|
+
const frontedEvent = event;
|
|
231
|
+
const [ownerFront, vaultIdFront] = unpackOwnerAndVaultId(frontedEvent.args.ownerAndVaultId);
|
|
232
|
+
return {
|
|
233
|
+
type: base_1.SpvWithdrawalStateType.FRONTED,
|
|
234
|
+
txId: event.transactionHash,
|
|
235
|
+
owner: ownerFront,
|
|
236
|
+
vaultId: vaultIdFront,
|
|
237
|
+
recipient: frontedEvent.args.recipient,
|
|
238
|
+
fronter: frontedEvent.args.caller
|
|
239
|
+
};
|
|
240
|
+
case "Claimed":
|
|
241
|
+
const claimedEvent = event;
|
|
242
|
+
const [ownerClaim, vaultIdClaim] = unpackOwnerAndVaultId(claimedEvent.args.ownerAndVaultId);
|
|
243
|
+
return {
|
|
244
|
+
type: base_1.SpvWithdrawalStateType.CLAIMED,
|
|
245
|
+
txId: event.transactionHash,
|
|
246
|
+
owner: ownerClaim,
|
|
247
|
+
vaultId: vaultIdClaim,
|
|
248
|
+
recipient: claimedEvent.args.recipient,
|
|
249
|
+
claimer: claimedEvent.args.caller,
|
|
250
|
+
fronter: claimedEvent.args.frontingAddress
|
|
251
|
+
};
|
|
252
|
+
case "Closed":
|
|
253
|
+
const closedEvent = event;
|
|
254
|
+
return {
|
|
255
|
+
type: base_1.SpvWithdrawalStateType.CLOSED,
|
|
256
|
+
txId: event.transactionHash,
|
|
257
|
+
owner: closedEvent.args.owner,
|
|
258
|
+
vaultId: closedEvent.args.vaultId,
|
|
259
|
+
error: closedEvent.args.error
|
|
260
|
+
};
|
|
261
|
+
default:
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
170
265
|
async getWithdrawalState(btcTxId) {
|
|
171
266
|
const txHash = buffer_1.Buffer.from(btcTxId, "hex").reverse();
|
|
172
267
|
let result = await this.Events.findInContractEvents(["Fronted", "Claimed", "Closed"], [
|
|
@@ -174,46 +269,47 @@ class EVMSpvVaultContract extends EVMContractBase_1.EVMContractBase {
|
|
|
174
269
|
null,
|
|
175
270
|
(0, ethers_1.hexlify)(txHash)
|
|
176
271
|
], async (event) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
}
|
|
272
|
+
const result = this.parseWithdrawalEvent(event);
|
|
273
|
+
if (result != null)
|
|
274
|
+
return result;
|
|
211
275
|
});
|
|
212
276
|
result ?? (result = {
|
|
213
277
|
type: base_1.SpvWithdrawalStateType.NOT_FOUND
|
|
214
278
|
});
|
|
215
279
|
return result;
|
|
216
280
|
}
|
|
281
|
+
async getWithdrawalStates(btcTxIds) {
|
|
282
|
+
const result = {};
|
|
283
|
+
for (let i = 0; i < btcTxIds.length; i += this.Chain.config.maxLogTopics) {
|
|
284
|
+
const checkBtcTxIds = btcTxIds.slice(i, i + this.Chain.config.maxLogTopics);
|
|
285
|
+
const checkBtcTxIdsSet = new Set(checkBtcTxIds);
|
|
286
|
+
await this.Events.findInContractEvents(["Fronted", "Claimed", "Closed"], [
|
|
287
|
+
null,
|
|
288
|
+
null,
|
|
289
|
+
checkBtcTxIds.map(btcTxId => (0, ethers_1.hexlify)(buffer_1.Buffer.from(btcTxId, "hex").reverse()))
|
|
290
|
+
], async (event) => {
|
|
291
|
+
const _event = event;
|
|
292
|
+
const btcTxId = buffer_1.Buffer.from(_event.args.btcTxHash.substring(2), "hex").reverse().toString("hex");
|
|
293
|
+
if (!checkBtcTxIdsSet.has(btcTxId)) {
|
|
294
|
+
this.logger.warn(`getWithdrawalStates(): findInContractEvents-callback: loaded event for ${btcTxId}, but transaction not found in input params!`);
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const eventResult = this.parseWithdrawalEvent(event);
|
|
298
|
+
if (eventResult == null)
|
|
299
|
+
return null;
|
|
300
|
+
checkBtcTxIdsSet.delete(btcTxId);
|
|
301
|
+
result[btcTxId] = eventResult;
|
|
302
|
+
if (checkBtcTxIdsSet.size === 0)
|
|
303
|
+
return true; //All processed
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
for (let btcTxId of btcTxIds) {
|
|
307
|
+
result[btcTxId] ?? (result[btcTxId] = {
|
|
308
|
+
type: base_1.SpvWithdrawalStateType.NOT_FOUND
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
217
313
|
getWithdrawalData(btcTx) {
|
|
218
314
|
return Promise.resolve(new EVMSpvWithdrawalData_1.EVMSpvWithdrawalData(btcTx));
|
|
219
315
|
}
|
|
@@ -2,6 +2,7 @@ import { SpvVaultClaimEvent, SpvVaultCloseEvent, SpvVaultData, SpvVaultDepositEv
|
|
|
2
2
|
import { EVMSpvWithdrawalData } from "./EVMSpvWithdrawalData";
|
|
3
3
|
import { SpvVaultParametersStruct, SpvVaultStateStruct } from "./SpvVaultContractTypechain";
|
|
4
4
|
export declare function getVaultParamsCommitment(vaultParams: SpvVaultParametersStruct): string;
|
|
5
|
+
export declare function getVaultUtxoFromState(state: SpvVaultStateStruct): string;
|
|
5
6
|
export declare class EVMSpvVaultData extends SpvVaultData<EVMSpvWithdrawalData> {
|
|
6
7
|
readonly owner: string;
|
|
7
8
|
readonly vaultId: bigint;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EVMSpvVaultData = exports.getVaultParamsCommitment = void 0;
|
|
3
|
+
exports.EVMSpvVaultData = exports.getVaultUtxoFromState = exports.getVaultParamsCommitment = void 0;
|
|
4
4
|
const base_1 = require("@atomiqlabs/base");
|
|
5
5
|
const buffer_1 = require("buffer");
|
|
6
6
|
const EVMSpvWithdrawalData_1 = require("./EVMSpvWithdrawalData");
|
|
@@ -11,6 +11,11 @@ function getVaultParamsCommitment(vaultParams) {
|
|
|
11
11
|
return (0, ethers_2.keccak256)(ethers_2.AbiCoder.defaultAbiCoder().encode(["address", "address", "address", "uint192", "uint192", "uint256"], [vaultParams.btcRelayContract, vaultParams.token0, vaultParams.token1, vaultParams.token0Multiplier, vaultParams.token1Multiplier, vaultParams.confirmations]));
|
|
12
12
|
}
|
|
13
13
|
exports.getVaultParamsCommitment = getVaultParamsCommitment;
|
|
14
|
+
function getVaultUtxoFromState(state) {
|
|
15
|
+
const txHash = buffer_1.Buffer.from((0, ethers_1.hexlify)(state.utxoTxHash).substring(2), "hex");
|
|
16
|
+
return txHash.reverse().toString("hex") + ":" + BigInt(state.utxoVout).toString(10);
|
|
17
|
+
}
|
|
18
|
+
exports.getVaultUtxoFromState = getVaultUtxoFromState;
|
|
14
19
|
class EVMSpvVaultData extends base_1.SpvVaultData {
|
|
15
20
|
constructor(ownerOrObj, vaultId, state, params, initialUtxo) {
|
|
16
21
|
super();
|
|
@@ -28,8 +33,7 @@ class EVMSpvVaultData extends base_1.SpvVaultData {
|
|
|
28
33
|
multiplier: BigInt(params.token1Multiplier),
|
|
29
34
|
rawAmount: BigInt(state.token1Amount)
|
|
30
35
|
};
|
|
31
|
-
|
|
32
|
-
this.utxo = txHash.reverse().toString("hex") + ":" + BigInt(state.utxoVout).toString(10);
|
|
36
|
+
this.utxo = getVaultUtxoFromState(state);
|
|
33
37
|
this.confirmations = Number(params.confirmations);
|
|
34
38
|
this.withdrawCount = Number(state.withdrawCount);
|
|
35
39
|
this.depositCount = Number(state.depositCount);
|
|
@@ -128,6 +128,12 @@ export declare class EVMSwapContract<ChainId extends string = string> extends EV
|
|
|
128
128
|
* @param data
|
|
129
129
|
*/
|
|
130
130
|
getCommitStatus(signer: string, data: EVMSwapData): Promise<SwapCommitState>;
|
|
131
|
+
getCommitStatuses(request: {
|
|
132
|
+
signer: string;
|
|
133
|
+
swapData: EVMSwapData;
|
|
134
|
+
}[]): Promise<{
|
|
135
|
+
[p: string]: SwapCommitState;
|
|
136
|
+
}>;
|
|
131
137
|
/**
|
|
132
138
|
* Returns the data committed for a specific payment hash, or null if no data is currently commited for
|
|
133
139
|
* the specific swap
|
|
@@ -243,6 +243,22 @@ class EVMSwapContract extends EVMContractBase_1.EVMContractBase {
|
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
+
async getCommitStatuses(request) {
|
|
247
|
+
const result = {};
|
|
248
|
+
let promises = [];
|
|
249
|
+
//TODO: We can upgrade this to use multicall
|
|
250
|
+
for (let { signer, swapData } of request) {
|
|
251
|
+
promises.push(this.getCommitStatus(signer, swapData).then(val => {
|
|
252
|
+
result[swapData.getEscrowHash()] = val;
|
|
253
|
+
}));
|
|
254
|
+
if (promises.length >= this.Chain.config.maxParallelCalls) {
|
|
255
|
+
await Promise.all(promises);
|
|
256
|
+
promises = [];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
await Promise.all(promises);
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
246
262
|
/**
|
|
247
263
|
* Returns the data committed for a specific payment hash, or null if no data is currently commited for
|
|
248
264
|
* the specific swap
|
package/dist/utils/Utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-evm",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.92",
|
|
4
4
|
"description": "EVM specific base implementation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types:": "./dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "adambor",
|
|
24
24
|
"license": "Apache-2.0",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@atomiqlabs/base": "^10.0.0-dev.
|
|
26
|
+
"@atomiqlabs/base": "^10.0.0-dev.13",
|
|
27
27
|
"@noble/hashes": "^1.8.0",
|
|
28
28
|
"@scure/btc-signer": "^1.6.0",
|
|
29
29
|
"buffer": "6.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType} from "@atomiqlabs/base";
|
|
2
2
|
import {JsonRpcApiProvider, JsonRpcProvider, WebSocketProvider} from "ethers";
|
|
3
|
-
import {EVMChainInterface, EVMRetryPolicy} from "../../evm/chain/EVMChainInterface";
|
|
3
|
+
import {EVMChainInterface, EVMConfiguration, EVMRetryPolicy} from "../../evm/chain/EVMChainInterface";
|
|
4
4
|
import {EVMFees} from "../../evm/chain/modules/EVMFees";
|
|
5
5
|
import {EVMBtcRelay} from "../../evm/btcrelay/EVMBtcRelay";
|
|
6
6
|
import {EVMSwapContract} from "../../evm/swaps/EVMSwapContract";
|
|
@@ -70,7 +70,6 @@ export type BotanixOptions = {
|
|
|
70
70
|
rpcUrl: string | JsonRpcApiProvider,
|
|
71
71
|
retryPolicy?: EVMRetryPolicy,
|
|
72
72
|
chainType?: "MAINNET" | "TESTNET",
|
|
73
|
-
maxLogsBlockRange?: number,
|
|
74
73
|
|
|
75
74
|
swapContract?: string,
|
|
76
75
|
btcRelayContract?: string,
|
|
@@ -86,7 +85,9 @@ export type BotanixOptions = {
|
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
fees?: EVMFees
|
|
88
|
+
fees?: EVMFees,
|
|
89
|
+
|
|
90
|
+
evmConfig?: Omit<EVMConfiguration, "safeBlockTag">
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
export function initializeBotanix(
|
|
@@ -120,7 +121,11 @@ export function initializeBotanix(
|
|
|
120
121
|
|
|
121
122
|
const chainInterface = new EVMChainInterface("BOTANIX", chainId, provider, {
|
|
122
123
|
safeBlockTag: "finalized",
|
|
123
|
-
maxLogsBlockRange:
|
|
124
|
+
maxLogsBlockRange: 950,
|
|
125
|
+
maxLogTopics: 64,
|
|
126
|
+
maxParallelLogRequests: 5,
|
|
127
|
+
maxParallelCalls: 5,
|
|
128
|
+
...options?.evmConfig
|
|
124
129
|
}, options.retryPolicy, Fees);
|
|
125
130
|
|
|
126
131
|
const btcRelay = new EVMBtcRelay(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType} from "@atomiqlabs/base";
|
|
2
2
|
import {JsonRpcApiProvider, JsonRpcProvider, WebSocketProvider} from "ethers";
|
|
3
|
-
import {EVMChainInterface, EVMRetryPolicy} from "../../evm/chain/EVMChainInterface";
|
|
3
|
+
import {EVMChainInterface, EVMConfiguration, EVMRetryPolicy} from "../../evm/chain/EVMChainInterface";
|
|
4
4
|
import {CitreaChainType} from "./CitreaChainType";
|
|
5
5
|
import {EVMChainEventsBrowser} from "../../evm/events/EVMChainEventsBrowser";
|
|
6
6
|
import {EVMSwapData} from "../../evm/swaps/EVMSwapData";
|
|
@@ -76,7 +76,6 @@ export type CitreaOptions = {
|
|
|
76
76
|
rpcUrl: string | JsonRpcApiProvider,
|
|
77
77
|
retryPolicy?: EVMRetryPolicy,
|
|
78
78
|
chainType?: "MAINNET" | "TESTNET4",
|
|
79
|
-
maxLogsBlockRange?: number,
|
|
80
79
|
|
|
81
80
|
swapContract?: string,
|
|
82
81
|
btcRelayContract?: string,
|
|
@@ -92,7 +91,9 @@ export type CitreaOptions = {
|
|
|
92
91
|
}
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
fees?: CitreaFees
|
|
94
|
+
fees?: CitreaFees,
|
|
95
|
+
|
|
96
|
+
evmConfig?: Omit<EVMConfiguration, "safeBlockTag">
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export function initializeCitrea(
|
|
@@ -126,7 +127,11 @@ export function initializeCitrea(
|
|
|
126
127
|
|
|
127
128
|
const chainInterface = new EVMChainInterface("CITREA", chainId, provider, {
|
|
128
129
|
safeBlockTag: "latest",
|
|
129
|
-
maxLogsBlockRange:
|
|
130
|
+
maxLogsBlockRange: 950,
|
|
131
|
+
maxLogTopics: 64,
|
|
132
|
+
maxParallelLogRequests: 5,
|
|
133
|
+
maxParallelCalls: 5,
|
|
134
|
+
...options?.evmConfig
|
|
130
135
|
}, options.retryPolicy, Fees);
|
|
131
136
|
chainInterface.Tokens = new CitreaTokens(chainInterface); //Override with custom token module allowing l1 state diff based fee calculation
|
|
132
137
|
|
|
@@ -18,7 +18,10 @@ export type EVMRetryPolicy = {
|
|
|
18
18
|
|
|
19
19
|
export type EVMConfiguration = {
|
|
20
20
|
safeBlockTag: EVMBlockTag,
|
|
21
|
-
maxLogsBlockRange: number
|
|
21
|
+
maxLogsBlockRange: number,
|
|
22
|
+
maxParallelLogRequests: number,
|
|
23
|
+
maxParallelCalls: number,
|
|
24
|
+
maxLogTopics: number
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
export class EVMChainInterface<ChainId extends string = string> implements ChainInterface {
|
|
@@ -102,19 +102,35 @@ export class EVMEvents extends EVMModule<any> {
|
|
|
102
102
|
): Promise<T> {
|
|
103
103
|
const {number: latestBlockNumber} = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
104
104
|
|
|
105
|
+
let promises: Promise<Log[]>[] = [];
|
|
105
106
|
for(let blockNumber = latestBlockNumber; blockNumber >= (genesisHeight ?? 0); blockNumber-=this.root.config.maxLogsBlockRange) {
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
promises.push(this.getLogs(
|
|
108
|
+
contract,
|
|
108
109
|
topics,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
Math.max(blockNumber-this.root.config.maxLogsBlockRange, 0),
|
|
111
|
+
blockNumber
|
|
112
|
+
));
|
|
112
113
|
|
|
113
|
-
if(
|
|
114
|
+
if(promises.length>=this.root.config.maxParallelLogRequests) {
|
|
115
|
+
const eventsResult = (await Promise.all(promises)).map(
|
|
116
|
+
arr => arr.reverse() //Oldest events first
|
|
117
|
+
).flat();
|
|
118
|
+
promises = [];
|
|
119
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
const result: T = await processor(eventsResult);
|
|
122
|
+
if(result!=null) return result;
|
|
123
|
+
}
|
|
117
124
|
}
|
|
125
|
+
|
|
126
|
+
const eventsResult = (await Promise.all(promises)).map(
|
|
127
|
+
arr => arr.reverse() //Oldest events first
|
|
128
|
+
).flat();
|
|
129
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
130
|
+
|
|
131
|
+
const result: T = await processor(eventsResult); //Oldest events first
|
|
132
|
+
if(result!=null) return result;
|
|
133
|
+
|
|
118
134
|
return null;
|
|
119
135
|
}
|
|
120
136
|
|
|
@@ -136,20 +152,30 @@ export class EVMEvents extends EVMModule<any> {
|
|
|
136
152
|
): Promise<T> {
|
|
137
153
|
const {number: latestBlockNumber} = await this.provider.getBlock(this.root.config.safeBlockTag);
|
|
138
154
|
|
|
155
|
+
let promises: Promise<Log[]>[] = [];
|
|
139
156
|
for(let blockNumber = startHeight ?? 0; blockNumber < latestBlockNumber; blockNumber += this.root.config.maxLogsBlockRange) {
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
promises.push(this.getLogs(
|
|
158
|
+
contract,
|
|
142
159
|
topics,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
blockNumber,
|
|
161
|
+
Math.min(blockNumber + this.root.config.maxLogsBlockRange, latestBlockNumber)
|
|
162
|
+
));
|
|
163
|
+
if(promises.length>=this.root.config.maxParallelLogRequests) {
|
|
164
|
+
const eventsResult = (await Promise.all(promises)).flat();
|
|
165
|
+
promises = [];
|
|
166
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
148
167
|
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
const result: T = await processor(eventsResult); //Oldest events first
|
|
169
|
+
if(result!=null) return result;
|
|
170
|
+
}
|
|
151
171
|
}
|
|
152
172
|
|
|
173
|
+
const eventsResult = (await Promise.all(promises)).flat();
|
|
174
|
+
if(abortSignal!=null) abortSignal.throwIfAborted();
|
|
175
|
+
|
|
176
|
+
const result: T = await processor(eventsResult); //Oldest events first
|
|
177
|
+
if(result!=null) return result;
|
|
178
|
+
|
|
153
179
|
return null;
|
|
154
180
|
}
|
|
155
181
|
|
|
@@ -21,7 +21,7 @@ export class EVMContractEvents<T extends BaseContract> extends EVMEvents {
|
|
|
21
21
|
|
|
22
22
|
private toFilter<TEventName extends keyof T["filters"]>(
|
|
23
23
|
events: TEventName[],
|
|
24
|
-
keys: string[],
|
|
24
|
+
keys: (string | string[])[],
|
|
25
25
|
): (string | string[])[] {
|
|
26
26
|
const filterArray: (string | string[])[] = [];
|
|
27
27
|
filterArray.push(events.map(name => {
|
|
@@ -61,7 +61,7 @@ export class EVMContractEvents<T extends BaseContract> extends EVMEvents {
|
|
|
61
61
|
*/
|
|
62
62
|
public async findInContractEvents<TResult, TEventName extends keyof T["filters"]>(
|
|
63
63
|
events: TEventName[],
|
|
64
|
-
keys: string[],
|
|
64
|
+
keys: (string | string[])[],
|
|
65
65
|
processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>,
|
|
66
66
|
abortSignal?: AbortSignal
|
|
67
67
|
): Promise<TResult> {
|
|
@@ -85,7 +85,7 @@ export class EVMContractEvents<T extends BaseContract> extends EVMEvents {
|
|
|
85
85
|
*/
|
|
86
86
|
public async findInContractEventsForward<TResult, TEventName extends keyof T["filters"]>(
|
|
87
87
|
events: TEventName[],
|
|
88
|
-
keys: string[],
|
|
88
|
+
keys: (string | string[])[],
|
|
89
89
|
processor: (event: TypedEventLog<T["filters"][TEventName]>) => Promise<TResult>,
|
|
90
90
|
abortSignal?: AbortSignal
|
|
91
91
|
): Promise<TResult> {
|
|
@@ -21,7 +21,7 @@ import {getLogger} from "../../utils/Utils";
|
|
|
21
21
|
import {EVMChainInterface} from "../chain/EVMChainInterface";
|
|
22
22
|
import {AbiCoder, getAddress, hexlify, keccak256, TransactionRequest, ZeroAddress, ZeroHash} from "ethers";
|
|
23
23
|
import {EVMAddresses} from "../chain/modules/EVMAddresses";
|
|
24
|
-
import {EVMSpvVaultData, getVaultParamsCommitment} from "./EVMSpvVaultData";
|
|
24
|
+
import {EVMSpvVaultData, getVaultParamsCommitment, getVaultUtxoFromState} from "./EVMSpvVaultData";
|
|
25
25
|
import {EVMSpvWithdrawalData} from "./EVMSpvWithdrawalData";
|
|
26
26
|
import {EVMFees} from "../chain/modules/EVMFees";
|
|
27
27
|
import {EVMBtcStoredHeader} from "../btcrelay/headers/EVMBtcStoredHeader";
|
|
@@ -210,6 +210,25 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
210
210
|
return frontingAddress;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
async getFronterAddresses(withdrawals: {owner: string, vaultId: bigint, withdrawal: EVMSpvWithdrawalData}[]): Promise<{[btcTxId: string]: string | null}> {
|
|
214
|
+
const result: {
|
|
215
|
+
[btcTxId: string]: string | null
|
|
216
|
+
} = {};
|
|
217
|
+
let promises: Promise<void>[] = [];
|
|
218
|
+
//TODO: We can upgrade this to use multicall
|
|
219
|
+
for(let {owner, vaultId, withdrawal} of withdrawals) {
|
|
220
|
+
promises.push(this.getFronterAddress(owner, vaultId, withdrawal).then(val => {
|
|
221
|
+
result[withdrawal.getTxId()] = val;
|
|
222
|
+
}));
|
|
223
|
+
if(promises.length>=this.Chain.config.maxParallelCalls) {
|
|
224
|
+
await Promise.all(promises);
|
|
225
|
+
promises = [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
await Promise.all(promises);
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
213
232
|
private vaultParamsCache: PromiseLruCache<string, SpvVaultParametersStructOutput> = new PromiseLruCache<string, SpvVaultParametersStructOutput>(5000);
|
|
214
233
|
|
|
215
234
|
async getVaultData(owner: string, vaultId: bigint): Promise<EVMSpvVaultData> {
|
|
@@ -239,6 +258,49 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
239
258
|
return new EVMSpvVaultData(owner, vaultId, vaultState, vaultParams);
|
|
240
259
|
}
|
|
241
260
|
|
|
261
|
+
async getMultipleVaultData(vaults: {owner: string, vaultId: bigint}[]): Promise<{[owner: string]: {[vaultId: string]: EVMSpvVaultData}}> {
|
|
262
|
+
const result: {[owner: string]: {[vaultId: string]: EVMSpvVaultData}} = {};
|
|
263
|
+
let promises: Promise<void>[] = [];
|
|
264
|
+
//TODO: We can upgrade this to use multicall
|
|
265
|
+
for(let {owner, vaultId} of vaults) {
|
|
266
|
+
promises.push(this.getVaultData(owner, vaultId).then(val => {
|
|
267
|
+
result[owner] ??= {};
|
|
268
|
+
result[owner][vaultId.toString(10)] = val;
|
|
269
|
+
}));
|
|
270
|
+
if(promises.length>=this.Chain.config.maxParallelCalls) {
|
|
271
|
+
await Promise.all(promises);
|
|
272
|
+
promises = [];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
await Promise.all(promises);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async getVaultLatestUtxo(owner: string, vaultId: bigint): Promise<string | null> {
|
|
280
|
+
const vaultState = await this.contract.getVault(owner, vaultId);
|
|
281
|
+
const utxo = getVaultUtxoFromState(vaultState);
|
|
282
|
+
if(utxo==="0000000000000000000000000000000000000000000000000000000000000000:0") return null;
|
|
283
|
+
return utxo;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async getVaultLatestUtxos(vaults: {owner: string, vaultId: bigint}[]): Promise<{[owner: string]: {[vaultId: string]: string | null}}> {
|
|
287
|
+
const result: {[owner: string]: {[vaultId: string]: string | null}} = {};
|
|
288
|
+
let promises: Promise<void>[] = [];
|
|
289
|
+
//TODO: We can upgrade this to use multicall
|
|
290
|
+
for(let {owner, vaultId} of vaults) {
|
|
291
|
+
promises.push(this.getVaultLatestUtxo(owner, vaultId).then(val => {
|
|
292
|
+
result[owner] ??= {};
|
|
293
|
+
result[owner][vaultId.toString(10)] = val;
|
|
294
|
+
}));
|
|
295
|
+
if(promises.length>=this.Chain.config.maxParallelCalls) {
|
|
296
|
+
await Promise.all(promises);
|
|
297
|
+
promises = [];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
await Promise.all(promises);
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
|
|
242
304
|
async getAllVaults(owner?: string): Promise<EVMSpvVaultData[]> {
|
|
243
305
|
const openedVaults = new Map<string, SpvVaultParametersStructOutput>();
|
|
244
306
|
await this.Events.findInContractEventsForward(
|
|
@@ -269,6 +331,45 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
269
331
|
return vaults;
|
|
270
332
|
}
|
|
271
333
|
|
|
334
|
+
private parseWithdrawalEvent(event: TypedEventLog<SpvVaultManager["filters"][keyof SpvVaultManager["filters"]]>): SpvWithdrawalState | null {
|
|
335
|
+
switch(event.eventName) {
|
|
336
|
+
case "Fronted":
|
|
337
|
+
const frontedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Fronted"]>;
|
|
338
|
+
const [ownerFront, vaultIdFront] = unpackOwnerAndVaultId(frontedEvent.args.ownerAndVaultId);
|
|
339
|
+
return {
|
|
340
|
+
type: SpvWithdrawalStateType.FRONTED,
|
|
341
|
+
txId: event.transactionHash,
|
|
342
|
+
owner: ownerFront,
|
|
343
|
+
vaultId: vaultIdFront,
|
|
344
|
+
recipient: frontedEvent.args.recipient,
|
|
345
|
+
fronter: frontedEvent.args.caller
|
|
346
|
+
};
|
|
347
|
+
case "Claimed":
|
|
348
|
+
const claimedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Claimed"]>;
|
|
349
|
+
const [ownerClaim, vaultIdClaim] = unpackOwnerAndVaultId(claimedEvent.args.ownerAndVaultId);
|
|
350
|
+
return {
|
|
351
|
+
type: SpvWithdrawalStateType.CLAIMED,
|
|
352
|
+
txId: event.transactionHash,
|
|
353
|
+
owner: ownerClaim,
|
|
354
|
+
vaultId: vaultIdClaim,
|
|
355
|
+
recipient: claimedEvent.args.recipient,
|
|
356
|
+
claimer: claimedEvent.args.caller,
|
|
357
|
+
fronter: claimedEvent.args.frontingAddress
|
|
358
|
+
};
|
|
359
|
+
case "Closed":
|
|
360
|
+
const closedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Closed"]>;
|
|
361
|
+
return {
|
|
362
|
+
type: SpvWithdrawalStateType.CLOSED,
|
|
363
|
+
txId: event.transactionHash,
|
|
364
|
+
owner: closedEvent.args.owner,
|
|
365
|
+
vaultId: closedEvent.args.vaultId,
|
|
366
|
+
error: closedEvent.args.error
|
|
367
|
+
};
|
|
368
|
+
default:
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
272
373
|
async getWithdrawalState(btcTxId: string): Promise<SpvWithdrawalState> {
|
|
273
374
|
const txHash = Buffer.from(btcTxId, "hex").reverse();
|
|
274
375
|
let result: SpvWithdrawalState = await this.Events.findInContractEvents(
|
|
@@ -279,40 +380,8 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
279
380
|
hexlify(txHash)
|
|
280
381
|
],
|
|
281
382
|
async (event) => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const frontedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Fronted"]>;
|
|
285
|
-
const [ownerFront, vaultIdFront] = unpackOwnerAndVaultId(frontedEvent.args.ownerAndVaultId);
|
|
286
|
-
return {
|
|
287
|
-
type: SpvWithdrawalStateType.FRONTED,
|
|
288
|
-
txId: event.transactionHash,
|
|
289
|
-
owner: ownerFront,
|
|
290
|
-
vaultId: vaultIdFront,
|
|
291
|
-
recipient: frontedEvent.args.recipient,
|
|
292
|
-
fronter: frontedEvent.args.caller
|
|
293
|
-
};
|
|
294
|
-
case "Claimed":
|
|
295
|
-
const claimedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Claimed"]>;
|
|
296
|
-
const [ownerClaim, vaultIdClaim] = unpackOwnerAndVaultId(claimedEvent.args.ownerAndVaultId);
|
|
297
|
-
return {
|
|
298
|
-
type: SpvWithdrawalStateType.CLAIMED,
|
|
299
|
-
txId: event.transactionHash,
|
|
300
|
-
owner: ownerClaim,
|
|
301
|
-
vaultId: vaultIdClaim,
|
|
302
|
-
recipient: claimedEvent.args.recipient,
|
|
303
|
-
claimer: claimedEvent.args.caller,
|
|
304
|
-
fronter: claimedEvent.args.frontingAddress
|
|
305
|
-
};
|
|
306
|
-
case "Closed":
|
|
307
|
-
const closedEvent = event as TypedEventLog<SpvVaultManager["filters"]["Closed"]>;
|
|
308
|
-
return {
|
|
309
|
-
type: SpvWithdrawalStateType.CLOSED,
|
|
310
|
-
txId: event.transactionHash,
|
|
311
|
-
owner: closedEvent.args.owner,
|
|
312
|
-
vaultId: closedEvent.args.vaultId,
|
|
313
|
-
error: closedEvent.args.error
|
|
314
|
-
};
|
|
315
|
-
}
|
|
383
|
+
const result = this.parseWithdrawalEvent(event);
|
|
384
|
+
if(result!=null) return result;
|
|
316
385
|
}
|
|
317
386
|
);
|
|
318
387
|
result ??= {
|
|
@@ -321,6 +390,45 @@ export class EVMSpvVaultContract<ChainId extends string>
|
|
|
321
390
|
return result;
|
|
322
391
|
}
|
|
323
392
|
|
|
393
|
+
async getWithdrawalStates(btcTxIds: string[]): Promise<{[btcTxId: string]: SpvWithdrawalState}> {
|
|
394
|
+
const result: {[btcTxId: string]: SpvWithdrawalState} = {};
|
|
395
|
+
|
|
396
|
+
for(let i=0;i<btcTxIds.length;i+=this.Chain.config.maxLogTopics) {
|
|
397
|
+
const checkBtcTxIds = btcTxIds.slice(i, i+this.Chain.config.maxLogTopics);
|
|
398
|
+
const checkBtcTxIdsSet = new Set(checkBtcTxIds);
|
|
399
|
+
|
|
400
|
+
await this.Events.findInContractEvents(
|
|
401
|
+
["Fronted", "Claimed", "Closed"],
|
|
402
|
+
[
|
|
403
|
+
null,
|
|
404
|
+
null,
|
|
405
|
+
checkBtcTxIds.map(btcTxId => hexlify(Buffer.from(btcTxId, "hex").reverse()))
|
|
406
|
+
],
|
|
407
|
+
async (event) => {
|
|
408
|
+
const _event = event as TypedEventLog<SpvVaultManager["filters"]["Fronted" | "Claimed" | "Closed"]>;
|
|
409
|
+
const btcTxId = Buffer.from(_event.args.btcTxHash.substring(2), "hex").reverse().toString("hex");
|
|
410
|
+
if(!checkBtcTxIdsSet.has(btcTxId)) {
|
|
411
|
+
this.logger.warn(`getWithdrawalStates(): findInContractEvents-callback: loaded event for ${btcTxId}, but transaction not found in input params!`)
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
const eventResult = this.parseWithdrawalEvent(event);
|
|
415
|
+
if(eventResult==null) return null;
|
|
416
|
+
checkBtcTxIdsSet.delete(btcTxId);
|
|
417
|
+
result[btcTxId] = eventResult;
|
|
418
|
+
if(checkBtcTxIdsSet.size===0) return true; //All processed
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
for(let btcTxId of btcTxIds) {
|
|
424
|
+
result[btcTxId] ??= {
|
|
425
|
+
type: SpvWithdrawalStateType.NOT_FOUND
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
|
|
324
432
|
getWithdrawalData(btcTx: BtcTx): Promise<EVMSpvWithdrawalData> {
|
|
325
433
|
return Promise.resolve(new EVMSpvWithdrawalData(btcTx));
|
|
326
434
|
}
|
|
@@ -23,6 +23,11 @@ export function getVaultParamsCommitment(vaultParams: SpvVaultParametersStruct)
|
|
|
23
23
|
));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function getVaultUtxoFromState(state: SpvVaultStateStruct): string {
|
|
27
|
+
const txHash = Buffer.from(hexlify(state.utxoTxHash).substring(2), "hex");
|
|
28
|
+
return txHash.reverse().toString("hex")+":"+BigInt(state.utxoVout).toString(10);
|
|
29
|
+
}
|
|
30
|
+
|
|
26
31
|
export class EVMSpvVaultData extends SpvVaultData<EVMSpvWithdrawalData> {
|
|
27
32
|
|
|
28
33
|
readonly owner: string;
|
|
@@ -63,8 +68,7 @@ export class EVMSpvVaultData extends SpvVaultData<EVMSpvWithdrawalData> {
|
|
|
63
68
|
multiplier: BigInt(params.token1Multiplier),
|
|
64
69
|
rawAmount: BigInt(state.token1Amount)
|
|
65
70
|
};
|
|
66
|
-
|
|
67
|
-
this.utxo = txHash.reverse().toString("hex")+":"+BigInt(state.utxoVout).toString(10);
|
|
71
|
+
this.utxo = getVaultUtxoFromState(state);
|
|
68
72
|
this.confirmations = Number(params.confirmations);
|
|
69
73
|
this.withdrawCount = Number(state.withdrawCount);
|
|
70
74
|
this.depositCount = Number(state.depositCount);
|
|
@@ -324,6 +324,27 @@ export class EVMSwapContract<ChainId extends string = string>
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
async getCommitStatuses(request: { signer: string; swapData: EVMSwapData }[]): Promise<{
|
|
328
|
+
[p: string]: SwapCommitState
|
|
329
|
+
}> {
|
|
330
|
+
const result: {
|
|
331
|
+
[p: string]: SwapCommitState
|
|
332
|
+
} = {};
|
|
333
|
+
let promises: Promise<void>[] = [];
|
|
334
|
+
//TODO: We can upgrade this to use multicall
|
|
335
|
+
for(let {signer, swapData} of request) {
|
|
336
|
+
promises.push(this.getCommitStatus(signer, swapData).then(val => {
|
|
337
|
+
result[swapData.getEscrowHash()] = val;
|
|
338
|
+
}));
|
|
339
|
+
if(promises.length>=this.Chain.config.maxParallelCalls) {
|
|
340
|
+
await Promise.all(promises);
|
|
341
|
+
promises = [];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
await Promise.all(promises);
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
|
|
327
348
|
/**
|
|
328
349
|
* Returns the data committed for a specific payment hash, or null if no data is currently commited for
|
|
329
350
|
* the specific swap
|
package/src/utils/Utils.ts
CHANGED
|
@@ -95,7 +95,7 @@ export const allowedEthersErrorNumbers: Set<number> = new Set([
|
|
|
95
95
|
-32700, //Invalid JSON
|
|
96
96
|
-32600, //Invalid request
|
|
97
97
|
-32601, //Method not found
|
|
98
|
-
|
|
98
|
+
-32602, //Invalid params
|
|
99
99
|
// -32603, //Internal error
|
|
100
100
|
-32000, //Invalid input
|
|
101
101
|
// -32001, //Resource not found
|