@gearbox-protocol/sdk 5.2.0 → 6.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 (28) hide show
  1. package/dist/cjs/dev/calcLiquidatableLTs.js +5 -1
  2. package/dist/cjs/sdk/base/BaseContract.js +0 -1
  3. package/dist/cjs/sdk/market/MarketSuite.js +1 -5
  4. package/dist/cjs/sdk/market/oracle/PriceOracleBaseContract.js +52 -47
  5. package/dist/cjs/sdk/market/oracle/PriceOracleV300Contract.js +2 -3
  6. package/dist/cjs/sdk/market/oracle/PriceOracleV310Contract.js +2 -3
  7. package/dist/cjs/sdk/market/oracle/createPriceOracle.js +28 -9
  8. package/dist/cjs/sdk/market/pricefeeds/AbstractLPPriceFeed.js +2 -1
  9. package/dist/cjs/sdk/market/pricefeeds/RedstonePriceFeed.js +1 -1
  10. package/dist/cjs/sdk/plugins/V300StalenessPeriodPlugin.js +5 -5
  11. package/dist/esm/dev/calcLiquidatableLTs.js +5 -1
  12. package/dist/esm/sdk/base/BaseContract.js +0 -1
  13. package/dist/esm/sdk/market/MarketSuite.js +2 -6
  14. package/dist/esm/sdk/market/oracle/PriceOracleBaseContract.js +53 -47
  15. package/dist/esm/sdk/market/oracle/PriceOracleV300Contract.js +2 -3
  16. package/dist/esm/sdk/market/oracle/PriceOracleV310Contract.js +2 -3
  17. package/dist/esm/sdk/market/oracle/createPriceOracle.js +27 -8
  18. package/dist/esm/sdk/market/pricefeeds/AbstractLPPriceFeed.js +2 -1
  19. package/dist/esm/sdk/market/pricefeeds/RedstonePriceFeed.js +2 -2
  20. package/dist/esm/sdk/plugins/V300StalenessPeriodPlugin.js +6 -6
  21. package/dist/types/sdk/market/MarketSuite.d.ts +2 -2
  22. package/dist/types/sdk/market/oracle/PriceOracleBaseContract.d.ts +28 -684
  23. package/dist/types/sdk/market/oracle/PriceOracleV300Contract.d.ts +1 -1
  24. package/dist/types/sdk/market/oracle/PriceOracleV310Contract.d.ts +2 -2
  25. package/dist/types/sdk/market/oracle/createPriceOracle.d.ts +14 -3
  26. package/dist/types/sdk/market/oracle/types.d.ts +97 -6
  27. package/dist/types/sdk/plugins/V300StalenessPeriodPlugin.d.ts +3 -2
  28. package/package.json +1 -1
