@gearbox-protocol/sdk 6.0.2 → 7.0.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 (30) hide show
  1. package/dist/cjs/sdk/GearboxSDK.js +34 -30
  2. package/dist/cjs/sdk/market/MarketRegister.js +5 -2
  3. package/dist/cjs/sdk/market/MarketSuite.js +3 -0
  4. package/dist/cjs/sdk/market/oracle/PriceOracleBaseContract.js +1 -3
  5. package/dist/cjs/sdk/market/pool/PoolSuite.js +7 -0
  6. package/dist/cjs/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -4
  7. package/dist/cjs/sdk/market/pricefeeds/RedstoneCache.js +61 -0
  8. package/dist/cjs/sdk/market/pricefeeds/RedstoneUpdater.js +33 -40
  9. package/dist/cjs/sdk/sdk-legacy/core/endpoint.js +2 -2
  10. package/dist/cjs/sdk/utils/index.js +3 -1
  11. package/dist/cjs/sdk/utils/type-utils.js +16 -0
  12. package/dist/esm/sdk/GearboxSDK.js +34 -30
  13. package/dist/esm/sdk/market/MarketRegister.js +5 -2
  14. package/dist/esm/sdk/market/MarketSuite.js +3 -0
  15. package/dist/esm/sdk/market/oracle/PriceOracleBaseContract.js +1 -3
  16. package/dist/esm/sdk/market/pool/PoolSuite.js +7 -0
  17. package/dist/esm/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -4
  18. package/dist/esm/sdk/market/pricefeeds/RedstoneCache.js +37 -0
  19. package/dist/esm/sdk/market/pricefeeds/RedstoneUpdater.js +33 -40
  20. package/dist/esm/sdk/sdk-legacy/core/endpoint.js +2 -2
  21. package/dist/esm/sdk/utils/index.js +1 -0
  22. package/dist/esm/sdk/utils/type-utils.js +0 -0
  23. package/dist/types/sdk/GearboxSDK.d.ts +9 -13
  24. package/dist/types/sdk/market/pool/PoolSuite.d.ts +3 -0
  25. package/dist/types/sdk/market/pricefeeds/PriceFeedsRegister.d.ts +6 -3
  26. package/dist/types/sdk/market/pricefeeds/RedstoneCache.d.ts +25 -0
  27. package/dist/types/sdk/market/pricefeeds/RedstoneUpdater.d.ts +27 -9
  28. package/dist/types/sdk/utils/index.d.ts +1 -0
  29. package/dist/types/sdk/utils/type-utils.d.ts +1 -0
  30. package/package.json +3 -2
