@atomiqlabs/chain-starknet 4.0.0-dev.20 → 4.0.0-dev.22

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.
@@ -1,6 +1,6 @@
1
1
  import { constants, Provider } from "starknet";
2
2
  import { StarknetFees } from "./chain/modules/StarknetFees";
3
- import { StarknetRetryPolicy } from "./chain/StarknetChainInterface";
3
+ import { StarknetConfig, StarknetRetryPolicy } from "./chain/StarknetChainInterface";
4
4
  import { BaseTokenType, BitcoinNetwork, BitcoinRpc, ChainData, ChainInitializer, ChainSwapType } from "@atomiqlabs/base";
5
5
  import { StarknetChainType } from "./StarknetChainType";
6
6
  export type StarknetAssetsType = BaseTokenType<"ETH" | "STRK" | "WBTC">;
@@ -21,6 +21,7 @@ export type StarknetOptions = {
21
21
  };
22
22
  };
23
23
  fees?: StarknetFees;
24
+ starknetConfig?: StarknetConfig;
24
25
  };
25
26
  export declare function initializeStarknet(options: StarknetOptions, bitcoinRpc: BitcoinRpc<any>, network: BitcoinNetwork): ChainData<StarknetChainType>;
26
27
  export type StarknetInitializerType = ChainInitializer<StarknetOptions, StarknetChainType, StarknetAssetsType>;