@@ -30,7 +30,11 @@ async function calcLiquidatableLTs(sdk, ca, factor = 9990n, logger) {
30
30
  return balance > minBalance;
31
31
  }).map((t) => {
32
32
  const { token, balance } = t;
33
- const balanceU = market.priceOracle.convertToUnderlying(token, balance);
33
+ const balanceU = market.priceOracle.convert(
34
+ token,
35
+ ca.underlying,
36
+ balance
37
+ );
34
38
  const lt = BigInt(cm.creditManager.liquidationThresholds.mustGet(token));
35
39
  return {
36
40
  token,
@@ -75,7 +75,6 @@ class BaseContract extends import_SDKConstruct.SDKConstruct {
75
75
  this.addressLabels.set(this.#address, name);
76
76
  }
77
77
  }
78
- // TODO: unused
79
78
  stateHuman(_ = true) {
80
79
  return {
81
80
  address: this.sdk.provider.addressLabels.get(this.address),
@@ -58,11 +58,7 @@ class MarketSuite extends import_base.SDKConstruct {
58
58
  for (let i = 0; i < marketData.creditManagers.length; i++) {
59
59
  this.creditManagers.push(new import_credit.CreditSuite(sdk, marketData, i));
60
60
  }
61
- this.priceOracle = (0, import_oracle.createPriceOracle)(
62
- sdk,
63
- marketData.priceOracle,
64
- marketData.pool.underlying
65
- );
61
+ this.priceOracle = (0, import_oracle.getOrCreatePriceOracle)(sdk, marketData.priceOracle);
66
62
  }
67
63
  get dirty() {
68
64
  return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
@@ -42,10 +42,6 @@ var import_pricefeeds = require("../pricefeeds/index.js");
42
42
  var import_PriceFeedAnswerMap = __toESM(require("./PriceFeedAnswerMap.js"));
43
43
  const ZERO_PRICE_FEED = (0, import_viem.stringToHex)("PRICE_FEED::ZERO", { size: 32 });
44
44
  class PriceOracleBaseContract extends import_base.BaseContract {
45
- /**
46
- * Underlying token of market to which this price oracle belongs
47
- */
48
- underlying;
49
45
  /**
50
46
  * Mapping Token => [PriceFeed Address, stalenessPeriod]
51
47
  */
@@ -71,12 +67,14 @@ class PriceOracleBaseContract extends import_base.BaseContract {
71
67
  void 0,
72
68
  "reservePrices"
73
69
  );
74
- #priceFeedTree = [];
75
- constructor(sdk, args, data, underlying) {
70
+ #priceFeedTree = new import_utils.AddressMap(
71
+ void 0,
72
+ "priceFeedTree"
73
+ );
74
+ constructor(sdk, args, data) {
76
75
  super(sdk, args);
77
- this.underlying = underlying;
78
76
  const { priceFeedMap, priceFeedTree } = data;
79
- this.#loadState(priceFeedMap, priceFeedTree);
77
+ this.#loadState(priceFeedMap, priceFeedTree, true);
80
78
  }
81
79
  /**
82
80
  * Returns main and reserve price feeds for given tokens
@@ -98,7 +96,7 @@ class PriceOracleBaseContract extends import_base.BaseContract {
98
96
  */
99
97
  async updatePriceFeeds() {
100
98
  const updatables = [];
101
- for (const node of this.#priceFeedTree) {
99
+ for (const node of this.#priceFeedTree.values()) {
102
100
  if (node.updatable) {
103
101
  updatables.push(this.sdk.priceFeeds.mustGet(node.baseParams.addr));
104
102
  }
@@ -164,30 +162,19 @@ class PriceOracleBaseContract extends import_base.BaseContract {
164
162
  }
165
163
  /**
166
164
  * Returns true if oracle's price feed tree contains given price feed
165
+ * This feed is not necessary connected to token, but can be a component of composite feed for some token
167
166
  * @param priceFeed
168
167
  * @returns
169
168
  */
170
169
  usesPriceFeed(priceFeed) {
171
- return this.#priceFeedTree.some(
172
- (node) => node.baseParams.addr.toLowerCase() === priceFeed.toLowerCase()
173
- );
174
- }
175
- /**
176
- * Tries to convert amount of token into underlying of current market
177
- * @param token
178
- * @param amount
179
- * @param reserve
180
- * @returns
181
- */
182
- convertToUnderlying(token, amount, reserve = false) {
183
- return this.convert(token, this.underlying, amount, reserve);
170
+ return this.#priceFeedTree.has(priceFeed);
184
171
  }
185
172
  /**
186
173
  * Tries to convert amount of from one token to another, using latest known prices
187
174
  * @param from
188
175
  * @param to
189
176
  * @param amount
190
- * @param reserve
177
+ * @param reserve use reserve price feed instead of main
191
178
  */
192
179
  convert(from, to, amount, reserve = false) {
193
180
  if (from === to) {
@@ -202,9 +189,8 @@ class PriceOracleBaseContract extends import_base.BaseContract {
202
189
  /**
203
190
  * Tries to convert amount of token to USD, using latest known prices
204
191
  * @param from
205
- * @param to
206
192
  * @param amount
207
- * @param reserve
193
+ * @param reserve use reserve price feed instead of main
208
194
  */
209
195
  convertToUSD(from, amount, reserve = false) {
210
196
  const price = reserve ? this.reservePrice(from) : this.mainPrice(from);
@@ -215,7 +201,7 @@ class PriceOracleBaseContract extends import_base.BaseContract {
215
201
  * Tries to convert amount of USD to token, using latest known prices
216
202
  * @param to
217
203
  * @param amount
218
- * @param reserve
204
+ * @param reserve use reserve price feed instead of main
219
205
  */
220
206
  convertFromUSD(to, amount, reserve = false) {
221
207
  const price = reserve ? this.reservePrice(to) : this.mainPrice(to);
@@ -224,23 +210,26 @@ class PriceOracleBaseContract extends import_base.BaseContract {
224
210
  }
225
211
  /**
226
212
  * Loads new prices for this oracle from PriceFeedCompressor
227
- * Does not update price feeds, only updates prices
213
+ * Will (re)create price feeds if needed
228
214
  */
229
215
  async updatePrices() {
230
216
  await this.sdk.marketRegister.updatePrices([this.address]);
231
217
  }
218
+ /**
219
+ * Paired method to updatePrices, helps to update prices on all oracles in one multicall
220
+ */
232
221
  syncStateMulticall() {
233
- const args = [this.address];
234
- if (this.version === 300) {
235
- args.push(
222
+ let args = [this.address];
223
+ if ((0, import_constants.isV300)(this.version)) {
224
+ args = [
225
+ args[0],
236
226
  Array.from(
237
227
  /* @__PURE__ */ new Set([
238
- this.underlying,
239
228
  ...this.mainPriceFeeds.keys(),
240
229
  ...this.reservePriceFeeds.keys()
241
230
  ])
242
231
  )
243
- );
232
+ ];
244
233
  }
245
234
  const [address] = this.sdk.addressProvider.mustGetLatest(
246
235
  import_constants.AP_PRICE_FEED_COMPRESSOR,
@@ -255,25 +244,39 @@ class PriceOracleBaseContract extends import_base.BaseContract {
255
244
  },
256
245
  onResult: (resp) => {
257
246
  const { priceFeedMap, priceFeedTree } = resp;
258
- this.#loadState(priceFeedMap, priceFeedTree);
247
+ this.#loadState(priceFeedMap, priceFeedTree, true);
259
248
  }
260
249
  };
261
250
  }
262
- #loadState(entries, tree) {
263
- this.#priceFeedTree = tree;
264
- this.mainPriceFeeds.clear();
265
- this.reservePriceFeeds.clear();
266
- this.mainPrices.clear();
267
- this.reservePrices.clear();
251
+ /**
252
+ * Helper function to handle situation when we have multiple different compressor data entries for same oracle
253
+ * This happens in v300
254
+ *
255
+ * @deprecated should be unnecessary after full v310 migration (oracles will be unique)
256
+ * @param data
257
+ * @returns
258
+ */
259
+ merge(data) {
260
+ const { priceFeedMap, priceFeedTree } = data;
261
+ this.#loadState(priceFeedMap, priceFeedTree, false);
262
+ return this;
263
+ }
264
+ #loadState(entries, tree, reset) {
265
+ if (reset) {
266
+ this.#priceFeedTree.clear();
267
+ this.mainPriceFeeds.clear();
268
+ this.reservePriceFeeds.clear();
269
+ this.mainPrices.clear();
270
+ this.reservePrices.clear();
271
+ }
268
272
  for (const node of tree) {
273
+ this.#priceFeedTree.upsert(node.baseParams.addr, node);
269
274
  this.sdk.priceFeeds.getOrCreate(node);
270
275
  }
271
- entries.forEach((entry) => {
276
+ for (const entry of entries) {
272
277
  const { token, priceFeed, reserve, stalenessPeriod } = entry;
273
278
  const ref = new import_pricefeeds.PriceFeedRef(this.sdk, priceFeed, stalenessPeriod);
274
- const node = this.#priceFeedTree.find(
275
- (n) => n.baseParams.addr === priceFeed
276
- );
279
+ const node = this.#priceFeedTree.get(priceFeed);
277
280
  const price = node?.answer?.price;
278
281
  const priceFeedType = node?.baseParams.contractType;
279
282
  if (reserve) {
@@ -290,7 +293,7 @@ class PriceOracleBaseContract extends import_base.BaseContract {
290
293
  }
291
294
  }
292
295
  this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
293
- });
296
+ }
294
297
  this.logger?.debug(
295
298
  `Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
296
299
  );
@@ -311,6 +314,8 @@ class PriceOracleBaseContract extends import_base.BaseContract {
311
314
  * Helper method to find "attachment point" of price feed (makes sense for updatable price feeds only) -
312
315
  * returns token (in v3.0 can be ticker) and main/reserve flag
313
316
  *
317
+ * @deprecated Should be gone after v310 migration
318
+ *
314
319
  * @param priceFeed
315
320
  * @returns
316
321
  */
@@ -327,6 +332,9 @@ class PriceOracleBaseContract extends import_base.BaseContract {
327
332
  }
328
333
  return [void 0, false];
329
334
  }
335
+ /**
336
+ * Returns list of addresses that should be watched for events to sync state
337
+ */
330
338
  get watchAddresses() {
331
339
  return /* @__PURE__ */ new Set([this.address]);
332
340
  }
@@ -353,9 +361,6 @@ class PriceOracleBaseContract extends import_base.BaseContract {
353
361
  )
354
362
  };
355
363
  }
356
- get priceFeedTree() {
357
- return this.#priceFeedTree;
358
- }
359
364
  #noAnswerWarn(priceFeed, node) {
360
365
  let label = this.labelAddress(priceFeed);
361
366
  if (!node) {
@@ -27,7 +27,7 @@ var import_sdk_gov_legacy = require("../../sdk-gov-legacy/index.js");
27
27
  var import_PriceOracleBaseContract = require("./PriceOracleBaseContract.js");
28
28
  const abi = [...import_v300.iPriceOracleV300Abi, ...import_iPausable.iPausableAbi];
29
29
  class PriceOracleV300Contract extends import_PriceOracleBaseContract.PriceOracleBaseContract {
30
- constructor(sdk, data, underlying) {
30
+ constructor(sdk, data) {
31
31
  super(
32
32
  sdk,
33
33
  {
@@ -35,8 +35,7 @@ class PriceOracleV300Contract extends import_PriceOracleBaseContract.PriceOracle
35
35
  name: "PriceOracleV3",
36
36
  abi
37
37
  },
38
- data,
39
- underlying
38
+ data
40
39
  );
41
40
  }
42
41
  processLog(log) {
@@ -25,7 +25,7 @@ var import_v310 = require("../../../abi/v310.js");
25
25
  var import_PriceOracleBaseContract = require("./PriceOracleBaseContract.js");
26
26
  const abi = import_v310.iPriceOracleV310Abi;
27
27
  class PriceOracleV310Contract extends import_PriceOracleBaseContract.PriceOracleBaseContract {
28
- constructor(sdk, data, underlying) {
28
+ constructor(sdk, data) {
29
29
  super(
30
30
  sdk,
31
31
  {
@@ -33,8 +33,7 @@ class PriceOracleV310Contract extends import_PriceOracleBaseContract.PriceOracle
33
33
  name: "PriceOracleV3",
34
34
  abi
35
35
  },
36
- data,
37
- underlying
36
+ data
38
37
  );
39
38
  }
40
39
  processLog(log) {
@@ -18,23 +18,42 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var createPriceOracle_exports = {};
20
20
  __export(createPriceOracle_exports, {
21
- createPriceOracle: () => createPriceOracle
21
+ getOrCreatePriceOracle: () => getOrCreatePriceOracle
22
22
  });
23
23
  module.exports = __toCommonJS(createPriceOracle_exports);
24
24
  var import_constants = require("../../constants/index.js");
25
+ var import_PriceOracleBaseContract = require("./PriceOracleBaseContract.js");
25
26
  var import_PriceOracleV300Contract = require("./PriceOracleV300Contract.js");
26
27
  var import_PriceOracleV310Contract = require("./PriceOracleV310Contract.js");
27
- function createPriceOracle(sdk, data, underlying) {
28
- const v = data.baseParams.version;
29
- if ((0, import_constants.isV300)(v)) {
30
- return new import_PriceOracleV300Contract.PriceOracleV300Contract(sdk, data, underlying);
28
+ function getOrCreatePriceOracle(sdk, data) {
29
+ const { version, addr } = data.baseParams;
30
+ const existing = sdk.contracts.get(addr);
31
+ if (existing) {
32
+ return tryExtendExistingOracle(existing, data);
31
33
  }
32
- if ((0, import_constants.isV310)(v)) {
33
- return new import_PriceOracleV310Contract.PriceOracleV310Contract(sdk, data, underlying);
34
+ if ((0, import_constants.isV300)(version)) {
35
+ return new import_PriceOracleV300Contract.PriceOracleV300Contract(sdk, data);
34
36
  }
35
- throw new Error(`Unsupported oracle version ${v}`);
37
+ if ((0, import_constants.isV310)(version)) {
38
+ return new import_PriceOracleV310Contract.PriceOracleV310Contract(sdk, data);
39
+ }
40
+ throw new Error(`Unsupported oracle version ${version}`);
41
+ }
42
+ function tryExtendExistingOracle(existing, data) {
43
+ const { version, addr } = data.baseParams;
44
+ if (!(existing instanceof import_PriceOracleBaseContract.PriceOracleBaseContract)) {
45
+ throw new Error(
46
+ `expected oracle contract at ${addr}, found existing ${existing.contractType}`
47
+ );
48
+ }
49
+ if (Number(existing.version) !== Number(version)) {
50
+ throw new Error(
51
+ `expected oracle contract at ${addr} to have version ${version}, found ${existing.version}`
52
+ );
53
+ }
54
+ return existing.merge(data);
36
55
  }
37
56
  // Annotate the CommonJS export names for ESM import in node:
38
57
  0 && (module.exports = {
39
- createPriceOracle
58
+ getOrCreatePriceOracle
40
59
  });
@@ -22,6 +22,7 @@ __export(AbstractLPPriceFeed_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(AbstractLPPriceFeed_exports);
24
24
  var import_viem = require("viem");
25
+ var import_versions = require("../../constants/versions.js");
25
26
  var import_AbstractPriceFeed = require("./AbstractPriceFeed.js");
26
27
  const LOWER_BOUND_FACTOR = 99n;
27
28
  class AbstractLPPriceFeedContract extends import_AbstractPriceFeed.AbstractPriceFeedContract {
@@ -36,7 +37,7 @@ class AbstractLPPriceFeedContract extends import_AbstractPriceFeed.AbstractPrice
36
37
  constructor(sdk, args) {
37
38
  super(sdk, { ...args, decimals: 8 });
38
39
  this.hasLowerBoundCap = true;
39
- if (args.baseParams.version === 310n) {
40
+ if ((0, import_versions.isV310)(args.baseParams.version)) {
40
41
  const decoder = (0, import_viem.decodeAbiParameters)(
41
42
  [
42
43
  { type: "address", name: "lpToken" },
@@ -40,7 +40,7 @@ class RedstonePriceFeedContract extends import_AbstractPriceFeed.AbstractPriceFe
40
40
  name: `RedstonePriceFeed`,
41
41
  abi: import_abi.redstonePriceFeedAbi
42
42
  });
43
- if (args.baseParams.version === 310n) {
43
+ if ((0, import_constants.isV310)(args.baseParams.version)) {
44
44
  const decoder = (0, import_viem.decodeAbiParameters)(
45
45
  [
46
46
  { type: "address", name: "token" },
@@ -39,12 +39,12 @@ class V300StalenessPeriodPlugin extends import_base.SDKConstruct {
39
39
  this.#logger = sdk.logger?.child?.({ name: "V300StalenessPeriodPlugin" }) ?? sdk.logger;
40
40
  }
41
41
  async attach() {
42
- await this.#syncReservePriceFeeds();
42
+ await this.#syncPriceFeeds();
43
43
  }
44
44
  async syncState() {
45
- await this.#syncReservePriceFeeds();
45
+ await this.#syncPriceFeeds();
46
46
  }
47
- async #syncReservePriceFeeds() {
47
+ async #syncPriceFeeds() {
48
48
  const oracles = this.#getOraclesMap();
49
49
  const [fromBlock, toBlock] = [this.#syncedTo + 1n, this.sdk.currentBlock];
50
50
  if (oracles.size === 0 || fromBlock > toBlock) {
@@ -67,7 +67,7 @@ class V300StalenessPeriodPlugin extends import_base.SDKConstruct {
67
67
  strict: true
68
68
  });
69
69
  this.#logger?.info(
70
- `loaded ${events.length} SetReservePriceFeed events in range [${fromBlock}; ${toBlock}]`
70
+ `loaded ${events.length} price feed events in range [${fromBlock}; ${toBlock}]`
71
71
  );
72
72
  for (const e of events) {
73
73
  const oracle = oracles.mustGet(e.address);
@@ -120,7 +120,7 @@ class V300StalenessPeriodPlugin extends import_base.SDKConstruct {
120
120
  }
121
121
  #getOraclesMap() {
122
122
  return new import_utils.AddressMap(
123
- this.sdk.marketRegister.markets.filter((m) => m.priceOracle.version === 300).map((m) => [m.priceOracle.address, m.priceOracle])
123
+ this.sdk.marketRegister.markets.filter((m) => (0, import_constants.isV300)(m.priceOracle.version)).map((m) => [m.priceOracle.address, m.priceOracle])
124
124
  );
125
125
  }
126
126
  }
@@ -7,7 +7,11 @@ async function calcLiquidatableLTs(sdk, ca, factor = 9990n, logger) {
7
7
  return balance > minBalance;
8
8
  }).map((t) => {
9
9
  const { token, balance } = t;
10
- const balanceU = market.priceOracle.convertToUnderlying(token, balance);
10
+ const balanceU = market.priceOracle.convert(
11
+ token,
12
+ ca.underlying,
13
+ balance
14
+ );
11
15
  const lt = BigInt(cm.creditManager.liquidationThresholds.mustGet(token));
12
16
  return {
13
17
  token,
@@ -57,7 +57,6 @@ class BaseContract extends SDKConstruct {
57
57
  this.addressLabels.set(this.#address, name);
58
58
  }
59
59
  }
60
- // TODO: unused
61
60
  stateHuman(_ = true) {
62
61
  return {
63
62
  address: this.sdk.provider.addressLabels.get(this.address),
@@ -1,7 +1,7 @@
1
1
  import { SDKConstruct } from "../base/index.js";
2
2
  import { CreditSuite } from "./credit/index.js";
3
3
  import { MarketConfiguratorContract } from "./MarketConfiguratorContract.js";
4
- import { createPriceOracle } from "./oracle/index.js";
4
+ import { getOrCreatePriceOracle } from "./oracle/index.js";
5
5
  import { PoolSuite } from "./pool/index.js";
6
6
  class MarketSuite extends SDKConstruct {
7
7
  acl;
@@ -35,11 +35,7 @@ class MarketSuite extends SDKConstruct {
35
35
  for (let i = 0; i < marketData.creditManagers.length; i++) {
36
36
  this.creditManagers.push(new CreditSuite(sdk, marketData, i));
37
37
  }
38
- this.priceOracle = createPriceOracle(
39
- sdk,
40
- marketData.priceOracle,
41
- marketData.pool.underlying
42
- );
38
+ this.priceOracle = getOrCreatePriceOracle(sdk, marketData.priceOracle);
43
39
  }
44
40
  get dirty() {
45
41
  return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
@@ -5,6 +5,7 @@ import { iUpdatablePriceFeedAbi } from "../../../abi/iUpdatablePriceFeed.js";
5
5
  import { BaseContract } from "../../base/index.js";
6
6
  import {
7
7
  AP_PRICE_FEED_COMPRESSOR,
8
+ isV300,
8
9
  VERSION_RANGE_310
9
10
  } from "../../constants/index.js";
10
11
  import { AddressMap, formatBN } from "../../utils/index.js";
@@ -12,10 +13,6 @@ import { PriceFeedRef } from "../pricefeeds/index.js";
12
13
  import PriceFeedAnswerMap from "./PriceFeedAnswerMap.js";
13
14
  const ZERO_PRICE_FEED = stringToHex("PRICE_FEED::ZERO", { size: 32 });
14
15
  class PriceOracleBaseContract extends BaseContract {
15
- /**
16
- * Underlying token of market to which this price oracle belongs
17
- */
18
- underlying;
19
16
  /**
20
17
  * Mapping Token => [PriceFeed Address, stalenessPeriod]
21
18
  */
@@ -41,12 +38,14 @@ class PriceOracleBaseContract extends BaseContract {
41
38
  void 0,
42
39
  "reservePrices"
43
40
  );
44
- #priceFeedTree = [];
45
- constructor(sdk, args, data, underlying) {
41
+ #priceFeedTree = new AddressMap(
42
+ void 0,
43
+ "priceFeedTree"
44
+ );
45
+ constructor(sdk, args, data) {
46
46
  super(sdk, args);
47
- this.underlying = underlying;
48
47
  const { priceFeedMap, priceFeedTree } = data;
49
- this.#loadState(priceFeedMap, priceFeedTree);
48
+ this.#loadState(priceFeedMap, priceFeedTree, true);
50
49
  }
51
50
  /**
52
51
  * Returns main and reserve price feeds for given tokens
@@ -68,7 +67,7 @@ class PriceOracleBaseContract extends BaseContract {
68
67
  */
69
68
  async updatePriceFeeds() {
70
69
  const updatables = [];
71
- for (const node of this.#priceFeedTree) {
70
+ for (const node of this.#priceFeedTree.values()) {
72
71
  if (node.updatable) {
73
72
  updatables.push(this.sdk.priceFeeds.mustGet(node.baseParams.addr));
74
73
  }
@@ -134,30 +133,19 @@ class PriceOracleBaseContract extends BaseContract {
134
133
  }
135
134
  /**
136
135
  * Returns true if oracle's price feed tree contains given price feed
136
+ * This feed is not necessary connected to token, but can be a component of composite feed for some token
137
137
  * @param priceFeed
138
138
  * @returns
139
139
  */
140
140
  usesPriceFeed(priceFeed) {
141
- return this.#priceFeedTree.some(
142
- (node) => node.baseParams.addr.toLowerCase() === priceFeed.toLowerCase()
143
- );
144
- }
145
- /**
146
- * Tries to convert amount of token into underlying of current market
147
- * @param token
148
- * @param amount
149
- * @param reserve
150
- * @returns
151
- */
152
- convertToUnderlying(token, amount, reserve = false) {
153
- return this.convert(token, this.underlying, amount, reserve);
141
+ return this.#priceFeedTree.has(priceFeed);
154
142
  }
155
143
  /**
156
144
  * Tries to convert amount of from one token to another, using latest known prices
157
145
  * @param from
158
146
  * @param to
159
147
  * @param amount
160
- * @param reserve
148
+ * @param reserve use reserve price feed instead of main
161
149
  */
162
150
  convert(from, to, amount, reserve = false) {
163
151
  if (from === to) {
@@ -172,9 +160,8 @@ class PriceOracleBaseContract extends BaseContract {
172
160
  /**
173
161
  * Tries to convert amount of token to USD, using latest known prices
174
162
  * @param from
175
- * @param to
176
163
  * @param amount
177
- * @param reserve
164
+ * @param reserve use reserve price feed instead of main
178
165
  */
179
166
  convertToUSD(from, amount, reserve = false) {
180
167
  const price = reserve ? this.reservePrice(from) : this.mainPrice(from);
@@ -185,7 +172,7 @@ class PriceOracleBaseContract extends BaseContract {
185
172
  * Tries to convert amount of USD to token, using latest known prices
186
173
  * @param to
187
174
  * @param amount
188
- * @param reserve
175
+ * @param reserve use reserve price feed instead of main
189
176
  */
190
177
  convertFromUSD(to, amount, reserve = false) {
191
178
  const price = reserve ? this.reservePrice(to) : this.mainPrice(to);
@@ -194,23 +181,26 @@ class PriceOracleBaseContract extends BaseContract {
194
181
  }
195
182
  /**
196
183
  * Loads new prices for this oracle from PriceFeedCompressor
197
- * Does not update price feeds, only updates prices
184
+ * Will (re)create price feeds if needed
198
185
  */
199
186
  async updatePrices() {
200
187
  await this.sdk.marketRegister.updatePrices([this.address]);
201
188
  }
189
+ /**
190
+ * Paired method to updatePrices, helps to update prices on all oracles in one multicall
191
+ */
202
192
  syncStateMulticall() {
203
- const args = [this.address];
204
- if (this.version === 300) {
205
- args.push(
193
+ let args = [this.address];
194
+ if (isV300(this.version)) {
195
+ args = [
196
+ args[0],
206
197
  Array.from(
207
198
  /* @__PURE__ */ new Set([
208
- this.underlying,
209
199
  ...this.mainPriceFeeds.keys(),
210
200
  ...this.reservePriceFeeds.keys()
211
201
  ])
212
202
  )
213
- );
203
+ ];
214
204
  }
215
205
  const [address] = this.sdk.addressProvider.mustGetLatest(
216
206
  AP_PRICE_FEED_COMPRESSOR,
@@ -225,25 +215,39 @@ class PriceOracleBaseContract extends BaseContract {
225
215
  },
226
216
  onResult: (resp) => {
227
217
  const { priceFeedMap, priceFeedTree } = resp;
228
- this.#loadState(priceFeedMap, priceFeedTree);
218
+ this.#loadState(priceFeedMap, priceFeedTree, true);
229
219
  }
230
220
  };
231
221
  }
232
- #loadState(entries, tree) {
233
- this.#priceFeedTree = tree;
234
- this.mainPriceFeeds.clear();
235
- this.reservePriceFeeds.clear();
236
- this.mainPrices.clear();
237
- this.reservePrices.clear();
222
+ /**
223
+ * Helper function to handle situation when we have multiple different compressor data entries for same oracle
224
+ * This happens in v300
225
+ *
226
+ * @deprecated should be unnecessary after full v310 migration (oracles will be unique)
227
+ * @param data
228
+ * @returns
229
+ */
230
+ merge(data) {
231
+ const { priceFeedMap, priceFeedTree } = data;
232
+ this.#loadState(priceFeedMap, priceFeedTree, false);
233
+ return this;
234
+ }
235
+ #loadState(entries, tree, reset) {
236
+ if (reset) {
237
+ this.#priceFeedTree.clear();
238
+ this.mainPriceFeeds.clear();
239
+ this.reservePriceFeeds.clear();
240
+ this.mainPrices.clear();
241
+ this.reservePrices.clear();
242
+ }
238
243
  for (const node of tree) {
244
+ this.#priceFeedTree.upsert(node.baseParams.addr, node);
239
245
  this.sdk.priceFeeds.getOrCreate(node);
240
246
  }
241
- entries.forEach((entry) => {
247
+ for (const entry of entries) {
242
248
  const { token, priceFeed, reserve, stalenessPeriod } = entry;
243
249
  const ref = new PriceFeedRef(this.sdk, priceFeed, stalenessPeriod);
244
- const node = this.#priceFeedTree.find(
245
- (n) => n.baseParams.addr === priceFeed
246
- );
250
+ const node = this.#priceFeedTree.get(priceFeed);
247
251
  const price = node?.answer?.price;
248
252
  const priceFeedType = node?.baseParams.contractType;
249
253
  if (reserve) {
@@ -260,7 +264,7 @@ class PriceOracleBaseContract extends BaseContract {
260
264
  }
261
265
  }
262
266
  this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
263
- });
267
+ }
264
268
  this.logger?.debug(
265
269
  `Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
266
270
  );
@@ -281,6 +285,8 @@ class PriceOracleBaseContract extends BaseContract {
281
285
  * Helper method to find "attachment point" of price feed (makes sense for updatable price feeds only) -
282
286
  * returns token (in v3.0 can be ticker) and main/reserve flag
283
287
  *
288
+ * @deprecated Should be gone after v310 migration
289
+ *
284
290
  * @param priceFeed
285
291
  * @returns
286
292
  */
@@ -297,6 +303,9 @@ class PriceOracleBaseContract extends BaseContract {
297
303
  }
298
304
  return [void 0, false];
299
305
  }
306
+ /**
307
+ * Returns list of addresses that should be watched for events to sync state
308
+ */
300
309
  get watchAddresses() {
301
310
  return /* @__PURE__ */ new Set([this.address]);
302
311
  }
@@ -323,9 +332,6 @@ class PriceOracleBaseContract extends BaseContract {
323
332
  )
324
333
  };
325
334
  }
326
- get priceFeedTree() {
327
- return this.#priceFeedTree;
328
- }
329
335
  #noAnswerWarn(priceFeed, node) {
330
336
  let label = this.labelAddress(priceFeed);
331
337
  if (!node) {