@@ -50,16 +50,13 @@ class GearboxSDK {
50
50
  #attachConfig;
51
51
  // Collection of markets
52
52
  #marketRegister;
53
+ #priceFeeds;
53
54
  logger;
54
55
  /**
55
56
  * Interest rate models can be reused across chain (and SDK operates on chain level)
56
57
  * TODO: use whatever interface is necessary for InterestRateModels
57
58
  */
58
59
  interestRateModels = new import_utils.AddressMap();
59
- /**
60
- * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
61
- */
62
- priceFeeds;
63
60
  /**
64
61
  * Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
65
62
  */
@@ -82,9 +79,10 @@ class GearboxSDK {
82
79
  logger,
83
80
  plugins,
84
81
  blockNumber,
85
- redstoneHistoricTimestamp,
82
+ redstone,
86
83
  ignoreUpdateablePrices,
87
- marketConfigurators: mcs
84
+ marketConfigurators: mcs,
85
+ strictContractTypes
88
86
  } = options;
89
87
  let { networkType, addressProvider, chainId } = options;
90
88
  const attachClient = (0, import_viem.createPublicClient)({
@@ -108,28 +106,33 @@ class GearboxSDK {
108
106
  return new GearboxSDK({
109
107
  provider,
110
108
  logger,
111
- plugins
109
+ plugins,
110
+ strictContractTypes
112
111
  }).#attach({
113
112
  addressProvider,
114
113
  blockNumber,
115
- redstoneHistoricTimestamp,
116
114
  ignoreUpdateablePrices,
117
- marketConfigurators
115
+ marketConfigurators,
116
+ redstone
118
117
  });
119
118
  }
120
119
  static hydrate(options, state) {
121
- const { logger, plugins, ...rest } = options;
120
+ const { logger, plugins, strictContractTypes, ...rest } = options;
122
121
  const provider = new import_chain.Provider({
123
122
  ...rest,
124
123
  chainId: state.chainId,
125
124
  networkType: state.network
126
125
  });
127
- return new GearboxSDK({ provider, plugins, logger }).#hydrate(rest, state);
126
+ return new GearboxSDK({
127
+ provider,
128
+ plugins,
129
+ logger,
130
+ strictContractTypes
131
+ }).#hydrate(rest, state);
128
132
  }
129
133
  constructor(options) {
130
134
  this.#provider = options.provider;
131
135
  this.logger = options.logger;
132
- this.priceFeeds = new import_pricefeeds.PriceFeedRegister(this);
133
136
  this.strictContractTypes = options.strictContractTypes ?? false;
134
137
  const pluginsInstances = {};
135
138
  for (const [name, Plugin] of import_utils.TypedObjectUtils.entries(
@@ -143,9 +146,9 @@ class GearboxSDK {
143
146
  const {
144
147
  addressProvider,
145
148
  blockNumber,
146
- redstoneHistoricTimestamp,
147
149
  ignoreUpdateablePrices,
148
- marketConfigurators
150
+ marketConfigurators,
151
+ redstone
149
152
  } = opts;
150
153
  const re = this.#attachConfig ? "re" : "";
151
154
  this.logger?.info(
@@ -157,7 +160,7 @@ class GearboxSDK {
157
160
  },
158
161
  `${re}attaching gearbox sdk`
159
162
  );
160
- if (!!blockNumber && !redstoneHistoricTimestamp) {
163
+ if (!!blockNumber && !opts.redstone?.historicTimestamp) {
161
164
  this.logger?.warn(
162
165
  `${re}attaching to fixed block number, but redstoneHistoricTimestamp is not set. price updates might fail`
163
166
  );
@@ -171,7 +174,7 @@ class GearboxSDK {
171
174
  );
172
175
  this.#currentBlock = block.number;
173
176
  this.#timestamp = block.timestamp;
174
- this.#confugureRedstone(opts);
177
+ this.#priceFeeds = new import_pricefeeds.PriceFeedRegister(this, { redstone });
175
178
  this.logger?.debug(
176
179
  `${re}attach block number ${this.currentBlock} timestamp ${this.timestamp}`
177
180
  );
@@ -207,7 +210,7 @@ class GearboxSDK {
207
210
  return this;
208
211
  }
209
212
  #hydrate(options, state) {
210
- const { logger: _logger, ...opts } = options;
213
+ const { logger: _logger, redstone, ...opts } = options;
211
214
  if (state.version !== STATE_VERSION) {
212
215
  throw new Error(
213
216
  `hydrated state version is ${state.version}, but expected ${STATE_VERSION}`
@@ -215,7 +218,7 @@ class GearboxSDK {
215
218
  }
216
219
  this.#currentBlock = state.currentBlock;
217
220
  this.#timestamp = state.timestamp;
218
- this.#confugureRedstone(opts);
221
+ this.#priceFeeds = new import_pricefeeds.PriceFeedRegister(this, { redstone });
219
222
  this.#addressProvider = (0, import_core.hydrateAddressProvider)(this, state.addressProvider);
220
223
  this.logger?.debug(
221
224
  `address provider version: ${this.#addressProvider.version}`
@@ -241,15 +244,6 @@ class GearboxSDK {
241
244
  }
242
245
  return this;
243
246
  }
244
- #confugureRedstone(opts) {
245
- const { redstoneGateways, redstoneHistoricTimestamp } = opts;
246
- if (redstoneHistoricTimestamp) {
247
- this.priceFeeds.redstoneUpdater.historicalTimestamp = redstoneHistoricTimestamp === true ? Number(this.timestamp) * 1e3 : redstoneHistoricTimestamp;
248
- }
249
- if (redstoneGateways?.length) {
250
- this.priceFeeds.redstoneUpdater.gateways = redstoneGateways;
251
- }
252
- }
253
247
  /**
254
248
  * Reattach SDK with the same config as before, without re-creating instance. Will load all state from scratch
255
249
  * Be mindful of block number, for example
@@ -351,7 +345,7 @@ class GearboxSDK {
351
345
  */
352
346
  async syncState(opts) {
353
347
  let { blockNumber, timestamp, skipPriceUpdate } = opts ?? {};
354
- if (this.#attachConfig?.redstoneHistoricTimestamp) {
348
+ if (this.#attachConfig?.redstone?.historicTimestamp) {
355
349
  throw new Error(
356
350
  "syncState is not supported with redstoneHistoricTimestamp"
357
351
  );
@@ -376,11 +370,12 @@ class GearboxSDK {
376
370
  ...Array.from(this.marketRegister.watchAddresses),
377
371
  this.addressProvider.address
378
372
  ];
373
+ const fromBlock = this.currentBlock + 1n;
379
374
  this.logger?.debug(
380
- `getting logs from ${watchAddresses.length} addresses in [${this.currentBlock}:${blockNumber}]`
375
+ `getting logs from ${watchAddresses.length} addresses in [${fromBlock}:${blockNumber}]`
381
376
  );
382
377
  const logs = await (0, import_viem2.getLogsSafe)(this.provider.publicClient, {
383
- fromBlock: this.currentBlock,
378
+ fromBlock,
384
379
  toBlock: blockNumber,
385
380
  address: watchAddresses
386
381
  });
@@ -434,6 +429,15 @@ class GearboxSDK {
434
429
  }
435
430
  return this.#timestamp;
436
431
  }
432
+ /**
433
+ * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
434
+ */
435
+ get priceFeeds() {
436
+ if (this.#priceFeeds === void 0) {
437
+ throw ERR_NOT_ATTACHED;
438
+ }
439
+ return this.#priceFeeds;
440
+ }
437
441
  get gear() {
438
442
  try {
439
443
  const g = this.addressProvider.getAddress(import_constants.AP_GEAR_TOKEN, import_constants.NO_VERSION);
@@ -64,14 +64,17 @@ class MarketRegister extends import_base.SDKConstruct {
64
64
  const nonDirtyOracles = [];
65
65
  for (const m of this.markets) {
66
66
  if (m.dirty) {
67
- dirtyPools.push(m.pool.pool.address);
67
+ dirtyPools.push(m.pool);
68
68
  } else {
69
69
  nonDirtyOracles.push(m.priceOracle.address);
70
70
  }
71
71
  }
72
72
  if (dirtyPools.length) {
73
73
  this.#logger?.debug(`need to reload ${dirtyPools.length} markets`);
74
- await this.#loadMarkets([], dirtyPools);
74
+ await this.#loadMarkets(
75
+ Array.from(new Set(dirtyPools.map((p) => p.marketConfigurator.address))),
76
+ dirtyPools.map((p) => p.pool.address)
77
+ );
75
78
  } else if (!skipPriceUpdate && nonDirtyOracles.length) {
76
79
  this.#logger?.debug(
77
80
  `syncing prices on ${nonDirtyOracles.length} oracles`
@@ -59,6 +59,9 @@ class MarketSuite extends import_base.SDKConstruct {
59
59
  this.creditManagers.push(new import_credit.CreditSuite(sdk, marketData, i));
60
60
  }
61
61
  this.priceOracle = (0, import_oracle.getOrCreatePriceOracle)(sdk, marketData.priceOracle);
62
+ sdk.logger?.debug(
63
+ `oracle ${this.labelAddress(this.priceOracle.address)} has ${this.priceOracle.mainPriceFeeds.size} main and ${this.priceOracle.reservePriceFeeds.size} reserve price feeds`
64
+ );
62
65
  }
63
66
  get dirty() {
64
67
  return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
@@ -294,9 +294,7 @@ class PriceOracleBaseContract extends import_base.BaseContract {
294
294
  }
295
295
  this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
296
296
  }
297
- this.logger?.debug(
298
- `Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
299
- );
297
+ this.dirty = false;
300
298
  }
301
299
  #labelPriceFeed(address, usage, token) {
302
300
  this.sdk.provider.addressLabels.set(address, (oldLabel) => {
@@ -44,6 +44,7 @@ class PoolSuite extends import_base.SDKConstruct {
44
44
  pqk;
45
45
  rateKeeper;
46
46
  interestRateModel;
47
+ #marketConfigurator;
47
48
  constructor(sdk, data) {
48
49
  super(sdk);
49
50
  this.pool = (0, import_createPool.default)(sdk, data.pool);
@@ -53,6 +54,7 @@ class PoolSuite extends import_base.SDKConstruct {
53
54
  sdk,
54
55
  data.interestRateModel
55
56
  );
57
+ this.#marketConfigurator = data.configurator;
56
58
  }
57
59
  get gauge() {
58
60
  if (this.rateKeeper instanceof import_GaugeContract.GaugeContract) {
@@ -78,6 +80,11 @@ class PoolSuite extends import_base.SDKConstruct {
78
80
  `Interest rate model is not a linear model, but a ${this.interestRateModel.contractType}`
79
81
  );
80
82
  }
83
+ get marketConfigurator() {
84
+ return this.sdk.contracts.mustGet(
85
+ this.#marketConfigurator
86
+ );
87
+ }
81
88
  get underlying() {
82
89
  return this.pool.underlying;
83
90
  }
@@ -50,10 +50,10 @@ class PriceFeedRegister extends import_base.SDKConstruct {
50
50
  #feeds = new import_utils.AddressMap(void 0, "priceFeeds");
51
51
  #latestUpdate;
52
52
  redstoneUpdater;
53
- constructor(sdk) {
53
+ constructor(sdk, opts = {}) {
54
54
  super(sdk);
55
55
  this.logger = (0, import_utils.childLogger)("PriceFeedRegister", sdk.logger);
56
- this.redstoneUpdater = new import_RedstoneUpdater.RedstoneUpdater(sdk);
56
+ this.redstoneUpdater = new import_RedstoneUpdater.RedstoneUpdater(sdk, opts?.redstone);
57
57
  }
58
58
  addHook = this.#hooks.addHook.bind(this.#hooks);
59
59
  removeHook = this.#hooks.removeHook.bind(this.#hooks);
@@ -126,12 +126,11 @@ class PriceFeedRegister extends import_base.SDKConstruct {
126
126
  * Loads PARTIAL information about all updatable price feeds from MarketCompressor
127
127
  * This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
128
128
  */
129
- async getPartialUpdatablePriceFeeds(marketConfigurators, pools) {
129
+ async getPartialUpdatablePriceFeeds(configurators, pools) {
130
130
  const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
131
131
  import_constants.AP_PRICE_FEED_COMPRESSOR,
132
132
  import_constants.VERSION_RANGE_310
133
133
  );
134
- const configurators = marketConfigurators ?? this.sdk.marketRegister.marketConfigurators.map((mc) => mc.address);
135
134
  this.logger?.debug(
136
135
  { configurators, pools },
137
136
  `calling getUpdatablePriceFeeds in block ${this.sdk.currentBlock}`
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var RedstoneCache_exports = {};
20
+ __export(RedstoneCache_exports, {
21
+ RedstoneCache: () => RedstoneCache
22
+ });
23
+ module.exports = __toCommonJS(RedstoneCache_exports);
24
+ class RedstoneCache {
25
+ #cache = /* @__PURE__ */ new Map();
26
+ #ttlMs;
27
+ #historical;
28
+ constructor(opts) {
29
+ this.#ttlMs = opts.ttl;
30
+ this.#historical = opts.historical;
31
+ }
32
+ get(dataServiceId, dataFeedId, uniqueSignersCount) {
33
+ const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
34
+ const data = this.#cache.get(key);
35
+ if (!data) {
36
+ return void 0;
37
+ }
38
+ if (this.#expired(data)) {
39
+ this.#cache.delete(key);
40
+ return void 0;
41
+ }
42
+ return data;
43
+ }
44
+ set(dataServiceId, dataFeedId, uniqueSignersCount, value) {
45
+ const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
46
+ this.#cache.set(key, value);
47
+ }
48
+ #expired(value) {
49
+ if (this.#historical) {
50
+ return false;
51
+ }
52
+ return value.timestamp * 1e3 + this.#ttlMs < Date.now();
53
+ }
54
+ #cacheKey(dataServiceId, dataFeedId, uniqueSignersCount) {
55
+ return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}`;
56
+ }
57
+ }
58
+ // Annotate the CommonJS export names for ESM import in node:
59
+ 0 && (module.exports = {
60
+ RedstoneCache
61
+ });
@@ -27,6 +27,7 @@ var import_protocol = require("@redstone-finance/protocol");
27
27
  var import_viem = require("viem");
28
28
  var import_base = require("../../base/index.js");
29
29
  var import_utils = require("../../utils/index.js");
30
+ var import_RedstoneCache = require("./RedstoneCache.js");
30
31
  const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
31
32
  const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
32
33
  class RedstoneUpdateTx {
@@ -54,24 +55,29 @@ class RedstoneUpdateTx {
54
55
  }
55
56
  class RedstoneUpdater extends import_base.SDKConstruct {
56
57
  #logger;
57
- #cache = /* @__PURE__ */ new Map();
58
+ #cache;
58
59
  #historicalTimestampMs;
59
60
  #gateways;
60
- constructor(sdk) {
61
+ #ignoreMissingFeeds;
62
+ #enableLogging;
63
+ constructor(sdk, opts = {}) {
61
64
  super(sdk);
62
65
  this.#logger = (0, import_utils.childLogger)("RedstoneUpdater", sdk.logger);
63
- }
64
- /**
65
- * Set redstone historical timestamp in milliseconds
66
- */
67
- set historicalTimestamp(timestampMs) {
68
- this.#historicalTimestampMs = 6e4 * Math.floor(timestampMs / 6e4);
69
- }
70
- /**
71
- * Set redstone gateways
72
- */
73
- set gateways(gateways) {
74
- this.#gateways = gateways;
66
+ this.#ignoreMissingFeeds = opts.ignoreMissingFeeds;
67
+ this.#enableLogging = opts.enableLogging;
68
+ this.#gateways = opts.gateways?.length ? opts.gateways : void 0;
69
+ let ts = opts.historicTimestamp;
70
+ if (ts) {
71
+ ts = ts === true ? Number(this.sdk.timestamp) * 1e3 : ts;
72
+ this.#historicalTimestampMs = 6e4 * Math.floor(ts / 6e4);
73
+ }
74
+ this.#cache = new import_RedstoneCache.RedstoneCache({
75
+ // currently staleness period is 240 seconds on all networks, add some buffer
76
+ // this period of 4 minutes is selected based on time that is required for user to sign transaction with wallet
77
+ // so it's unlikely to decrease
78
+ ttl: opts.cacheTTL ?? 225 * 1e3,
79
+ historical: !!ts
80
+ });
75
81
  }
76
82
  async getUpdateTxs(feeds, logContext = {}) {
77
83
  this.#logger?.debug(
@@ -145,40 +151,28 @@ class RedstoneUpdater extends import_base.SDKConstruct {
145
151
  const fromCache = [];
146
152
  const uncached = [];
147
153
  for (const dataFeedId of dataFeedsIds) {
148
- const key = cacheKey(
154
+ const cached = this.#cache.get(
149
155
  dataServiceId,
150
156
  dataFeedId,
151
- uniqueSignersCount,
152
- this.#historicalTimestampMs
157
+ uniqueSignersCount
153
158
  );
154
- const cached = this.#cache.get(key);
155
- if (this.#historicalTimestampMs && !!cached) {
159
+ if (cached) {
156
160
  fromCache.push({ ...cached, cached: true });
157
161
  } else {
158
162
  uncached.push(dataFeedId);
159
163
  }
160
164
  }
161
- const [fromRedstoneResp] = await Promise.allSettled([
162
- this.#fetchPayloads(dataServiceId, new Set(uncached), uniqueSignersCount)
163
- ]);
164
- const fromRedstone = fromRedstoneResp.status === "fulfilled" ? fromRedstoneResp.value : [];
165
- if (this.#historicalTimestampMs) {
166
- for (const resp of fromRedstone) {
167
- const key = cacheKey(
168
- dataServiceId,
169
- resp.dataFeedId,
170
- uniqueSignersCount,
171
- this.#historicalTimestampMs
172
- );
173
- this.#cache.set(key, resp);
174
- }
165
+ const fromRedstone = await this.#fetchPayloads(
166
+ dataServiceId,
167
+ new Set(uncached),
168
+ uniqueSignersCount
169
+ );
170
+ for (const resp of fromRedstone) {
171
+ this.#cache.set(dataServiceId, resp.dataFeedId, uniqueSignersCount, resp);
175
172
  }
176
173
  this.#logger?.debug(
177
174
  `got ${fromRedstone.length} new redstone updates and ${fromCache.length} from cache`
178
175
  );
179
- if (fromRedstoneResp.status === "rejected") {
180
- this.#logger?.error(fromRedstoneResp.reason);
181
- }
182
176
  return [...fromCache, ...fromRedstone];
183
177
  }
184
178
  /**
@@ -203,7 +197,9 @@ class RedstoneUpdater extends import_base.SDKConstruct {
203
197
  dataPackagesIds,
204
198
  uniqueSignersCount,
205
199
  historicalTimestamp: this.#historicalTimestampMs,
206
- urls: this.#gateways
200
+ urls: this.#gateways,
201
+ ignoreMissingFeed: this.#ignoreMissingFeeds,
202
+ enableEnhancedLogs: this.#enableLogging
207
203
  });
208
204
  const dataPayload = await (0, import_utils.retry)(
209
205
  () => wrapper.prepareRedstonePayload(true),
@@ -229,9 +225,6 @@ class RedstoneUpdater extends import_base.SDKConstruct {
229
225
  });
230
226
  }
231
227
  }
232
- function cacheKey(dataServiceId, dataFeedId, uniqueSignersCount, historicalTimestamp = 0) {
233
- return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}:${historicalTimestamp}`;
234
- }
235
228
  function groupDataPackages(signedDataPackages) {
236
229
  const packagesByDataFeedId = {};
237
230
  for (const p of signedDataPackages) {
@@ -98,11 +98,11 @@ class GearboxBackendApi {
98
98
  static getStaticTokenUrl = () => STATIC_TOKEN;
99
99
  static getRewardsMerkleUrl = (network, root, account) => {
100
100
  const path = `${network}_${root.slice(2)}/${account.slice(2, 4)}`;
101
- const url = `https://am.gearbox.fi/${path.toLowerCase()}.json`;
101
+ const url = `https://am.gearbox.finance/${path.toLowerCase()}.json`;
102
102
  return url;
103
103
  };
104
104
  static getNFTMerkleUrl = (network, root) => {
105
- const url = `https://dm.gearbox.fi/${network.toLowerCase()}_${root}.json`;
105
+ const url = `https://dm.gearbox.finance/${network.toLowerCase()}_${root}.json`;
106
106
  return url;
107
107
  };
108
108
  static getLeaderboardUrl = (url, chainId) => {
@@ -27,6 +27,7 @@ __reExport(utils_exports, require("./json.js"), module.exports);
27
27
  __reExport(utils_exports, require("./mappers.js"), module.exports);
28
28
  __reExport(utils_exports, require("./retry.js"), module.exports);
29
29
  __reExport(utils_exports, require("./toAddress.js"), module.exports);
30
+ __reExport(utils_exports, require("./type-utils.js"), module.exports);
30
31
  // Annotate the CommonJS export names for ESM import in node:
31
32
  0 && (module.exports = {
32
33
  ...require("./AddressMap.js"),
@@ -40,5 +41,6 @@ __reExport(utils_exports, require("./toAddress.js"), module.exports);
40
41
  ...require("./json.js"),
41
42
  ...require("./mappers.js"),
42
43
  ...require("./retry.js"),
43
- ...require("./toAddress.js")
44
+ ...require("./toAddress.js"),
45
+ ...require("./type-utils.js")
44
46
  });
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var type_utils_exports = {};
16
+ module.exports = __toCommonJS(type_utils_exports);
@@ -48,16 +48,13 @@ class GearboxSDK {
48
48
  #attachConfig;
49
49
  // Collection of markets
50
50
  #marketRegister;
51
+ #priceFeeds;
51
52
  logger;
52
53
  /**
53
54
  * Interest rate models can be reused across chain (and SDK operates on chain level)
54
55
  * TODO: use whatever interface is necessary for InterestRateModels
55
56
  */
56
57
  interestRateModels = new AddressMap();
57
- /**
58
- * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
59
- */
60
- priceFeeds;
61
58
  /**
62
59
  * Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
63
60
  */
@@ -80,9 +77,10 @@ class GearboxSDK {
80
77
  logger,
81
78
  plugins,
82
79
  blockNumber,
83
- redstoneHistoricTimestamp,
80
+ redstone,
84
81
  ignoreUpdateablePrices,
85
- marketConfigurators: mcs
82
+ marketConfigurators: mcs,
83
+ strictContractTypes
86
84
  } = options;
87
85
  let { networkType, addressProvider, chainId } = options;
88
86
  const attachClient = createPublicClient({
@@ -106,28 +104,33 @@ class GearboxSDK {
106
104
  return new GearboxSDK({
107
105
  provider,
108
106
  logger,
109
- plugins
107
+ plugins,
108
+ strictContractTypes
110
109
  }).#attach({
111
110
  addressProvider,
112
111
  blockNumber,
113
- redstoneHistoricTimestamp,
114
112
  ignoreUpdateablePrices,
115
- marketConfigurators
113
+ marketConfigurators,
114
+ redstone
116
115
  });
117
116
  }
118
117
  static hydrate(options, state) {
119
- const { logger, plugins, ...rest } = options;
118
+ const { logger, plugins, strictContractTypes, ...rest } = options;
120
119
  const provider = new Provider({
121
120
  ...rest,
122
121
  chainId: state.chainId,
123
122
  networkType: state.network
124
123
  });
125
- return new GearboxSDK({ provider, plugins, logger }).#hydrate(rest, state);
124
+ return new GearboxSDK({
125
+ provider,
126
+ plugins,
127
+ logger,
128
+ strictContractTypes
129
+ }).#hydrate(rest, state);
126
130
  }
127
131
  constructor(options) {
128
132
  this.#provider = options.provider;
129
133
  this.logger = options.logger;
130
- this.priceFeeds = new PriceFeedRegister(this);
131
134
  this.strictContractTypes = options.strictContractTypes ?? false;
132
135
  const pluginsInstances = {};
133
136
  for (const [name, Plugin] of TypedObjectUtils.entries(
@@ -141,9 +144,9 @@ class GearboxSDK {
141
144
  const {
142
145
  addressProvider,
143
146
  blockNumber,
144
- redstoneHistoricTimestamp,
145
147
  ignoreUpdateablePrices,
146
- marketConfigurators
148
+ marketConfigurators,
149
+ redstone
147
150
  } = opts;
148
151
  const re = this.#attachConfig ? "re" : "";
149
152
  this.logger?.info(
@@ -155,7 +158,7 @@ class GearboxSDK {
155
158
  },
156
159
  `${re}attaching gearbox sdk`
157
160
  );
158
- if (!!blockNumber && !redstoneHistoricTimestamp) {
161
+ if (!!blockNumber && !opts.redstone?.historicTimestamp) {
159
162
  this.logger?.warn(
160
163
  `${re}attaching to fixed block number, but redstoneHistoricTimestamp is not set. price updates might fail`
161
164
  );
@@ -169,7 +172,7 @@ class GearboxSDK {
169
172
  );
170
173
  this.#currentBlock = block.number;
171
174
  this.#timestamp = block.timestamp;
172
- this.#confugureRedstone(opts);
175
+ this.#priceFeeds = new PriceFeedRegister(this, { redstone });
173
176
  this.logger?.debug(
174
177
  `${re}attach block number ${this.currentBlock} timestamp ${this.timestamp}`
175
178
  );
@@ -205,7 +208,7 @@ class GearboxSDK {
205
208
  return this;
206
209
  }
207
210
  #hydrate(options, state) {
208
- const { logger: _logger, ...opts } = options;
211
+ const { logger: _logger, redstone, ...opts } = options;
209
212
  if (state.version !== STATE_VERSION) {
210
213
  throw new Error(
211
214
  `hydrated state version is ${state.version}, but expected ${STATE_VERSION}`
@@ -213,7 +216,7 @@ class GearboxSDK {
213
216
  }
214
217
  this.#currentBlock = state.currentBlock;
215
218
  this.#timestamp = state.timestamp;
216
- this.#confugureRedstone(opts);
219
+ this.#priceFeeds = new PriceFeedRegister(this, { redstone });
217
220
  this.#addressProvider = hydrateAddressProvider(this, state.addressProvider);
218
221
  this.logger?.debug(
219
222
  `address provider version: ${this.#addressProvider.version}`
@@ -239,15 +242,6 @@ class GearboxSDK {
239
242
  }
240
243
  return this;
241
244
  }
242
- #confugureRedstone(opts) {
243
- const { redstoneGateways, redstoneHistoricTimestamp } = opts;
244
- if (redstoneHistoricTimestamp) {
245
- this.priceFeeds.redstoneUpdater.historicalTimestamp = redstoneHistoricTimestamp === true ? Number(this.timestamp) * 1e3 : redstoneHistoricTimestamp;
246
- }
247
- if (redstoneGateways?.length) {
248
- this.priceFeeds.redstoneUpdater.gateways = redstoneGateways;
249
- }
250
- }
251
245
  /**
252
246
  * Reattach SDK with the same config as before, without re-creating instance. Will load all state from scratch
253
247
  * Be mindful of block number, for example
@@ -349,7 +343,7 @@ class GearboxSDK {
349
343
  */
350
344
  async syncState(opts) {
351
345
  let { blockNumber, timestamp, skipPriceUpdate } = opts ?? {};
352
- if (this.#attachConfig?.redstoneHistoricTimestamp) {
346
+ if (this.#attachConfig?.redstone?.historicTimestamp) {
353
347
  throw new Error(
354
348
  "syncState is not supported with redstoneHistoricTimestamp"
355
349
  );
@@ -374,11 +368,12 @@ class GearboxSDK {
374
368
  ...Array.from(this.marketRegister.watchAddresses),
375
369
  this.addressProvider.address
376
370
  ];
371
+ const fromBlock = this.currentBlock + 1n;
377
372
  this.logger?.debug(
378
- `getting logs from ${watchAddresses.length} addresses in [${this.currentBlock}:${blockNumber}]`
373
+ `getting logs from ${watchAddresses.length} addresses in [${fromBlock}:${blockNumber}]`
379
374
  );
380
375
  const logs = await getLogsSafe(this.provider.publicClient, {
381
- fromBlock: this.currentBlock,
376
+ fromBlock,
382
377
  toBlock: blockNumber,
383
378
  address: watchAddresses
384
379
  });
@@ -432,6 +427,15 @@ class GearboxSDK {
432
427
  }
433
428
  return this.#timestamp;
434
429
  }
430
+ /**
431
+ * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
432
+ */
433
+ get priceFeeds() {
434
+ if (this.#priceFeeds === void 0) {
435
+ throw ERR_NOT_ATTACHED;
436
+ }
437
+ return this.#priceFeeds;
438
+ }
435
439
  get gear() {
436
440
  try {
437
441
  const g = this.addressProvider.getAddress(AP_GEAR_TOKEN, NO_VERSION);
@@ -45,14 +45,17 @@ class MarketRegister extends SDKConstruct {
45
45
  const nonDirtyOracles = [];
46
46
  for (const m of this.markets) {
47
47
  if (m.dirty) {
48
- dirtyPools.push(m.pool.pool.address);
48
+ dirtyPools.push(m.pool);
49
49
  } else {
50
50
  nonDirtyOracles.push(m.priceOracle.address);
51
51
  }
52
52
  }
53
53
  if (dirtyPools.length) {
54
54
  this.#logger?.debug(`need to reload ${dirtyPools.length} markets`);
55
- await this.#loadMarkets([], dirtyPools);
55
+ await this.#loadMarkets(
56
+ Array.from(new Set(dirtyPools.map((p) => p.marketConfigurator.address))),
57
+ dirtyPools.map((p) => p.pool.address)
58
+ );
56
59
  } else if (!skipPriceUpdate && nonDirtyOracles.length) {
57
60
  this.#logger?.debug(
58
61
  `syncing prices on ${nonDirtyOracles.length} oracles`
@@ -36,6 +36,9 @@ class MarketSuite extends SDKConstruct {
36
36
  this.creditManagers.push(new CreditSuite(sdk, marketData, i));
37
37
  }
38
38
  this.priceOracle = getOrCreatePriceOracle(sdk, marketData.priceOracle);
39
+ sdk.logger?.debug(
40
+ `oracle ${this.labelAddress(this.priceOracle.address)} has ${this.priceOracle.mainPriceFeeds.size} main and ${this.priceOracle.reservePriceFeeds.size} reserve price feeds`
41
+ );
39
42
  }
40
43
  get dirty() {
41
44
  return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
@@ -265,9 +265,7 @@ class PriceOracleBaseContract extends BaseContract {
265
265
  }
266
266
  this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
267
267
  }
268
- this.logger?.debug(
269
- `Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
270
- );
268
+ this.dirty = false;
271
269
  }
272
270
  #labelPriceFeed(address, usage, token) {
273
271
  this.sdk.provider.addressLabels.set(address, (oldLabel) => {
@@ -11,6 +11,7 @@ class PoolSuite extends SDKConstruct {
11
11
  pqk;
12
12
  rateKeeper;
13
13
  interestRateModel;
14
+ #marketConfigurator;
14
15
  constructor(sdk, data) {
15
16
  super(sdk);
16
17
  this.pool = createPool(sdk, data.pool);
@@ -20,6 +21,7 @@ class PoolSuite extends SDKConstruct {
20
21
  sdk,
21
22
  data.interestRateModel
22
23
  );
24
+ this.#marketConfigurator = data.configurator;
23
25
  }
24
26
  get gauge() {
25
27
  if (this.rateKeeper instanceof GaugeContract) {
@@ -45,6 +47,11 @@ class PoolSuite extends SDKConstruct {
45
47
  `Interest rate model is not a linear model, but a ${this.interestRateModel.contractType}`
46
48
  );
47
49
  }
50
+ get marketConfigurator() {
51
+ return this.sdk.contracts.mustGet(
52
+ this.#marketConfigurator
53
+ );
54
+ }
48
55
  get underlying() {
49
56
  return this.pool.underlying;
50
57
  }
@@ -33,10 +33,10 @@ class PriceFeedRegister extends SDKConstruct {
33
33
  #feeds = new AddressMap(void 0, "priceFeeds");
34
34
  #latestUpdate;
35
35
  redstoneUpdater;
36
- constructor(sdk) {
36
+ constructor(sdk, opts = {}) {
37
37
  super(sdk);
38
38
  this.logger = childLogger("PriceFeedRegister", sdk.logger);
39
- this.redstoneUpdater = new RedstoneUpdater(sdk);
39
+ this.redstoneUpdater = new RedstoneUpdater(sdk, opts?.redstone);
40
40
  }
41
41
  addHook = this.#hooks.addHook.bind(this.#hooks);
42
42
  removeHook = this.#hooks.removeHook.bind(this.#hooks);
@@ -109,12 +109,11 @@ class PriceFeedRegister extends SDKConstruct {
109
109
  * Loads PARTIAL information about all updatable price feeds from MarketCompressor
110
110
  * This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
111
111
  */
112
- async getPartialUpdatablePriceFeeds(marketConfigurators, pools) {
112
+ async getPartialUpdatablePriceFeeds(configurators, pools) {
113
113
  const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
114
114
  AP_PRICE_FEED_COMPRESSOR,
115
115
  VERSION_RANGE_310
116
116
  );
117
- const configurators = marketConfigurators ?? this.sdk.marketRegister.marketConfigurators.map((mc) => mc.address);
118
117
  this.logger?.debug(
119
118
  { configurators, pools },
120
119
  `calling getUpdatablePriceFeeds in block ${this.sdk.currentBlock}`
@@ -0,0 +1,37 @@
1
+ class RedstoneCache {
2
+ #cache = /* @__PURE__ */ new Map();
3
+ #ttlMs;
4
+ #historical;
5
+ constructor(opts) {
6
+ this.#ttlMs = opts.ttl;
7
+ this.#historical = opts.historical;
8
+ }
9
+ get(dataServiceId, dataFeedId, uniqueSignersCount) {
10
+ const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
11
+ const data = this.#cache.get(key);
12
+ if (!data) {
13
+ return void 0;
14
+ }
15
+ if (this.#expired(data)) {
16
+ this.#cache.delete(key);
17
+ return void 0;
18
+ }
19
+ return data;
20
+ }
21
+ set(dataServiceId, dataFeedId, uniqueSignersCount, value) {
22
+ const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
23
+ this.#cache.set(key, value);
24
+ }
25
+ #expired(value) {
26
+ if (this.#historical) {
27
+ return false;
28
+ }
29
+ return value.timestamp * 1e3 + this.#ttlMs < Date.now();
30
+ }
31
+ #cacheKey(dataServiceId, dataFeedId, uniqueSignersCount) {
32
+ return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}`;
33
+ }
34
+ }
35
+ export {
36
+ RedstoneCache
37
+ };
@@ -3,6 +3,7 @@ import { RedstonePayload } from "@redstone-finance/protocol";
3
3
  import { encodeAbiParameters, toBytes } from "viem";
4
4
  import { SDKConstruct } from "../../base/index.js";
5
5
  import { AddressMap, childLogger, retry } from "../../utils/index.js";
6
+ import { RedstoneCache } from "./RedstoneCache.js";
6
7
  const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
7
8
  const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
8
9
  class RedstoneUpdateTx {
@@ -30,24 +31,29 @@ class RedstoneUpdateTx {
30
31
  }
31
32
  class RedstoneUpdater extends SDKConstruct {
32
33
  #logger;
33
- #cache = /* @__PURE__ */ new Map();
34
+ #cache;
34
35
  #historicalTimestampMs;
35
36
  #gateways;
36
- constructor(sdk) {
37
+ #ignoreMissingFeeds;
38
+ #enableLogging;
39
+ constructor(sdk, opts = {}) {
37
40
  super(sdk);
38
41
  this.#logger = childLogger("RedstoneUpdater", sdk.logger);
39
- }
40
- /**
41
- * Set redstone historical timestamp in milliseconds
42
- */
43
- set historicalTimestamp(timestampMs) {
44
- this.#historicalTimestampMs = 6e4 * Math.floor(timestampMs / 6e4);
45
- }
46
- /**
47
- * Set redstone gateways
48
- */
49
- set gateways(gateways) {
50
- this.#gateways = gateways;
42
+ this.#ignoreMissingFeeds = opts.ignoreMissingFeeds;
43
+ this.#enableLogging = opts.enableLogging;
44
+ this.#gateways = opts.gateways?.length ? opts.gateways : void 0;
45
+ let ts = opts.historicTimestamp;
46
+ if (ts) {
47
+ ts = ts === true ? Number(this.sdk.timestamp) * 1e3 : ts;
48
+ this.#historicalTimestampMs = 6e4 * Math.floor(ts / 6e4);
49
+ }
50
+ this.#cache = new RedstoneCache({
51
+ // currently staleness period is 240 seconds on all networks, add some buffer
52
+ // this period of 4 minutes is selected based on time that is required for user to sign transaction with wallet
53
+ // so it's unlikely to decrease
54
+ ttl: opts.cacheTTL ?? 225 * 1e3,
55
+ historical: !!ts
56
+ });
51
57
  }
52
58
  async getUpdateTxs(feeds, logContext = {}) {
53
59
  this.#logger?.debug(
@@ -121,40 +127,28 @@ class RedstoneUpdater extends SDKConstruct {
121
127
  const fromCache = [];
122
128
  const uncached = [];
123
129
  for (const dataFeedId of dataFeedsIds) {
124
- const key = cacheKey(
130
+ const cached = this.#cache.get(
125
131
  dataServiceId,
126
132
  dataFeedId,
127
- uniqueSignersCount,
128
- this.#historicalTimestampMs
133
+ uniqueSignersCount
129
134
  );
130
- const cached = this.#cache.get(key);
131
- if (this.#historicalTimestampMs && !!cached) {
135
+ if (cached) {
132
136
  fromCache.push({ ...cached, cached: true });
133
137
  } else {
134
138
  uncached.push(dataFeedId);
135
139
  }
136
140
  }
137
- const [fromRedstoneResp] = await Promise.allSettled([
138
- this.#fetchPayloads(dataServiceId, new Set(uncached), uniqueSignersCount)
139
- ]);
140
- const fromRedstone = fromRedstoneResp.status === "fulfilled" ? fromRedstoneResp.value : [];
141
- if (this.#historicalTimestampMs) {
142
- for (const resp of fromRedstone) {
143
- const key = cacheKey(
144
- dataServiceId,
145
- resp.dataFeedId,
146
- uniqueSignersCount,
147
- this.#historicalTimestampMs
148
- );
149
- this.#cache.set(key, resp);
150
- }
141
+ const fromRedstone = await this.#fetchPayloads(
142
+ dataServiceId,
143
+ new Set(uncached),
144
+ uniqueSignersCount
145
+ );
146
+ for (const resp of fromRedstone) {
147
+ this.#cache.set(dataServiceId, resp.dataFeedId, uniqueSignersCount, resp);
151
148
  }
152
149
  this.#logger?.debug(
153
150
  `got ${fromRedstone.length} new redstone updates and ${fromCache.length} from cache`
154
151
  );
155
- if (fromRedstoneResp.status === "rejected") {
156
- this.#logger?.error(fromRedstoneResp.reason);
157
- }
158
152
  return [...fromCache, ...fromRedstone];
159
153
  }
160
154
  /**
@@ -179,7 +173,9 @@ class RedstoneUpdater extends SDKConstruct {
179
173
  dataPackagesIds,
180
174
  uniqueSignersCount,
181
175
  historicalTimestamp: this.#historicalTimestampMs,
182
- urls: this.#gateways
176
+ urls: this.#gateways,
177
+ ignoreMissingFeed: this.#ignoreMissingFeeds,
178
+ enableEnhancedLogs: this.#enableLogging
183
179
  });
184
180
  const dataPayload = await retry(
185
181
  () => wrapper.prepareRedstonePayload(true),
@@ -205,9 +201,6 @@ class RedstoneUpdater extends SDKConstruct {
205
201
  });
206
202
  }
207
203
  }
208
- function cacheKey(dataServiceId, dataFeedId, uniqueSignersCount, historicalTimestamp = 0) {
209
- return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}:${historicalTimestamp}`;
210
- }
211
204
  function groupDataPackages(signedDataPackages) {
212
205
  const packagesByDataFeedId = {};
213
206
  for (const p of signedDataPackages) {
@@ -69,11 +69,11 @@ class GearboxBackendApi {
69
69
  static getStaticTokenUrl = () => STATIC_TOKEN;
70
70
  static getRewardsMerkleUrl = (network, root, account) => {
71
71
  const path = `${network}_${root.slice(2)}/${account.slice(2, 4)}`;
72
- const url = `https://am.gearbox.fi/${path.toLowerCase()}.json`;
72
+ const url = `https://am.gearbox.finance/${path.toLowerCase()}.json`;
73
73
  return url;
74
74
  };
75
75
  static getNFTMerkleUrl = (network, root) => {
76
- const url = `https://dm.gearbox.fi/${network.toLowerCase()}_${root}.json`;
76
+ const url = `https://dm.gearbox.finance/${network.toLowerCase()}_${root}.json`;
77
77
  return url;
78
78
  };
79
79
  static getLeaderboardUrl = (url, chainId) => {
@@ -10,3 +10,4 @@ export * from "./json.js";
10
10
  export * from "./mappers.js";
11
11
  export * from "./retry.js";
12
12
  export * from "./toAddress.js";
13
+ export * from "./type-utils.js";
File without changes
@@ -7,6 +7,7 @@ import type { IAddressProviderContract } from "./core/index.js";
7
7
  import { BotListContract, GearStakingContract } from "./core/index.js";
8
8
  import { MarketRegister } from "./market/MarketRegister.js";
9
9
  import { PriceFeedRegister } from "./market/pricefeeds/index.js";
10
+ import type { RedstoneOptions } from "./market/pricefeeds/RedstoneUpdater.js";
10
11
  import { type PluginConstructorMap, type PluginsMap } from "./plugins/index.js";
11
12
  import { type IRouterContract } from "./router/index.js";
12
13
  import type { GearboxState, GearboxStateHuman, ILogger, MultiCall } from "./types/index.js";
@@ -28,15 +29,6 @@ export interface SDKOptions<Plugins extends PluginsMap> {
28
29
  * Attach and load state at this specific block number
29
30
  */
30
31
  blockNumber?: bigint | number;
31
- /**
32
- * Fixed redstone historic timestamp in ms
33
- * Set to true to enable redstone historical mode using timestamp from attach block
34
- */
35
- redstoneHistoricTimestamp?: number | true;
36
- /**
37
- * Override redstone gateways. Can be used to set caching proxies, to avoid rate limiting
38
- */
39
- redstoneGateways?: string[];
40
32
  /**
41
33
  * Will skip updateable prices on attach and sync
42
34
  * Makes things faster when your service is not intereseted in prices
@@ -54,6 +46,10 @@ export interface SDKOptions<Plugins extends PluginsMap> {
54
46
  * Bring your own logger
55
47
  */
56
48
  logger?: ILogger;
49
+ /**
50
+ * Options related to redstone price feeds
51
+ */
52
+ redstone?: RedstoneOptions;
57
53
  }
58
54
  export type HydrateOptions<Plugins extends PluginsMap> = Omit<SDKOptions<Plugins>, "blockNumber" | "addressProvider" | "marketConfigurators">;
59
55
  export interface SyncStateOptions {
@@ -73,10 +69,6 @@ export declare class GearboxSDK<const Plugins extends PluginsMap = {}> {
73
69
  * TODO: use whatever interface is necessary for InterestRateModels
74
70
  */
75
71
  readonly interestRateModels: AddressMap<BaseContract<readonly unknown[]>>;
76
- /**
77
- * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
78
- */
79
- readonly priceFeeds: PriceFeedRegister;
80
72
  /**
81
73
  * Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
82
74
  */
@@ -150,6 +142,10 @@ export declare class GearboxSDK<const Plugins extends PluginsMap = {}> {
150
142
  get provider(): Provider;
151
143
  get currentBlock(): bigint;
152
144
  get timestamp(): bigint;
145
+ /**
146
+ * All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
147
+ */
148
+ get priceFeeds(): PriceFeedRegister;
153
149
  get gear(): Address | undefined;
154
150
  get addressProvider(): IAddressProviderContract;
155
151
  get botListContract(): BotListContract | undefined;
@@ -3,11 +3,13 @@ import type { MarketData } from "../../base/index.js";
3
3
  import { SDKConstruct } from "../../base/index.js";
4
4
  import type { GearboxSDK } from "../../GearboxSDK.js";
5
5
  import type { PoolSuiteStateHuman } from "../../types/index.js";
6
+ import type { MarketConfiguratorContract } from "../MarketConfiguratorContract.js";
6
7
  import { GaugeContract } from "./GaugeContract.js";
7
8
  import { LinearInterestRateModelContract } from "./LinearInterestRateModelContract.js";
8
9
  import { TumblerContract } from "./TumblerContract.js";
9
10
  import type { IInterestRateModelContract, IRateKeeperContract, PoolContract, PoolQuotaKeeperContract } from "./types.js";
10
11
  export declare class PoolSuite extends SDKConstruct {
12
+ #private;
11
13
  readonly pool: PoolContract;
12
14
  readonly pqk: PoolQuotaKeeperContract;
13
15
  readonly rateKeeper: IRateKeeperContract;
@@ -16,6 +18,7 @@ export declare class PoolSuite extends SDKConstruct {
16
18
  get gauge(): GaugeContract;
17
19
  get tumbler(): TumblerContract;
18
20
  get linearModel(): LinearInterestRateModelContract;
21
+ get marketConfigurator(): MarketConfiguratorContract;
19
22
  get underlying(): Address;
20
23
  get dirty(): boolean;
21
24
  get watchAddresses(): Set<Address>;
@@ -5,7 +5,7 @@ import type { GearboxSDK } from "../../GearboxSDK.js";
5
5
  import type { ILogger } from "../../types/index.js";
6
6
  import type { IHooks } from "../../utils/internal/index.js";
7
7
  import { type PartialPriceFeedTreeNode } from "./AbstractPriceFeed.js";
8
- import type { RedstoneUpdateTask } from "./RedstoneUpdater.js";
8
+ import type { RedstoneOptions, RedstoneUpdateTask } from "./RedstoneUpdater.js";
9
9
  import { RedstoneUpdater } from "./RedstoneUpdater.js";
10
10
  import type { IPriceFeedContract, UpdatePriceFeedsResult } from "./types.js";
11
11
  export type PriceFeedRegisterHooks = {
@@ -14,6 +14,9 @@ export type PriceFeedRegisterHooks = {
14
14
  */
15
15
  updatesGenerated: [UpdatePriceFeedsResult];
16
16
  };
17
+ export interface PriceFeedRegisterOptions {
18
+ redstone?: RedstoneOptions;
19
+ }
17
20
  export interface LatestUpdate {
18
21
  timestamp: number;
19
22
  redstone: RedstoneUpdateTask[];
@@ -27,7 +30,7 @@ export declare class PriceFeedRegister extends SDKConstruct implements IHooks<Pr
27
30
  #private;
28
31
  readonly logger?: ILogger;
29
32
  readonly redstoneUpdater: RedstoneUpdater;
30
- constructor(sdk: GearboxSDK);
33
+ constructor(sdk: GearboxSDK, opts?: PriceFeedRegisterOptions);
31
34
  addHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
32
35
  removeHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
33
36
  /**
@@ -44,7 +47,7 @@ export declare class PriceFeedRegister extends SDKConstruct implements IHooks<Pr
44
47
  * Loads PARTIAL information about all updatable price feeds from MarketCompressor
45
48
  * This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
46
49
  */
47
- getPartialUpdatablePriceFeeds(marketConfigurators?: Address[], pools?: Address[]): Promise<IPriceFeedContract[]>;
50
+ getPartialUpdatablePriceFeeds(configurators: Address[], pools?: Address[]): Promise<IPriceFeedContract[]>;
48
51
  create(data: PartialPriceFeedTreeNode): IPriceFeedContract;
49
52
  /**
50
53
  * Information update latest update of updatable price feeds, for diagnostic purposes
@@ -0,0 +1,25 @@
1
+ export interface TimestampedCalldata {
2
+ dataFeedId: string;
3
+ data: `0x${string}`;
4
+ /**
5
+ * This timestamp is in seconds
6
+ */
7
+ timestamp: number;
8
+ cached: boolean;
9
+ }
10
+ export interface RedstoneCacheOptions {
11
+ /**
12
+ * Assume that in historical mode we only need to fetch once and then reuse from cache forever
13
+ */
14
+ historical: boolean;
15
+ /**
16
+ * TTL in milliseconds
17
+ */
18
+ ttl: number;
19
+ }
20
+ export declare class RedstoneCache {
21
+ #private;
22
+ constructor(opts: RedstoneCacheOptions);
23
+ get(dataServiceId: string, dataFeedId: string, uniqueSignersCount: number): Omit<TimestampedCalldata, "cached"> | undefined;
24
+ set(dataServiceId: string, dataFeedId: string, uniqueSignersCount: number, value: Omit<TimestampedCalldata, "cached">): void;
25
+ }
@@ -17,19 +17,37 @@ export declare class RedstoneUpdateTx implements IPriceUpdateTx<RedstoneUpdateTa
17
17
  get pretty(): string;
18
18
  validateTimestamp(blockTimestamp: bigint): "valid" | "too old" | "in future";
19
19
  }
20
+ export interface RedstoneOptions {
21
+ /**
22
+ * Fixed redstone historic timestamp in ms
23
+ * Set to true to enable redstone historical mode using timestamp from attach block
24
+ */
25
+ historicTimestamp?: number | true;
26
+ /**
27
+ * Override redstone gateways. Can be used to set caching proxies, to avoid rate limiting
28
+ */
29
+ gateways?: string[];
30
+ /**
31
+ * TTL for redstone cache in milliseconds
32
+ * If 0, disables caching
33
+ * If not set, uses some default value
34
+ * Cache is always enabled in historical mode
35
+ */
36
+ cacheTTL?: number;
37
+ /**
38
+ * When true, no error will be thrown when redstone is unable to fetch data for some feeds
39
+ */
40
+ ignoreMissingFeeds?: boolean;
41
+ /**
42
+ * Enable redstone internal logging
43
+ */
44
+ enableLogging?: boolean;
45
+ }
20
46
  /**
21
47
  * Class to update multiple redstone price feeds at once
22
48
  */
23
49
  export declare class RedstoneUpdater extends SDKConstruct {
24
50
  #private;
25
- constructor(sdk: GearboxSDK);
26
- /**
27
- * Set redstone historical timestamp in milliseconds
28
- */
29
- set historicalTimestamp(timestampMs: number);
30
- /**
31
- * Set redstone gateways
32
- */
33
- set gateways(gateways: string[]);
51
+ constructor(sdk: GearboxSDK, opts?: RedstoneOptions);
34
52
  getUpdateTxs(feeds: RedstonePriceFeedContract[], logContext?: Record<string, any>): Promise<RedstoneUpdateTx[]>;
35
53
  }
@@ -10,3 +10,4 @@ export * from "./json.js";
10
10
  export * from "./mappers.js";
11
11
  export * from "./retry.js";
12
12
  export * from "./toAddress.js";
13
+ export * from "./type-utils.js";
@@ -0,0 +1 @@
1
+ export type PickSomeRequired<T, RequiredKeys extends keyof T, OptionalKeys extends keyof T> = Required<Pick<T, RequiredKeys>> & Partial<Pick<T, OptionalKeys>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "6.0.2",
3
+ "version": "7.0.0-next.2",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "main": "./dist/cjs/sdk/index.js",
@@ -70,6 +70,7 @@
70
70
  "abitype": "^1.0.8",
71
71
  "date-fns": "^4.1.0",
72
72
  "decimal.js-light": "^2.5.1",
73
+ "keyv": "^5.3.3",
73
74
  "viem": ">=2.23.15 <3.0.0",
74
75
  "zod": "^3.24.4"
75
76
  },
@@ -84,7 +85,7 @@
84
85
  "eslint": "^8.57.1",
85
86
  "eslint-plugin-require-extensions": "^0.1.3",
86
87
  "husky": "^9.1.7",
87
- "lint-staged": "^15.5.2",
88
+ "lint-staged": "^16.0.0",
88
89
  "pino": "^9.6.0",
89
90
  "pino-pretty": "^13.0.0",
90
91
  "prettier": "^3.5.3",