@gearbox-protocol/sdk 15.1.0-next.1 → 15.1.0-next.2

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.
Files changed (65) hide show
  1. package/dist/cjs/abi/iExpirable.js +15 -0
  2. package/dist/cjs/dev/AccountOpener.js +0 -1
  3. package/dist/cjs/sdk/MultichainSDK.js +3 -4
  4. package/dist/cjs/sdk/OnchainSDK.js +42 -13
  5. package/dist/cjs/sdk/accounts/withdrawal-compressor/AbstractWithdrawalCompressorContract.js +29 -18
  6. package/dist/cjs/sdk/base/TokensMeta.js +78 -27
  7. package/dist/cjs/sdk/index.js +2 -0
  8. package/dist/cjs/sdk/market/MarketRegister.js +16 -7
  9. package/dist/cjs/sdk/market/ZapperRegister.js +56 -25
  10. package/dist/cjs/sdk/market/rwa/RWARegistry.js +22 -0
  11. package/dist/cjs/sdk/options.js +1 -5
  12. package/dist/cjs/sdk/utils/viem/executeMulticallBatches.js +32 -0
  13. package/dist/cjs/sdk/utils/viem/index.js +2 -0
  14. package/dist/esm/abi/iExpirable.js +14 -0
  15. package/dist/esm/dev/AccountOpener.js +1 -2
  16. package/dist/esm/dev/withdrawalUtils.js +1 -1
  17. package/dist/esm/plugins/adapters/contracts/ERC4626AdapterContract.js +1 -1
  18. package/dist/esm/preview/simulate/simulatePoolOperation.js +1 -1
  19. package/dist/esm/preview/trace/extractTransfers.js +1 -1
  20. package/dist/esm/sdk/MultichainSDK.js +3 -4
  21. package/dist/esm/sdk/OnchainSDK.js +42 -13
  22. package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +2 -2
  23. package/dist/esm/sdk/accounts/liquidations/LiquidationsService.js +1 -1
  24. package/dist/esm/sdk/accounts/withdrawal-compressor/AbstractWithdrawalCompressorContract.js +29 -18
  25. package/dist/esm/sdk/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
  26. package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
  27. package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
  28. package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  29. package/dist/esm/sdk/base/TokensMeta.js +80 -29
  30. package/dist/esm/sdk/chain/detectNetwork.js +1 -1
  31. package/dist/esm/sdk/core/createAddressProvider.js +1 -1
  32. package/dist/esm/sdk/index.js +2 -1
  33. package/dist/esm/sdk/market/MarketRegister.js +16 -7
  34. package/dist/esm/sdk/market/ZapperRegister.js +56 -25
  35. package/dist/esm/sdk/market/credit/CreditFacadeV310BaseContract.js +1 -1
  36. package/dist/esm/sdk/market/pool/PoolV310Contract.js +1 -1
  37. package/dist/esm/sdk/market/rwa/RWARegistry.js +22 -0
  38. package/dist/esm/sdk/market/zapper/IETHZapperContract.js +1 -1
  39. package/dist/esm/sdk/market/zapper/ZapperContract.js +1 -1
  40. package/dist/esm/sdk/options.js +1 -5
  41. package/dist/esm/sdk/pools/PoolService.js +1 -1
  42. package/dist/esm/sdk/utils/viem/executeMulticallBatches.js +31 -0
  43. package/dist/esm/sdk/utils/viem/index.js +2 -1
  44. package/dist/esm/sdk/utils/viem/simulateWithPriceUpdates.js +1 -1
  45. package/dist/types/abi/iExpirable.d.ts +14 -0
  46. package/dist/types/sdk/MultichainSDK.d.ts +0 -5
  47. package/dist/types/sdk/OnchainSDK.d.ts +1 -5
  48. package/dist/types/sdk/accounts/withdrawal-compressor/AbstractWithdrawalCompressorContract.d.ts +6 -0
  49. package/dist/types/sdk/accounts/withdrawal-compressor/types.d.ts +18 -0
  50. package/dist/types/sdk/base/TokensMeta.d.ts +44 -1
  51. package/dist/types/sdk/base/index.d.ts +2 -2
  52. package/dist/types/sdk/base/token-types.d.ts +12 -1
  53. package/dist/types/sdk/index.d.ts +12 -11
  54. package/dist/types/sdk/market/MarketRegister.d.ts +7 -3
  55. package/dist/types/sdk/market/ZapperRegister.d.ts +20 -2
  56. package/dist/types/sdk/market/index.d.ts +2 -2
  57. package/dist/types/sdk/market/oracle/PriceOracleBaseContract.d.ts +2 -2
  58. package/dist/types/sdk/market/oracle/types.d.ts +2 -2
  59. package/dist/types/sdk/market/rwa/RWARegistry.d.ts +13 -0
  60. package/dist/types/sdk/market/types.d.ts +5 -1
  61. package/dist/types/sdk/options.d.ts +0 -1
  62. package/dist/types/sdk/types/state.d.ts +6 -0
  63. package/dist/types/sdk/utils/viem/executeMulticallBatches.d.ts +39 -0
  64. package/dist/types/sdk/utils/viem/index.d.ts +3 -2
  65. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/abi/iExpirable.ts