@@ -41,7 +41,7 @@ function initializeStarknet(options, bitcoinRpc, network) {
41
41
  const Fees = options.fees ?? new StarknetFees_1.StarknetFees(provider);
42
42
  const chainId = options.chainId ??
43
43
  (network === base_1.BitcoinNetwork.MAINNET ? starknet_1.constants.StarknetChainId.SN_MAIN : starknet_1.constants.StarknetChainId.SN_SEPOLIA);
44
- const chainInterface = new StarknetChainInterface_1.StarknetChainInterface(chainId, provider, options.retryPolicy, Fees);
44
+ const chainInterface = new StarknetChainInterface_1.StarknetChainInterface(chainId, provider, options.retryPolicy, Fees, options.starknetConfig);
45
45
  const btcRelay = new StarknetBtcRelay_1.StarknetBtcRelay(chainInterface, bitcoinRpc, network, options.btcRelayContract);
46
46
  const swapContract = new StarknetSwapContract_1.StarknetSwapContract(chainInterface, btcRelay, options.swapContract, options.handlerContracts);
47
47
  const spvVaultContract = new StarknetSpvVaultContract_1.StarknetSpvVaultContract(chainInterface, btcRelay, bitcoinRpc, options.spvVaultContract);
@@ -13,6 +13,12 @@ export type StarknetRetryPolicy = {
13
13
  delay?: number;
14
14
  exponential?: boolean;
15
15
  };
16
+ export type StarknetConfig = {
17
+ getLogChunkSize?: number;
18
+ getLogForwardBlockRange?: number;
19
+ maxGetLogKeys?: number;
20
+ maxParallelCalls?: number;
21
+ };
16
22
  export declare class StarknetChainInterface implements ChainInterface {
17
23
  readonly chainId = "STARKNET";
18
24
  readonly provider: Provider;
@@ -31,7 +37,8 @@ export declare class StarknetChainInterface implements ChainInterface {
31
37
  warn: (msg: any, ...args: any[]) => void;
32
38
  error: (msg: any, ...args: any[]) => void;
33
39
  };
34
- constructor(chainId: constants.StarknetChainId, provider: Provider, retryPolicy?: StarknetRetryPolicy, solanaFeeEstimator?: StarknetFees);
40
+ readonly config: StarknetConfig;
41
+ constructor(chainId: constants.StarknetChainId, provider: Provider, retryPolicy?: StarknetRetryPolicy, feeEstimator?: StarknetFees, options?: StarknetConfig);
35
42
  getBalance(signer: string, tokenAddress: string): Promise<bigint>;
36
43
  getNativeCurrencyAddress(): string;
37
44
  isValidToken(tokenIdentifier: string): boolean;
@@ -15,13 +15,19 @@ const StarknetSigner_1 = require("../wallet/StarknetSigner");
15
15
  const buffer_1 = require("buffer");
16
16
  const StarknetKeypairWallet_1 = require("../wallet/StarknetKeypairWallet");
17
17
  class StarknetChainInterface {
18
- constructor(chainId, provider, retryPolicy, solanaFeeEstimator = new StarknetFees_1.StarknetFees(provider)) {
18
+ constructor(chainId, provider, retryPolicy, feeEstimator = new StarknetFees_1.StarknetFees(provider), options) {
19
+ var _a, _b, _c, _d;
19
20
  this.chainId = "STARKNET";
20
21
  this.logger = (0, Utils_1.getLogger)("StarknetChainInterface: ");
21
22
  this.starknetChainId = chainId;
22
23
  this.provider = provider;
23
24
  this.retryPolicy = retryPolicy;
24
- this.Fees = solanaFeeEstimator;
25
+ this.config = options ?? {};
26
+ (_a = this.config).getLogForwardBlockRange ?? (_a.getLogForwardBlockRange = 2000);
27
+ (_b = this.config).getLogChunkSize ?? (_b.getLogChunkSize = 100);
28
+ (_c = this.config).maxGetLogKeys ?? (_c.maxGetLogKeys = 64);
29
+ (_d = this.config).maxParallelCalls ?? (_d.maxParallelCalls = 10);
30
+ this.Fees = feeEstimator;
25
31
  this.Tokens = new StarknetTokens_1.StarknetTokens(this);
26
32
  this.Transactions = new StarknetTransactions_1.StarknetTransactions(this);
27
33
  this.Signatures = new StarknetSignatures_1.StarknetSignatures(this);
@@ -27,7 +27,7 @@ export declare class StarknetContractEvents<TAbi extends Abi> extends StarknetEv
27
27
  * @param startBlockHeight
28
28
  * @param endBlockHeight
29
29
  */
30
- getContractBlockEvents<T extends ExtractAbiEventNames<TAbi>>(events: T[], keys: string[], startBlockHeight?: number, endBlockHeight?: number): Promise<StarknetAbiEvent<TAbi, T>[]>;
30
+ getContractBlockEvents<T extends ExtractAbiEventNames<TAbi>>(events: T[], keys: (string | string[])[], startBlockHeight?: number, endBlockHeight?: number): Promise<StarknetAbiEvent<TAbi, T>[]>;
31
31
  /**
32
32
  * Runs a search backwards in time, processing the events for a specific topic public key
33
33
  *
@@ -37,7 +37,7 @@ export declare class StarknetContractEvents<TAbi extends Abi> extends StarknetEv
37
37
  * if the search should continue
38
38
  * @param abortSignal
39
39
  */
40
- findInContractEvents<T, TEvent extends ExtractAbiEventNames<TAbi>>(events: TEvent[], keys: string[], processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>, abortSignal?: AbortSignal): Promise<T>;
40
+ findInContractEvents<T, TEvent extends ExtractAbiEventNames<TAbi>>(events: TEvent[], keys: (string | string[])[], processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>, abortSignal?: AbortSignal): Promise<T>;
41
41
  /**
42
42
  * Runs a search forwards in time, processing the events for a specific topic public key
43
43
  *
@@ -47,5 +47,5 @@ export declare class StarknetContractEvents<TAbi extends Abi> extends StarknetEv
47
47
  * if the search should continue
48
48
  * @param abortSignal
49
49
  */
50
- findInContractEventsForward<T, TEvent extends ExtractAbiEventNames<TAbi>>(events: TEvent[], keys: string[], processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>, abortSignal?: AbortSignal): Promise<T>;
50
+ findInContractEventsForward<T, TEvent extends ExtractAbiEventNames<TAbi>>(events: TEvent[], keys: (string | string[])[], processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>, abortSignal?: AbortSignal): Promise<T>;
51
51
  }
@@ -39,7 +39,7 @@ class StarknetContractEvents extends StarknetEvents_1.StarknetEvents {
39
39
  return (0, Utils_1.toHex)(starknet_1.hash.starknetKeccak(eventName));
40
40
  }));
41
41
  if (keys != null)
42
- keys.forEach(key => filterArray.push(key == null ? [] : [key]));
42
+ keys.forEach(key => filterArray.push(key == null ? [] : Array.isArray(key) ? key : [key]));
43
43
  return filterArray;
44
44
  }
45
45
  /**
@@ -31,8 +31,36 @@ export declare class StarknetSpvVaultContract extends StarknetContractBase<typeo
31
31
  checkWithdrawalTx(tx: SpvWithdrawalTransactionData): Promise<void>;
32
32
  createVaultData(owner: string, vaultId: bigint, utxo: string, confirmations: number, tokenData: SpvVaultTokenData[]): Promise<StarknetSpvVaultData>;
33
33
  getVaultData(owner: string, vaultId: bigint): Promise<StarknetSpvVaultData>;
34
+ getMultipleVaultData(vaults: {
35
+ owner: string;
36
+ vaultId: bigint;
37
+ }[]): Promise<{
38
+ [owner: string]: {
39
+ [vaultId: string]: StarknetSpvVaultData;
40
+ };
41
+ }>;
42
+ getVaultLatestUtxo(owner: string, vaultId: bigint): Promise<string | null>;
43
+ getVaultLatestUtxos(vaults: {
44
+ owner: string;
45
+ vaultId: bigint;
46
+ }[]): Promise<{
47
+ [owner: string]: {
48
+ [vaultId: string]: string | null;
49
+ };
50
+ }>;
34
51
  getAllVaults(owner?: string): Promise<StarknetSpvVaultData[]>;
35
52
  getFronterAddress(owner: string, vaultId: bigint, withdrawal: StarknetSpvWithdrawalData): Promise<string | null>;
53
+ getFronterAddresses(withdrawals: {
54
+ owner: string;
55
+ vaultId: bigint;
56
+ withdrawal: StarknetSpvWithdrawalData;
57
+ }[]): Promise<{
58
+ [btcTxId: string]: string | null;
59
+ }>;
60
+ private parseWithdrawalEvent;
61
+ getWithdrawalStates(btcTxIds: string[]): Promise<{
62
+ [btcTxId: string]: SpvWithdrawalState;
63
+ }>;
36
64
  getWithdrawalState(btcTxId: string): Promise<SpvWithdrawalState>;
37
65
  getWithdrawalData(btcTx: BtcTx): Promise<StarknetSpvWithdrawalData>;
38
66
  fromOpReturnData(data: Buffer): {
@@ -97,6 +97,48 @@ class StarknetSpvVaultContract extends StarknetContractBase_1.StarknetContractBa
97
97
  return null;
98
98
  return new StarknetSpvVaultData_1.StarknetSpvVaultData(owner, vaultId, struct);
99
99
  }
100
+ async getMultipleVaultData(vaults) {
101
+ const result = {};
102
+ let promises = [];
103
+ //TODO: We can upgrade this to use multicall
104
+ for (let { owner, vaultId } of vaults) {
105
+ promises.push(this.getVaultData(owner, vaultId).then(val => {
106
+ result[owner] ?? (result[owner] = {});
107
+ result[owner][vaultId.toString(10)] = val;
108
+ }));
109
+ if (promises.length >= this.Chain.config.maxParallelCalls) {
110
+ await Promise.all(promises);
111
+ promises = [];
112
+ }
113
+ }
114
+ await Promise.all(promises);
115
+ return result;
116
+ }
117
+ async getVaultLatestUtxo(owner, vaultId) {
118
+ const vault = await this.getVaultData(owner, vaultId);
119
+ if (vault == null)
120
+ return null;
121
+ if (!vault.isOpened())
122
+ return null;
123
+ return vault.getUtxo();
124
+ }
125
+ async getVaultLatestUtxos(vaults) {
126
+ const result = {};
127
+ let promises = [];
128
+ //TODO: We can upgrade this to use multicall
129
+ for (let { owner, vaultId } of vaults) {
130
+ promises.push(this.getVaultLatestUtxo(owner, vaultId).then(val => {
131
+ result[owner] ?? (result[owner] = {});
132
+ result[owner][vaultId.toString(10)] = val;
133
+ }));
134
+ if (promises.length >= this.Chain.config.maxParallelCalls) {
135
+ await Promise.all(promises);
136
+ promises = [];
137
+ }
138
+ }
139
+ await Promise.all(promises);
140
+ return result;
141
+ }
100
142
  async getAllVaults(owner) {
101
143
  const openedVaults = new Set();
102
144
  await this.Events.findInContractEventsForward(["spv_swap_vault::events::Opened", "spv_swap_vault::events::Closed"], owner == null ? null : [null, owner], (event) => {
@@ -126,6 +168,88 @@ class StarknetSpvVaultContract extends StarknetContractBase_1.StarknetContractBa
126
168
  return null;
127
169
  return fronterAddress;
128
170
  }
171
+ async getFronterAddresses(withdrawals) {
172
+ const result = {};
173
+ let promises = [];
174
+ //TODO: We can upgrade this to use multicall
175
+ for (let { owner, vaultId, withdrawal } of withdrawals) {
176
+ promises.push(this.getFronterAddress(owner, vaultId, withdrawal).then(val => {
177
+ result[withdrawal.getTxId()] = val;
178
+ }));
179
+ if (promises.length >= this.Chain.config.maxParallelCalls) {
180
+ await Promise.all(promises);
181
+ promises = [];
182
+ }
183
+ }
184
+ await Promise.all(promises);
185
+ return result;
186
+ }
187
+ parseWithdrawalEvent(event) {
188
+ switch (event.name) {
189
+ case "spv_swap_vault::events::Fronted":
190
+ return {
191
+ type: base_1.SpvWithdrawalStateType.FRONTED,
192
+ txId: event.txHash,
193
+ owner: (0, Utils_1.toHex)(event.params.owner),
194
+ vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
195
+ recipient: (0, Utils_1.toHex)(event.params.recipient),
196
+ fronter: (0, Utils_1.toHex)(event.params.fronting_address)
197
+ };
198
+ case "spv_swap_vault::events::Claimed":
199
+ return {
200
+ type: base_1.SpvWithdrawalStateType.CLAIMED,
201
+ txId: event.txHash,
202
+ owner: (0, Utils_1.toHex)(event.params.owner),
203
+ vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
204
+ recipient: (0, Utils_1.toHex)(event.params.recipient),
205
+ claimer: (0, Utils_1.toHex)(event.params.caller),
206
+ fronter: (0, Utils_1.toHex)(event.params.fronting_address)
207
+ };
208
+ case "spv_swap_vault::events::Closed":
209
+ return {
210
+ type: base_1.SpvWithdrawalStateType.CLOSED,
211
+ txId: event.txHash,
212
+ owner: (0, Utils_1.toHex)(event.params.owner),
213
+ vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
214
+ error: (0, Utils_1.bigNumberishToBuffer)(event.params.error).toString()
215
+ };
216
+ default:
217
+ return null;
218
+ }
219
+ }
220
+ async getWithdrawalStates(btcTxIds) {
221
+ const result = {};
222
+ btcTxIds.forEach(txId => {
223
+ result[txId] = {
224
+ type: base_1.SpvWithdrawalStateType.NOT_FOUND
225
+ };
226
+ });
227
+ for (let i = 0; i < btcTxIds.length; i += this.Chain.config.maxGetLogKeys) {
228
+ const checkBtcTxIds = btcTxIds.slice(i, i + this.Chain.config.maxGetLogKeys);
229
+ const lows = [];
230
+ const highs = [];
231
+ checkBtcTxIds.forEach(btcTxId => {
232
+ const txHash = buffer_1.Buffer.from(btcTxId, "hex").reverse();
233
+ const txHashU256 = starknet_1.cairo.uint256("0x" + txHash.toString("hex"));
234
+ lows.push((0, Utils_1.toHex)(txHashU256.low));
235
+ highs.push((0, Utils_1.toHex)(txHashU256.high));
236
+ });
237
+ await this.Events.findInContractEventsForward(["spv_swap_vault::events::Fronted", "spv_swap_vault::events::Claimed", "spv_swap_vault::events::Closed"], [
238
+ lows,
239
+ highs
240
+ ], async (event) => {
241
+ const txId = (0, Utils_1.bigNumberishToBuffer)(event.params.btc_tx_hash, 32).reverse().toString("hex");
242
+ if (result[txId] == null) {
243
+ this.logger.warn(`getWithdrawalStates(): findInContractEvents-callback: loaded event for ${txId}, but transaction not found in input params!`);
244
+ return;
245
+ }
246
+ const eventResult = this.parseWithdrawalEvent(event);
247
+ if (eventResult != null)
248
+ result[txId] = eventResult;
249
+ });
250
+ }
251
+ return result;
252
+ }
129
253
  async getWithdrawalState(btcTxId) {
130
254
  const txHash = buffer_1.Buffer.from(btcTxId, "hex").reverse();
131
255
  const txHashU256 = starknet_1.cairo.uint256("0x" + txHash.toString("hex"));
@@ -136,38 +260,9 @@ class StarknetSpvVaultContract extends StarknetContractBase_1.StarknetContractBa
136
260
  (0, Utils_1.toHex)(txHashU256.low),
137
261
  (0, Utils_1.toHex)(txHashU256.high)
138
262
  ], async (event) => {
139
- switch (event.name) {
140
- case "spv_swap_vault::events::Fronted":
141
- result = {
142
- type: base_1.SpvWithdrawalStateType.FRONTED,
143
- txId: event.txHash,
144
- owner: (0, Utils_1.toHex)(event.params.owner),
145
- vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
146
- recipient: (0, Utils_1.toHex)(event.params.recipient),
147
- fronter: (0, Utils_1.toHex)(event.params.fronting_address)
148
- };
149
- break;
150
- case "spv_swap_vault::events::Claimed":
151
- result = {
152
- type: base_1.SpvWithdrawalStateType.CLAIMED,
153
- txId: event.txHash,
154
- owner: (0, Utils_1.toHex)(event.params.owner),
155
- vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
156
- recipient: (0, Utils_1.toHex)(event.params.recipient),
157
- claimer: (0, Utils_1.toHex)(event.params.caller),
158
- fronter: (0, Utils_1.toHex)(event.params.fronting_address)
159
- };
160
- break;
161
- case "spv_swap_vault::events::Closed":
162
- result = {
163
- type: base_1.SpvWithdrawalStateType.CLOSED,
164
- txId: event.txHash,
165
- owner: (0, Utils_1.toHex)(event.params.owner),
166
- vaultId: (0, Utils_1.toBigInt)(event.params.vault_id),
167
- error: (0, Utils_1.bigNumberishToBuffer)(event.params.error).toString()
168
- };
169
- break;
170
- }
263
+ const eventResult = this.parseWithdrawalEvent(event);
264
+ if (eventResult != null)
265
+ result = eventResult;
171
266
  });
172
267
  return result;
173
268
  }
@@ -126,6 +126,12 @@ export declare class StarknetSwapContract extends StarknetContractBase<typeof Es
126
126
  * @param data
127
127
  */
128
128
  getCommitStatus(signer: string, data: StarknetSwapData): Promise<SwapCommitState>;
129
+ getCommitStatuses(request: {
130
+ signer: string;
131
+ swapData: StarknetSwapData;
132
+ }[]): Promise<{
133
+ [p: string]: SwapCommitState;
134
+ }>;
129
135
  /**
130
136
  * Returns the data committed for a specific payment hash, or null if no data is currently commited for
131
137
  * the specific swap
@@ -287,6 +287,22 @@ class StarknetSwapContract extends StarknetContractBase_1.StarknetContractBase {
287
287
  };
288
288
  }
289
289
  }
290
+ async getCommitStatuses(request) {
291
+ const result = {};
292
+ let promises = [];
293
+ //TODO: We can upgrade this to use multicall
294
+ for (let { signer, swapData } of request) {
295
+ promises.push(this.getCommitStatus(signer, swapData).then(val => {
296
+ result[swapData.getEscrowHash()] = val;
297
+ }));
298
+ if (promises.length >= this.Chain.config.maxParallelCalls) {
299
+ await Promise.all(promises);
300
+ promises = [];
301
+ }
302
+ }
303
+ await Promise.all(promises);
304
+ return result;
305
+ }
290
306
  /**
291
307
  * Returns the data committed for a specific payment hash, or null if no data is currently commited for
292
308
  * the specific swap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-starknet",
3
- "version": "4.0.0-dev.20",
3
+ "version": "4.0.0-dev.22",
4
4
  "description": "Starknet specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  "url": "git+https://github.com/atomiqlabs/atomiq-chain-starknet.git"
29
29
  },
30
30
  "dependencies": {
31
- "@atomiqlabs/base": "^10.0.0-dev.9",
31
+ "@atomiqlabs/base": "^10.0.0-dev.13",
32
32
  "@noble/hashes": "^1.7.1",
33
33
  "@scure/btc-signer": "^1.6.0",
34
34
  "abi-wan-kanabi": "2.2.4",
@@ -1,6 +1,6 @@
1
1
  import {constants, Provider} from "starknet";
2
2
  import {StarknetFees} from "./chain/modules/StarknetFees";
3
- import {StarknetChainInterface, StarknetRetryPolicy} from "./chain/StarknetChainInterface";
3
+ import {StarknetChainInterface, StarknetConfig, StarknetRetryPolicy} from "./chain/StarknetChainInterface";
4
4
  import {StarknetBtcRelay} from "./btcrelay/StarknetBtcRelay";
5
5
  import {StarknetSwapContract} from "./swaps/StarknetSwapContract";
6
6
  import {StarknetChainEventsBrowser} from "./events/StarknetChainEventsBrowser";
@@ -52,7 +52,9 @@ export type StarknetOptions = {
52
52
  }
53
53
  }
54
54
 
55
- fees?: StarknetFees
55
+ fees?: StarknetFees,
56
+
57
+ starknetConfig?: StarknetConfig
56
58
  }
57
59
 
58
60
  export function initializeStarknet(
@@ -69,7 +71,7 @@ export function initializeStarknet(
69
71
  const chainId = options.chainId ??
70
72
  (network===BitcoinNetwork.MAINNET ? constants.StarknetChainId.SN_MAIN : constants.StarknetChainId.SN_SEPOLIA);
71
73
 
72
- const chainInterface = new StarknetChainInterface(chainId, provider, options.retryPolicy, Fees);
74
+ const chainInterface = new StarknetChainInterface(chainId, provider, options.retryPolicy, Fees, options.starknetConfig);
73
75
 
74
76
  const btcRelay = new StarknetBtcRelay(
75
77
  chainInterface, bitcoinRpc, network, options.btcRelayContract
@@ -19,6 +19,14 @@ export type StarknetRetryPolicy = {
19
19
  exponential?: boolean
20
20
  }
21
21
 
22
+ export type StarknetConfig = {
23
+ getLogChunkSize?: number, //100
24
+ getLogForwardBlockRange?: number, //2000
25
+ maxGetLogKeys?: number, //64
26
+
27
+ maxParallelCalls?: number, //10
28
+ };
29
+
22
30
  export class StarknetChainInterface implements ChainInterface {
23
31
 
24
32
  readonly chainId = "STARKNET";
@@ -38,17 +46,25 @@ export class StarknetChainInterface implements ChainInterface {
38
46
 
39
47
  protected readonly logger = getLogger("StarknetChainInterface: ");
40
48
 
49
+ public readonly config: StarknetConfig;
50
+
41
51
  constructor(
42
52
  chainId: constants.StarknetChainId,
43
53
  provider: Provider,
44
54
  retryPolicy?: StarknetRetryPolicy,
45
- solanaFeeEstimator: StarknetFees = new StarknetFees(provider)
55
+ feeEstimator: StarknetFees = new StarknetFees(provider),
56
+ options?: StarknetConfig
46
57
  ) {
47
58
  this.starknetChainId = chainId;
48
59
  this.provider = provider;
49
60
  this.retryPolicy = retryPolicy;
61
+ this.config = options ?? {};
62
+ this.config.getLogForwardBlockRange ??= 2000;
63
+ this.config.getLogChunkSize ??= 100;
64
+ this.config.maxGetLogKeys ??= 64;
65
+ this.config.maxParallelCalls ??= 10;
50
66
 
51
- this.Fees = solanaFeeEstimator;
67
+ this.Fees = feeEstimator;
52
68
  this.Tokens = new StarknetTokens(this);
53
69
  this.Transactions = new StarknetTransactions(this);
54
70
 
@@ -51,7 +51,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
51
51
 
52
52
  private toFilter<T extends ExtractAbiEventNames<TAbi>>(
53
53
  events: T[],
54
- keys: string[],
54
+ keys: (string | string[])[],
55
55
  ): string[][] {
56
56
  const filterArray: string[][] = [];
57
57
  filterArray.push(events.map(name => {
@@ -59,7 +59,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
59
59
  const eventName = arr[arr.length-1];
60
60
  return toHex(hash.starknetKeccak(eventName))
61
61
  }));
62
- if(keys!=null) keys.forEach(key => filterArray.push(key==null ? [] : [key]));
62
+ if(keys!=null) keys.forEach(key => filterArray.push(key==null ? [] : Array.isArray(key) ? key : [key]));
63
63
  return filterArray;
64
64
  }
65
65
 
@@ -74,7 +74,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
74
74
  */
75
75
  public async getContractBlockEvents<T extends ExtractAbiEventNames<TAbi>>(
76
76
  events: T[],
77
- keys: string[],
77
+ keys: (string | string[])[],
78
78
  startBlockHeight?: number,
79
79
  endBlockHeight: number = startBlockHeight
80
80
  ): Promise<StarknetAbiEvent<TAbi, T>[]> {
@@ -93,7 +93,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
93
93
  */
94
94
  public async findInContractEvents<T, TEvent extends ExtractAbiEventNames<TAbi>>(
95
95
  events: TEvent[],
96
- keys: string[],
96
+ keys: (string | string[])[],
97
97
  processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>,
98
98
  abortSignal?: AbortSignal
99
99
  ) {
@@ -117,7 +117,7 @@ export class StarknetContractEvents<TAbi extends Abi> extends StarknetEvents {
117
117
  */
118
118
  public async findInContractEventsForward<T, TEvent extends ExtractAbiEventNames<TAbi>>(
119
119
  events: TEvent[],
120
- keys: string[],
120
+ keys: (string | string[])[],
121
121
  processor: (event: StarknetAbiEvent<TAbi, TEvent>) => Promise<T>,
122
122
  abortSignal?: AbortSignal
123
123
  ) {
@@ -6,7 +6,7 @@ import {
6
6
  SpvVaultTokenData,
7
7
  SpvWithdrawalState,
8
8
  SpvWithdrawalStateType,
9
- SpvWithdrawalTransactionData,
9
+ SpvWithdrawalTransactionData, SwapCommitState,
10
10
  TransactionConfirmationOptions
11
11
  } from "@atomiqlabs/base";
12
12
  import {Buffer} from "buffer";
@@ -24,6 +24,9 @@ import {bigNumberishToBuffer, bufferToByteArray, bufferToU32Array, getLogger, to
24
24
  import {StarknetBtcStoredHeader} from "../btcrelay/headers/StarknetBtcStoredHeader";
25
25
  import {StarknetAddresses} from "../chain/modules/StarknetAddresses";
26
26
  import {StarknetFees} from "../chain/modules/StarknetFees";
27
+ import {toBig} from "@noble/hashes/_u64";
28
+ import {StarknetAbiEvent} from "../contract/modules/StarknetContractEvents";
29
+ import {ExtractAbiEventNames} from "abi-wan-kanabi/dist/kanabi";
27
30
 
28
31
  const spvVaultContractAddreses = {
29
32
  [constants.StarknetChainId.SN_SEPOLIA]: "0x02d581ea838cd5ca46ba08660eddd064d50a0392f618e95310432147928d572e",
@@ -166,6 +169,49 @@ export class StarknetSpvVaultContract
166
169
  return new StarknetSpvVaultData(owner, vaultId, struct);
167
170
  }
168
171
 
172
+ async getMultipleVaultData(vaults: {owner: string, vaultId: bigint}[]): Promise<{[owner: string]: {[vaultId: string]: StarknetSpvVaultData}}> {
173
+ const result: {[owner: string]: {[vaultId: string]: StarknetSpvVaultData}} = {};
174
+ let promises: Promise<void>[] = [];
175
+ //TODO: We can upgrade this to use multicall
176
+ for(let {owner, vaultId} of vaults) {
177
+ promises.push(this.getVaultData(owner, vaultId).then(val => {
178
+ result[owner] ??= {};
179
+ result[owner][vaultId.toString(10)] = val;
180
+ }));
181
+ if(promises.length>=this.Chain.config.maxParallelCalls) {
182
+ await Promise.all(promises);
183
+ promises = [];
184
+ }
185
+ }
186
+ await Promise.all(promises);
187
+ return result;
188
+ }
189
+
190
+ async getVaultLatestUtxo(owner: string, vaultId: bigint): Promise<string | null> {
191
+ const vault = await this.getVaultData(owner, vaultId);
192
+ if(vault==null) return null;
193
+ if(!vault.isOpened()) return null;
194
+ return vault.getUtxo();
195
+ }
196
+
197
+ async getVaultLatestUtxos(vaults: {owner: string, vaultId: bigint}[]): Promise<{[owner: string]: {[vaultId: string]: string | null}}> {
198
+ const result: {[owner: string]: {[vaultId: string]: string | null}} = {};
199
+ let promises: Promise<void>[] = [];
200
+ //TODO: We can upgrade this to use multicall
201
+ for(let {owner, vaultId} of vaults) {
202
+ promises.push(this.getVaultLatestUtxo(owner, vaultId).then(val => {
203
+ result[owner] ??= {};
204
+ result[owner][vaultId.toString(10)] = val;
205
+ }));
206
+ if(promises.length>=this.Chain.config.maxParallelCalls) {
207
+ await Promise.all(promises);
208
+ promises = [];
209
+ }
210
+ }
211
+ await Promise.all(promises);
212
+ return result;
213
+ }
214
+
169
215
  async getAllVaults(owner?: string): Promise<StarknetSpvVaultData[]> {
170
216
  const openedVaults = new Set<string>();
171
217
  await this.Events.findInContractEventsForward(
@@ -198,6 +244,98 @@ export class StarknetSpvVaultContract
198
244
  return fronterAddress;
199
245
  }
200
246
 
247
+ async getFronterAddresses(withdrawals: {owner: string, vaultId: bigint, withdrawal: StarknetSpvWithdrawalData}[]): Promise<{[btcTxId: string]: string | null}> {
248
+ const result: {
249
+ [btcTxId: string]: string | null
250
+ } = {};
251
+ let promises: Promise<void>[] = [];
252
+ //TODO: We can upgrade this to use multicall
253
+ for(let {owner, vaultId, withdrawal} of withdrawals) {
254
+ promises.push(this.getFronterAddress(owner, vaultId, withdrawal).then(val => {
255
+ result[withdrawal.getTxId()] = val;
256
+ }));
257
+ if(promises.length>=this.Chain.config.maxParallelCalls) {
258
+ await Promise.all(promises);
259
+ promises = [];
260
+ }
261
+ }
262
+ await Promise.all(promises);
263
+ return result;
264
+ }
265
+
266
+ private parseWithdrawalEvent(event: StarknetAbiEvent<typeof SpvVaultContractAbi, "spv_swap_vault::events::Claimed" | "spv_swap_vault::events::Fronted" | "spv_swap_vault::events::Closed">): SpvWithdrawalState | null {
267
+ switch(event.name) {
268
+ case "spv_swap_vault::events::Fronted":
269
+ return {
270
+ type: SpvWithdrawalStateType.FRONTED,
271
+ txId: event.txHash,
272
+ owner: toHex(event.params.owner),
273
+ vaultId: toBigInt(event.params.vault_id),
274
+ recipient: toHex(event.params.recipient),
275
+ fronter: toHex(event.params.fronting_address)
276
+ };
277
+ case "spv_swap_vault::events::Claimed":
278
+ return {
279
+ type: SpvWithdrawalStateType.CLAIMED,
280
+ txId: event.txHash,
281
+ owner: toHex(event.params.owner),
282
+ vaultId: toBigInt(event.params.vault_id),
283
+ recipient: toHex(event.params.recipient),
284
+ claimer: toHex(event.params.caller),
285
+ fronter: toHex(event.params.fronting_address)
286
+ };
287
+ case "spv_swap_vault::events::Closed":
288
+ return {
289
+ type: SpvWithdrawalStateType.CLOSED,
290
+ txId: event.txHash,
291
+ owner: toHex(event.params.owner),
292
+ vaultId: toBigInt(event.params.vault_id),
293
+ error: bigNumberishToBuffer(event.params.error).toString()
294
+ };
295
+ default:
296
+ return null;
297
+ }
298
+ }
299
+
300
+ async getWithdrawalStates(btcTxIds: string[]): Promise<{[btcTxId: string]: SpvWithdrawalState}> {
301
+ const result: {[btcTxId: string]: SpvWithdrawalState} = {};
302
+ btcTxIds.forEach(txId => {
303
+ result[txId] = {
304
+ type: SpvWithdrawalStateType.NOT_FOUND
305
+ };
306
+ });
307
+
308
+ for(let i=0;i<btcTxIds.length;i+=this.Chain.config.maxGetLogKeys) {
309
+ const checkBtcTxIds = btcTxIds.slice(i, i+this.Chain.config.maxGetLogKeys);
310
+ const lows: string[] = [];
311
+ const highs: string[] = [];
312
+ checkBtcTxIds.forEach(btcTxId => {
313
+ const txHash = Buffer.from(btcTxId, "hex").reverse();
314
+ const txHashU256 = cairo.uint256("0x"+txHash.toString("hex"));
315
+ lows.push(toHex(txHashU256.low));
316
+ highs.push(toHex(txHashU256.high));
317
+ });
318
+ await this.Events.findInContractEventsForward(
319
+ ["spv_swap_vault::events::Fronted", "spv_swap_vault::events::Claimed", "spv_swap_vault::events::Closed"],
320
+ [
321
+ lows,
322
+ highs
323
+ ],
324
+ async (event) => {
325
+ const txId = bigNumberishToBuffer(event.params.btc_tx_hash, 32).reverse().toString("hex");
326
+ if(result[txId]==null) {
327
+ this.logger.warn(`getWithdrawalStates(): findInContractEvents-callback: loaded event for ${txId}, but transaction not found in input params!`)
328
+ return;
329
+ }
330
+ const eventResult = this.parseWithdrawalEvent(event);
331
+ if(eventResult!=null) result[txId] = eventResult;
332
+ }
333
+ );
334
+ }
335
+
336
+ return result;
337
+ }
338
+
201
339
  async getWithdrawalState(btcTxId: string): Promise<SpvWithdrawalState> {
202
340
  const txHash = Buffer.from(btcTxId, "hex").reverse();
203
341
  const txHashU256 = cairo.uint256("0x"+txHash.toString("hex"));
@@ -211,38 +349,8 @@ export class StarknetSpvVaultContract
211
349
  toHex(txHashU256.high)
212
350
  ],
213
351
  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.params.owner),
220
- vaultId: toBigInt(event.params.vault_id),
221
- recipient: toHex(event.params.recipient),
222
- fronter: toHex(event.params.fronting_address)
223
- };
224
- break;
225
- case "spv_swap_vault::events::Claimed":
226
- result = {
227
- type: SpvWithdrawalStateType.CLAIMED,
228
- txId: event.txHash,
229
- owner: toHex(event.params.owner),
230
- vaultId: toBigInt(event.params.vault_id),
231
- recipient: toHex(event.params.recipient),
232
- claimer: toHex(event.params.caller),
233
- fronter: toHex(event.params.fronting_address)
234
- };
235
- break;
236
- case "spv_swap_vault::events::Closed":
237
- result = {
238
- type: SpvWithdrawalStateType.CLOSED,
239
- txId: event.txHash,
240
- owner: toHex(event.params.owner),
241
- vaultId: toBigInt(event.params.vault_id),
242
- error: bigNumberishToBuffer(event.params.error).toString()
243
- }
244
- break;
245
- }
352
+ const eventResult = this.parseWithdrawalEvent(event);
353
+ if(eventResult!=null) result = eventResult;
246
354
  }
247
355
  );
248
356
  return result;
@@ -369,6 +369,27 @@ export class StarknetSwapContract
369
369
  }
370
370
  }
371
371
 
372
+ async getCommitStatuses(request: { signer: string; swapData: StarknetSwapData }[]): Promise<{
373
+ [p: string]: SwapCommitState
374
+ }> {
375
+ const result: {
376
+ [p: string]: SwapCommitState
377
+ } = {};
378
+ let promises: Promise<void>[] = [];
379
+ //TODO: We can upgrade this to use multicall
380
+ for(let {signer, swapData} of request) {
381
+ promises.push(this.getCommitStatus(signer, swapData).then(val => {
382
+ result[swapData.getEscrowHash()] = val;
383
+ }));
384
+ if(promises.length>=this.Chain.config.maxParallelCalls) {
385
+ await Promise.all(promises);
386
+ promises = [];
387
+ }
388
+ }
389
+ await Promise.all(promises);
390
+ return result;
391
+ }
392
+
372
393
  /**
373
394
  * Returns the data committed for a specific payment hash, or null if no data is currently commited for
374
395
  * the specific swap