3
+ const iExpirableAbi = [{
4
+ type: "function",
5
+ inputs: [],
6
+ name: "isExpired",
7
+ outputs: [{
8
+ name: "",
9
+ internalType: "bool",
10
+ type: "bool"
11
+ }],
12
+ stateMutability: "view"
13
+ }];
14
+ //#endregion
15
+ exports.iExpirableAbi = iExpirableAbi;
@@ -87,7 +87,6 @@ var AccountOpener = class extends require_sdk_base_SDKConstruct.SDKConstruct {
87
87
  depositIntoPools,
88
88
  claimFromFaucet
89
89
  }, "opening credit accounts");
90
- await Promise.all([this.sdk.tokensMeta.loadTokenData(), this.sdk.marketRegister.loadZappers()]);
91
90
  let deposits = [];
92
91
  if (depositIntoPools) try {
93
92
  deposits = await this.#depositIntoPools(targets);
@@ -69,7 +69,6 @@ var MultichainSDK = class {
69
69
  const perChainOpts = options?.perChain?.[network] ?? {};
70
70
  return sdk.attach({
71
71
  ...perChainOpts,
72
- loadZappers: perChainOpts.loadZappers ?? options?.loadZappers,
73
72
  redstone: {
74
73
  ...options?.redstone,
75
74
  cache: this.#redstoneCache,
@@ -89,7 +88,7 @@ var MultichainSDK = class {
89
88
  * state and `allowMissingChains` is not set.
90
89
  */
91
90
  hydrate(state, options) {
92
- if (state.version !== 1) throw new require_sdk_core_errors.SdkStateVersionMismatchError(1, state.version);
91
+ if (state.version !== 2) throw new require_sdk_core_errors.SdkStateVersionMismatchError(2, state.version);
93
92
  if (options?.redstone) this.#redstoneCache = new require_sdk_market_pricefeeds_updates_PriceUpdatesCache.PriceUpdatesCache({
94
93
  ttl: options.redstone.cacheTTL ?? 225e3,
95
94
  historical: !!options.redstone.historicTimestamp
@@ -169,7 +168,7 @@ var MultichainSDK = class {
169
168
  **/
170
169
  get state() {
171
170
  return {
172
- version: 1,
171
+ version: 2,
173
172
  chains: [...this.#chains.values()].map((sdk) => sdk.state)
174
173
  };
175
174
  }
@@ -179,7 +178,7 @@ var MultichainSDK = class {
179
178
  */
180
179
  stateHuman(raw) {
181
180
  return {
182
- version: 1,
181
+ version: 2,
183
182
  chains: [...this.#chains.values()].map((sdk) => sdk.stateHuman(raw))
184
183
  };
185
184
  }
@@ -8,6 +8,7 @@ require("./constants/index.js");
8
8
  const require_sdk_utils_formatter = require("./utils/formatter.js");
9
9
  const require_sdk_utils_toAddress = require("./utils/toAddress.js");
10
10
  require("./utils/index.js");
11
+ const require_sdk_utils_viem_executeMulticallBatches = require("./utils/viem/executeMulticallBatches.js");
11
12
  const require_sdk_base_ChainContractsRegister = require("./base/ChainContractsRegister.js");
12
13
  require("./base/index.js");
13
14
  const require_sdk_utils_viem_executeDelegatedMulticalls = require("./utils/viem/executeDelegatedMulticalls.js");
@@ -41,7 +42,7 @@ let viem = require("viem");
41
42
  * Serialised state format version, checked during hydration to detect
42
43
  * incompatible snapshots.
43
44
  **/
44
- const STATE_VERSION = 1;
45
+ const STATE_VERSION = 2;
45
46
  /**
46
47
  * @internal
47
48
  * Creates a viem `PublicClient` from the given connection options.
@@ -151,7 +152,7 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
151
152
  */
152
153
  async attach(options) {
153
154
  if (this.#attached) throw new require_sdk_core_errors.SdkAlreadyAttachedError();
154
- const { addressProvider = require_sdk_constants_address_provider.ADDRESS_PROVIDER_V310, blockNumber, ignoreUpdateablePrices, ignoreMarkets, marketConfigurators: mcs, redstone, loadZappers } = options ?? {};
155
+ const { addressProvider = require_sdk_constants_address_provider.ADDRESS_PROVIDER_V310, blockNumber, ignoreUpdateablePrices, ignoreMarkets, marketConfigurators: mcs, redstone } = options ?? {};
155
156
  const marketConfigurators = mcs ?? require_sdk_utils_mappers.TypedObjectUtils.keys(this.client.chain.defaultMarketConfigurators);
156
157
  const rwaFactories = options?.rwaFactories ?? this.client.chain.rwaFactories;
157
158
  this.logger?.info({
@@ -175,19 +176,15 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
175
176
  this.#marketRegister = new require_sdk_market_MarketRegister.MarketRegister(this, ignoreMarkets);
176
177
  if (!marketConfigurators.length) this.logger?.warn("no market configurators provided, skipping market loading");
177
178
  else {
179
+ const priceUpdates = await this.#getPriceUpdateTxs(marketConfigurators, ignoreUpdateablePrices);
180
+ this.logger?.debug({ configurators: marketConfigurators }, `calling getMarkets with ${priceUpdates.length} price updates in block ${this.currentBlock}`);
178
181
  const delegated = [...this.#marketRegister.getLoadMulticalls(marketConfigurators), ...this.#rwa.getLoadMulticalls(marketConfigurators, rwaFactories)];
179
- let txs = [];
180
- if (!ignoreUpdateablePrices) {
181
- const updatables = await this.#priceFeeds.getPartialUpdatablePriceFeeds(marketConfigurators);
182
- txs = (await this.#priceFeeds.generatePriceFeedsUpdateTxs(updatables)).txs;
183
- }
184
- this.logger?.debug({ configurators: marketConfigurators }, `calling getMarkets with ${txs.length} price updates in block ${this.currentBlock}`);
185
182
  await require_sdk_utils_viem_executeDelegatedMulticalls.executeDelegatedMulticalls(this.client, delegated, {
186
- priceUpdates: txs,
183
+ priceUpdates,
187
184
  blockNumber: this.currentBlock,
188
185
  gas: this.gasLimit
189
186
  });
190
- if (loadZappers) await this.#marketRegister.loadZappers();
187
+ await this.#warmup();
191
188
  }
192
189
  const pluginsList = require_sdk_utils_mappers.TypedObjectUtils.entries(this.plugins);
193
190
  (await Promise.allSettled(pluginsList.map(([name, plugin]) => {
@@ -204,6 +201,36 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
204
201
  this.#attached = true;
205
202
  }
206
203
  /**
204
+ * @internal
205
+ * Pass 0 of attach: resolves the price updates that the market reads of
206
+ * pass 1 are simulated with.
207
+ *
208
+ * @param configurators - Market configurators whose feeds are considered.
209
+ * @param ignoreUpdateablePrices - When `true`, no updates are fetched.
210
+ **/
211
+ async #getPriceUpdateTxs(configurators, ignoreUpdateablePrices) {
212
+ if (ignoreUpdateablePrices) return [];
213
+ const updatables = await this.priceFeeds.getPartialUpdatablePriceFeeds(configurators);
214
+ const { txs } = await this.priceFeeds.generatePriceFeedsUpdateTxs(updatables);
215
+ return txs;
216
+ }
217
+ /**
218
+ * @internal
219
+ * Pass 2 of attach: loads the data that is read synchronously afterwards
220
+ * (zappers, withdrawable assets, extended token data) in a single multicall.
221
+ *
222
+ * Also re-run after a sync that reloaded the markets these caches derive from.
223
+ *
224
+ * @param force - Reload caches that are already populated.
225
+ **/
226
+ async #warmup(force = false) {
227
+ await require_sdk_utils_viem_executeMulticallBatches.executeMulticallBatches(this.client, [
228
+ this.marketRegister.getLoadZappersMulticall(force),
229
+ ...this.#withdrawalCompressor ? [this.#withdrawalCompressor.getLoadWithdrawableAssetsMulticall(force)] : [],
230
+ this.tokensMeta.getLoadTokenDataMulticall()
231
+ ], { blockNumber: this.currentBlock });
232
+ }
233
+ /**
207
234
  * Restores SDK state from a previously serialised {@link GearboxState}
208
235
  * snapshot, without making any on-chain calls.
209
236
  *
@@ -215,7 +242,7 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
215
242
  */
216
243
  hydrate(state, options) {
217
244
  if (this.#attached) throw new require_sdk_core_errors.SdkAlreadyAttachedError();
218
- if (state.version !== 1) throw new require_sdk_core_errors.SdkStateVersionMismatchError(1, state.version);
245
+ if (state.version !== 2) throw new require_sdk_core_errors.SdkStateVersionMismatchError(2, state.version);
219
246
  if (state.network !== this.networkType) throw new require_sdk_core_errors.SdkChainMismatchError(this.networkType, state.network);
220
247
  const { ignoreMarkets, redstone } = options ?? {};
221
248
  this.logger?.info({ networkType: this.networkType }, "hydrating sdk state");
@@ -229,6 +256,7 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
229
256
  this.#rwa = new require_sdk_market_rwa_RWARegistry.RWARegistry(this);
230
257
  this.#rwa.setState(state.rwa);
231
258
  if (state.withdrawals) this.#withdrawalCompressor?.hydrate(state.withdrawals);
259
+ if (state.tokens) this.tokensMeta.hydrate(state.tokens);
232
260
  for (const [name, plugin] of require_sdk_utils_mappers.TypedObjectUtils.entries(this.plugins)) {
233
261
  const pluginState = state.plugins[name];
234
262
  if (plugin.hydrate && pluginState) {
@@ -274,13 +302,14 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
274
302
  /** Serialisable snapshot of the current SDK state. */
275
303
  get state() {
276
304
  return {
277
- version: 1,
305
+ version: 2,
278
306
  network: this.networkType,
279
307
  chainId: this.chainId,
280
308
  currentBlock: this.currentBlock,
281
309
  timestamp: this.timestamp,
282
310
  addressProvider: this.addressProvider.state,
283
311
  ...this.marketRegister.state,
312
+ tokens: this.tokensMeta.state,
284
313
  rwa: this.#rwa.state,
285
314
  withdrawals: this.#withdrawalCompressor?.state,
286
315
  plugins: Object.fromEntries(require_sdk_utils_mappers.TypedObjectUtils.entries(this.plugins).map(([name, plugin]) => [name, plugin.loaded ? {
@@ -339,7 +368,7 @@ var OnchainSDK = class extends require_sdk_base_ChainContractsRegister.ChainCont
339
368
  }
340
369
  this.#currentBlock = blockNumber;
341
370
  this.#timestamp = timestamp;
342
- await this.marketRegister.syncState(ignoreUpdateablePrices);
371
+ if (await this.marketRegister.syncState(ignoreUpdateablePrices, this.#rwa.getSyncMulticalls())) await this.#warmup(true);
343
372
  const pluginsList = require_sdk_utils_mappers.TypedObjectUtils.entries(this.plugins);
344
373
  (await Promise.allSettled(pluginsList.map(([name, plugin]) => {
345
374
  if (plugin.syncState) {
@@ -4,8 +4,10 @@ require("../../constants/addresses.js");
4
4
  require("../../constants/index.js");
5
5
  const require_sdk_utils_hex = require("../../utils/hex.js");
6
6
  require("../../utils/index.js");
7
+ const require_sdk_utils_viem_executeMulticallBatches = require("../../utils/viem/executeMulticallBatches.js");
7
8
  const require_sdk_base_BaseContract = require("../../base/BaseContract.js");
8
9
  require("../../base/index.js");
10
+ require("../../utils/viem/index.js");
9
11
  const require_sdk_accounts_withdrawal_compressor_intent_codec = require("./intent-codec.js");
10
12
  let viem = require("viem");
11
13
  //#region src/sdk/accounts/withdrawal-compressor/AbstractWithdrawalCompressorContract.ts
@@ -58,37 +60,46 @@ var AbstractWithdrawalCompressorContract = class extends require_sdk_base_BaseCo
58
60
  **/
59
61
  getWithdrawableAssets(...creditManagers) {
60
62
  const cache = this.#withdrawableAssets;
61
- if (!cache) throw new Error("withdrawable assets are not loaded, call loadWithdrawableAssets first");
63
+ if (!cache) throw new Error("withdrawable assets are not loaded, check if the sdk was properly attached or hydrated");
62
64
  if (creditManagers.length === 0) return cache.values().flat();
63
65
  return creditManagers.flatMap((cm) => cache.get(cm) ?? []);
64
66
  }
65
67
  /**
66
- * {@inheritDoc IWithdrawalCompressorContract.loadWithdrawableAssets}
68
+ * {@inheritDoc IWithdrawalCompressorContract.getLoadWithdrawableAssetsMulticall}
67
69
  **/
68
- async loadWithdrawableAssets(force, blockNumber) {
69
- if (this.#withdrawableAssets && !force) return this.getWithdrawableAssets();
70
+ getLoadWithdrawableAssetsMulticall(force) {
71
+ if (this.#withdrawableAssets && !force) return {
72
+ contracts: [],
73
+ onResults: () => {}
74
+ };
70
75
  const cms = this.#sdk.marketRegister.creditManagers.map((cm) => cm.creditManager.address);
71
- const resp = await this.client.multicall({
76
+ return {
72
77
  contracts: cms.map((cm) => ({
73
78
  address: this.address,
74
79
  abi: this.abi,
75
80
  functionName: "getWithdrawableAssets",
76
81
  args: [cm]
77
82
  })),
78
- allowFailure: true,
79
- batchSize: 0,
80
- blockNumber
81
- });
82
- const cache = new require_sdk_utils_AddressMap.AddressMap(void 0, MAP_LABEL);
83
- for (let i = 0; i < resp.length; i++) {
84
- const r = resp[i];
85
- if (r.status !== "success") {
86
- this.logger?.warn(`failed to get withdrawable assets of credit manager ${cms[i]}: ${r.error}`);
87
- continue;
83
+ onResults: (resps) => {
84
+ const cache = new require_sdk_utils_AddressMap.AddressMap(void 0, MAP_LABEL);
85
+ for (let i = 0; i < resps.length; i++) {
86
+ const r = resps[i];
87
+ if (r.status !== "success") {
88
+ this.logger?.warn(`failed to get withdrawable assets of credit manager ${cms[i]}: ${r.error}`);
89
+ continue;
90
+ }
91
+ const assets = r.result;
92
+ if (assets.length > 0) cache.upsert(cms[i], assets.map((a) => toWithdrawableAsset(a, cms[i])));
93
+ }
94
+ this.#withdrawableAssets = cache;
88
95
  }
89
- if (r.result.length > 0) cache.upsert(cms[i], r.result.map((a) => toWithdrawableAsset(a, cms[i])));
90
- }
91
- this.#withdrawableAssets = cache;
96
+ };
97
+ }
98
+ /**
99
+ * {@inheritDoc IWithdrawalCompressorContract.loadWithdrawableAssets}
100
+ **/
101
+ async loadWithdrawableAssets(force, blockNumber) {
102
+ await require_sdk_utils_viem_executeMulticallBatches.executeMulticallBatches(this.client, [this.getLoadWithdrawableAssetsMulticall(force)], { blockNumber });
92
103
  return this.getWithdrawableAssets();
93
104
  }
94
105
  /**
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_abi_iExpirable = require("../../abi/iExpirable.js");
2
3
  const require_abi_iStateSerializer = require("../../abi/iStateSerializer.js");
3
4
  const require_abi_iVersion = require("../../abi/iVersion.js");
4
5
  const require_sdk_utils_AddressMap = require("../utils/AddressMap.js");
@@ -7,6 +8,7 @@ const require_sdk_utils_bytes32ToString = require("../utils/bytes32ToString.js")
7
8
  const require_sdk_chain_chains = require("../chain/chains.js");
8
9
  const require_sdk_utils_formatter = require("../utils/formatter.js");
9
10
  require("../utils/index.js");
11
+ const require_sdk_utils_viem_executeMulticallBatches = require("../utils/viem/executeMulticallBatches.js");
10
12
  //#region src/sdk/base/TokensMeta.ts
11
13
  /**
12
14
  * Registry of token metadata (symbol, decimals, phantom type) keyed by address.
@@ -63,7 +65,7 @@ var TokensMeta = class extends require_sdk_utils_AddressMap.AddressMap {
63
65
  * @returns
64
66
  */
65
67
  isPhantomToken(t) {
66
- if (!this.#tokenDataLoaded.has(t.addr)) throw new Error(`extended token data not loaded for ${t.symbol} (${t.addr})`);
68
+ if (!this.#tokenDataLoaded.has(t.addr)) throw new Error(`extended token data not loaded for ${t.symbol} (${t.addr}), check if the sdk was properly attached or hydrated`);
67
69
  return !!t.contractType?.startsWith("PHANTOM_TOKEN::");
68
70
  }
69
71
  /**
@@ -168,44 +170,93 @@ var TokensMeta = class extends require_sdk_utils_AddressMap.AddressMap {
168
170
  return meta;
169
171
  }
170
172
  /**
173
+ * @internal
174
+ *
175
+ * Returns the multicall batch that loads extended token data (`contractType`
176
+ * and `serialize()`) for the tokens that do not have it yet. Used by the SDK
177
+ * to warm this cache together with other loaders in a single multicall.
178
+ *
179
+ * @param tokens - tokens to load data for, defaults to all tokens
180
+ **/
181
+ getLoadTokenDataMulticall(...tokens) {
182
+ const tokenz = new require_sdk_utils_AddressSet.AddressSet(tokens.length > 0 ? tokens : this.keys());
183
+ const tokensToLoad = Array.from(tokenz.difference(this.#tokenDataLoaded));
184
+ return {
185
+ contracts: tokensToLoad.flatMap((t) => [
186
+ {
187
+ address: t,
188
+ abi: require_abi_iVersion.iVersionAbi,
189
+ functionName: "contractType"
190
+ },
191
+ {
192
+ address: t,
193
+ abi: require_abi_iStateSerializer.iStateSerializerAbi,
194
+ functionName: "serialize"
195
+ },
196
+ {
197
+ address: t,
198
+ abi: require_abi_iExpirable.iExpirableAbi,
199
+ functionName: "isExpired"
200
+ }
201
+ ]),
202
+ onResults: (resps) => {
203
+ this.#logger?.debug(`loaded data of ${tokensToLoad.length} tokens`);
204
+ for (let i = 0; i < tokensToLoad.length; i++) {
205
+ this.#overrideTokenMeta(tokensToLoad[i], resps[3 * i], resps[3 * i + 1], resps[3 * i + 2]);
206
+ this.#tokenDataLoaded.add(tokensToLoad[i]);
207
+ }
208
+ }
209
+ };
210
+ }
211
+ /**
171
212
  * Loads token information about phantom tokens
172
213
  * In future other custom tokens types that do not have compressors might be handled here
173
214
  *
215
+ * Tokens loaded during SDK attach or restored by {@link hydrate} are skipped,
216
+ * so calling this after either is a no-op.
217
+ *
174
218
  * @param tokens - tokens to load data for, defaults to all tokens
175
219
  */
176
220
  async loadTokenData(...tokens) {
177
- const tokenz = new require_sdk_utils_AddressSet.AddressSet(tokens.length > 0 ? tokens : this.keys());
178
- const tokensToLoad = Array.from(tokenz.difference(this.#tokenDataLoaded));
179
- if (tokensToLoad.length === 0) return;
180
- const resp = await this.#client.multicall({
181
- contracts: tokensToLoad.flatMap((t) => [{
182
- address: t,
183
- abi: require_abi_iVersion.iVersionAbi,
184
- functionName: "contractType"
185
- }, {
186
- address: t,
187
- abi: require_abi_iStateSerializer.iStateSerializerAbi,
188
- functionName: "serialize"
189
- }]),
190
- allowFailure: true,
191
- batchSize: 0
192
- });
193
- this.#logger?.debug(`loaded ${resp.length} contract types`);
194
- for (let i = 0; i < tokensToLoad.length; i++) {
195
- this.#overrideTokenMeta(tokensToLoad[i], resp[2 * i], resp[2 * i + 1]);
196
- this.#tokenDataLoaded.add(tokensToLoad[i]);
197
- }
221
+ await require_sdk_utils_viem_executeMulticallBatches.executeMulticallBatches(this.#client, [this.getLoadTokenDataMulticall(...tokens)]);
222
+ }
223
+ /**
224
+ * Serializable snapshot of all token metadata, suitable for hydration.
225
+ **/
226
+ get state() {
227
+ return {
228
+ tokens: this.values(),
229
+ extendedLoaded: this.#tokenDataLoaded.asArray()
230
+ };
198
231
  }
199
- #overrideTokenMeta(token, contractTypeResp, _serializeResp) {
232
+ /**
233
+ * Restores token metadata from a previously serialized snapshot,
234
+ * bypassing on-chain reads.
235
+ *
236
+ * Entries are merged into the ones already registered by markets, zappers and
237
+ * the RWA registry (see {@link upsert}), so the snapshot must be applied last
238
+ * for its augmented fields to win.
239
+ *
240
+ * @param state - Token metadata snapshot.
241
+ **/
242
+ hydrate(state) {
243
+ for (const token of state.tokens) this.upsert(token.addr, token);
244
+ for (const token of state.extendedLoaded) this.#tokenDataLoaded.add(token);
245
+ }
246
+ #overrideTokenMeta(token, contractTypeResp, serializeResp, isExpiredResp) {
200
247
  const meta = this.mustGet(token);
248
+ const update = { ...meta };
201
249
  if (contractTypeResp.status === "success") {
202
250
  const contractType = require_sdk_utils_bytes32ToString.bytes32ToString(contractTypeResp.result);
203
- this.upsert(token, {
204
- ...meta,
205
- contractType
206
- });
251
+ update.contractType = contractType;
252
+ update.serializedParams = serializeResp.status === "success" ? serializeResp.result : void 0;
207
253
  this.#logger?.debug(`token ${meta.symbol} is ${contractType}`);
208
254
  }
255
+ if (isExpiredResp.status === "success") {
256
+ update.isExpired = isExpiredResp.result;
257
+ this.#logger?.debug(`token ${meta.symbol} is expirable, expired: ${isExpiredResp.result}`);
258
+ }
259
+ if (contractTypeResp.status === "success" || isExpiredResp.status === "success") this.upsert(token, update);
209
260
  return this.mustGet(token);
210
261
  }
211
262
  };
@@ -31,6 +31,7 @@ const require_sdk_utils_zod = require("./utils/zod.js");
31
31
  require("./utils/index.js");
32
32
  const require_sdk_utils_viem_cast = require("./utils/viem/cast.js");
33
33
  const require_sdk_utils_viem_simulateCall = require("./utils/viem/simulateCall.js");
34
+ const require_sdk_utils_viem_executeMulticallBatches = require("./utils/viem/executeMulticallBatches.js");
34
35
  const require_sdk_base_TokensMeta = require("./base/TokensMeta.js");
35
36
  const require_sdk_base_ChainContractsRegister = require("./base/ChainContractsRegister.js");
36
37
  const require_sdk_base_Construct = require("./base/Construct.js");
@@ -348,6 +349,7 @@ exports.estimateRawTxGas = require_sdk_utils_viem_sendRawTx.estimateRawTxGas;
348
349
  exports.etherscanApiUrl = require_sdk_utils_etherscan.etherscanApiUrl;
349
350
  exports.etherscanUrl = require_sdk_utils_etherscan.etherscanUrl;
350
351
  exports.executeDelegatedMulticalls = require_sdk_utils_viem_executeDelegatedMulticalls.executeDelegatedMulticalls;
352
+ exports.executeMulticallBatches = require_sdk_utils_viem_executeMulticallBatches.executeMulticallBatches;
351
353
  exports.expectedBalanceDeltas = require_sdk_market_credit_expectedBalanceDeltas.expectedBalanceDeltas;
352
354
  exports.fetchRedstonePayloads = require_sdk_market_pricefeeds_updates_fetchRedstonePayloads.fetchRedstonePayloads;
353
355
  exports.filterDust = require_sdk_utils_filterDust.filterDust;
@@ -95,29 +95,38 @@ var MarketRegister = class extends require_sdk_market_ZapperRegister.ZapperRegis
95
95
  * Otherwise only prices are refreshed.
96
96
  *
97
97
  * @param ignoreUpdateablePrices - When `true`, skips off-chain price updates.
98
+ * @param extra - Additional multicalls to execute in the same batch, so that
99
+ * other registries can be synced without an extra request.
100
+ * @returns `true` when markets were fully reloaded, so that callers can
101
+ * refresh the caches derived from the market set.
98
102
  **/
99
- async syncState(ignoreUpdateablePrices) {
103
+ async syncState(ignoreUpdateablePrices, extra = []) {
100
104
  const dirty = this.markets.some((m) => m.dirty) || this.marketConfigurators.some((c) => c.dirty);
101
105
  let multicalls;
102
106
  let txs = [];
103
107
  if (dirty) {
104
108
  this.logger?.debug("some markets or market configurators are dirty, reloading everything");
105
- multicalls = this.getLoadMulticalls([...this.marketFilter.configurators]);
109
+ multicalls = [...this.getLoadMulticalls([...this.marketFilter.configurators]), ...extra];
106
110
  if (!ignoreUpdateablePrices) {
107
111
  const updatables = await this.sdk.priceFeeds.getPartialUpdatablePriceFeeds([...this.marketFilter.configurators]);
108
112
  txs = (await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(updatables)).txs;
109
113
  }
110
114
  } else if (!ignoreUpdateablePrices) {
111
- multicalls = this.#getOracleSyncMulticalls();
112
- if (!multicalls.length) return;
113
- this.logger?.debug(`syncing prices on ${multicalls.length} oracles`);
114
- txs = (await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs()).txs;
115
- } else return;
115
+ const oracles = this.#getOracleSyncMulticalls();
116
+ multicalls = [...oracles, ...extra];
117
+ if (!multicalls.length) return false;
118
+ if (oracles.length) {
119
+ this.logger?.debug(`syncing prices on ${oracles.length} oracles`);
120
+ txs = (await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs()).txs;
121
+ }
122
+ } else if (extra.length) multicalls = extra;
123
+ else return false;
116
124
  await require_sdk_utils_viem_executeDelegatedMulticalls.executeDelegatedMulticalls(this.client, multicalls, {
117
125
  priceUpdates: txs,
118
126
  blockNumber: this.sdk.currentBlock,
119
127
  gas: this.sdk.gasLimit
120
128
  });
129
+ return dirty;
121
130
  }
122
131
  #getOracleSyncMulticalls(oracles) {
123
132
  const uniqOracles = new require_sdk_utils_AddressMap.AddressMap();
@@ -5,53 +5,84 @@ const require_sdk_constants_versions = require("../constants/versions.js");
5
5
  require("../constants/index.js");
6
6
  const require_sdk_utils_hex = require("../utils/hex.js");
7
7
  require("../utils/index.js");
8
+ const require_sdk_utils_viem_executeMulticallBatches = require("../utils/viem/executeMulticallBatches.js");
8
9
  const require_sdk_base_SDKConstruct = require("../base/SDKConstruct.js");
9
10
  require("../base/index.js");
10
11
  const require_abi_compressors_peripheryCompressor = require("../../abi/compressors/peripheryCompressor.js");
12
+ require("../utils/viem/index.js");
11
13
  const require_sdk_market_zapper_createZapper = require("./zapper/createZapper.js");
12
14
  require("./zapper/index.js");
13
15
  //#region src/sdk/market/ZapperRegister.ts
14
16
  var ZapperRegister = class extends require_sdk_base_SDKConstruct.SDKConstruct {
15
17
  /**
16
18
  * Mapping pool.address -> Zapper[]
17
- * Needs to be loaded explicitly using loadZappers method
19
+ * Loaded during SDK attach, or restored by hydration
18
20
  */
19
21
  #zappers;
20
22
  /**
21
- * Load zappers for all pools using periphery compressor, adds hardcoded zappers
22
- */
23
- async loadZappers(force) {
24
- if (!force && this.#zappers) return;
25
- const [pcAddr] = this.sdk.addressProvider.mustGetLatest(require_sdk_constants_address_provider.AP_PERIPHERY_COMPRESSOR, require_sdk_constants_versions.VERSION_RANGE_310);
23
+ * @internal
24
+ *
25
+ * Returns the multicall batch that loads zappers of all pools from the
26
+ * periphery compressor. Used by the SDK to warm this cache together with
27
+ * other loaders in a single multicall.
28
+ *
29
+ * Returns an empty batch when zappers are already loaded and `force` is not set.
30
+ *
31
+ * @param force - reload zappers even when they are already loaded
32
+ **/
33
+ getLoadZappersMulticall(force) {
34
+ if (!force && this.#zappers) return {
35
+ contracts: [],
36
+ onResults: () => {}
37
+ };
38
+ this.#zappers = new require_sdk_utils_AddressMap.AddressMap(void 0, "zappers");
39
+ const compressor = this.sdk.addressProvider.getLatest(require_sdk_constants_address_provider.AP_PERIPHERY_COMPRESSOR, require_sdk_constants_versions.VERSION_RANGE_310);
40
+ if (!compressor) {
41
+ this.logger?.warn("no periphery compressor on this chain, skipping zappers");
42
+ return {
43
+ contracts: [],
44
+ onResults: () => {}
45
+ };
46
+ }
47
+ const [pcAddr] = compressor;
26
48
  this.logger?.debug(`loading zappers with periphery compressor ${pcAddr}`);
27
49
  const markets = this.sdk.marketRegister.markets;
28
- const resp = await this.client.multicall({
50
+ return {
29
51
  contracts: markets.map((m) => ({
30
52
  abi: require_abi_compressors_peripheryCompressor.peripheryCompressorAbi,
31
53
  address: pcAddr,
32
54
  functionName: "getZappers",
33
55
  args: [m.configurator.address, m.pool.pool.address]
34
56
  })),
35
- allowFailure: true,
36
- batchSize: 0
37
- });
38
- this.#zappers = new require_sdk_utils_AddressMap.AddressMap(void 0, "zappers");
39
- for (let i = 0; i < resp.length; i++) {
40
- const { status, result, error } = resp[i];
41
- const marketConfigurator = markets[i].configurator.address;
42
- const pool = markets[i].pool.pool.address;
43
- if (status === "success") for (const z of result) this.#addZapper({
44
- ...z,
45
- pool,
46
- type: "base"
47
- });
48
- else this.logger?.error(`failed to load zapper for market configurator ${this.labelAddress(marketConfigurator)} and pool ${this.labelAddress(pool)}: ${error}`);
49
- }
57
+ onResults: (resps) => {
58
+ for (let i = 0; i < resps.length; i++) {
59
+ const { status, result, error } = resps[i];
60
+ const marketConfigurator = markets[i].configurator.address;
61
+ const pool = markets[i].pool.pool.address;
62
+ if (status === "success") for (const z of result) this.#addZapper({
63
+ ...z,
64
+ pool,
65
+ type: "base"
66
+ });
67
+ else this.logger?.error(`failed to load zapper for market configurator ${this.labelAddress(marketConfigurator)} and pool ${this.labelAddress(pool)}: ${error}`);
68
+ }
69
+ }
70
+ };
71
+ }
72
+ /**
73
+ * Load zappers for all pools using periphery compressor, adds hardcoded zappers
74
+ *
75
+ * Zappers loaded during SDK attach or restored by hydration are kept unless
76
+ * `force` is set, so calling this after either is a no-op.
77
+ */
78
+ async loadZappers(force) {
79
+ await require_sdk_utils_viem_executeMulticallBatches.executeMulticallBatches(this.client, [this.getLoadZappersMulticall(force)]);
50
80
  }
51
81
  /**
52
82
  * Serializable snapshot of all loaded zappers, suitable for hydration.
53
- * Returns `undefined` when zappers were never loaded (i.e. `loadZappers` was
54
- * not called), so the not-loaded state round-trips cleanly.
83
+ * Returns `undefined` when zappers were never loaded (i.e. the SDK was
84
+ * attached without market configurators), so the not-loaded state round-trips
85
+ * cleanly.
55
86
  **/
56
87
  get zappersState() {
57
88
  if (!this.#zappers) return;
@@ -103,7 +134,7 @@ var ZapperRegister = class extends require_sdk_base_SDKConstruct.SDKConstruct {
103
134
  }
104
135
  }
105
136
  get zappers() {
106
- if (!this.#zappers) throw new Error("zappers not loaded, call loadZappers first");
137
+ if (!this.#zappers) throw new Error("zappers are not loaded, check if the sdk was properly attached or hydrated");
107
138
  return this.#zappers;
108
139
  }
109
140
  poolZappers(pool) {
@@ -25,6 +25,11 @@ var RWARegistry = class extends require_sdk_base_SDKConstruct.SDKConstruct {
25
25
  #state;
26
26
  #factories = new require_sdk_utils_AddressMap.AddressMap();
27
27
  /**
28
+ * Factories requested during attach; `undefined` when the registry was
29
+ * hydrated instead, in which case the request was never made on this instance.
30
+ **/
31
+ #requestedFactories;
32
+ /**
28
33
  * @internal
29
34
  *
30
35
  * Returns delegated multicalls for loading all RWA underlying tokens from the on-chain RWA compressor.
@@ -34,6 +39,7 @@ var RWARegistry = class extends require_sdk_base_SDKConstruct.SDKConstruct {
34
39
  * @param rwaFactories - RWA factory contracts to query.
35
40
  */
36
41
  getLoadMulticalls(configurators, rwaFactories = []) {
42
+ this.#requestedFactories = rwaFactories;
37
43
  if (!rwaFactories.length) return [];
38
44
  const [rwaCompressorAddress] = this.sdk.addressProvider.mustGetLatest(require_sdk_constants_address_provider.AP_RWA_COMPRESSOR, require_sdk_constants_versions.VERSION_RANGE_310);
39
45
  return [{
@@ -47,6 +53,22 @@ var RWARegistry = class extends require_sdk_base_SDKConstruct.SDKConstruct {
47
53
  }];
48
54
  }
49
55
  /**
56
+ * @internal
57
+ *
58
+ * Returns delegated multicalls that refresh the RWA state. Unlike the other
59
+ * warmed caches, RWA data carries values that change between blocks (e.g. the
60
+ * deposit allowance and claimable amount of an on-demand liquidity provider),
61
+ * so it is refreshed on every SDK sync.
62
+ *
63
+ * Factories are the ones requested during attach; after hydration the chain
64
+ * defaults are used, since the snapshot does not carry the requested list.
65
+ * An attach that explicitly requested no factories stays disabled.
66
+ **/
67
+ getSyncMulticalls() {
68
+ const factories = this.#requestedFactories ?? this.sdk.chain.rwaFactories;
69
+ return this.getLoadMulticalls([...this.sdk.marketRegister.marketFilter.configurators], factories);
70
+ }
71
+ /**
50
72
  * Fetches decoded investor data from the on-chain RWA compressor.
51
73
  *
52
74
  * Each factory produces its own investor data (e.g. registered tokens,
@@ -61,11 +61,7 @@ const attachOptionsSchema = zod_v4.z.object({
61
61
  /**
62
62
  * Options for Redstone price-feed updates.
63
63
  **/
64
- redstone: require_sdk_market_pricefeeds_updates_RedstoneUpdater.RedstoneOptions.optional(),
65
- /**
66
- * When `true`, automatically load zappers during attach.
67
- **/
68
- loadZappers: zod_v4.z.boolean().optional()
64
+ redstone: require_sdk_market_pricefeeds_updates_RedstoneUpdater.RedstoneOptions.optional()
69
65
  });
70
66
  //#endregion
71
67
  exports.attachOptionsSchema = attachOptionsSchema;