@gearbox-protocol/sdk 18.2.0-next.12 → 18.2.0-next.14

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 (56) hide show
  1. package/dist/cjs/abi/310/iKillSwitchProxy.js +130 -0
  2. package/dist/cjs/permissionless/api/index.js +2 -1
  3. package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +40 -5
  4. package/dist/cjs/permissionless/api/oracles/index.js +2 -1
  5. package/dist/cjs/permissionless/api/oracles/score/formula.js +19 -24
  6. package/dist/cjs/permissionless/api/oracles/score/index.js +1 -1
  7. package/dist/cjs/permissionless/api/oracles/score/run.js +186 -105
  8. package/dist/cjs/permissionless/api/oracles/stress/index.js +1 -0
  9. package/dist/cjs/permissionless/api/oracles/stress/run.js +63 -13
  10. package/dist/cjs/permissionless/bindings/bytecode-repository.js +8 -0
  11. package/dist/cjs/permissionless/bindings/governance/governor.js +1 -1
  12. package/dist/cjs/permissionless/bindings/index.js +7 -2
  13. package/dist/cjs/permissionless/bindings/kill-switch-proxy.js +188 -0
  14. package/dist/cjs/permissionless/bindings/market-configurator-factory.js +39 -1
  15. package/dist/cjs/permissionless/bindings/market-configurator.js +142 -0
  16. package/dist/cjs/permissionless/index.js +10 -3
  17. package/dist/cjs/permissionless/utils/index.js +1 -0
  18. package/dist/cjs/permissionless/utils/literals.js +2 -0
  19. package/dist/esm/abi/310/iKillSwitchProxy.js +129 -0
  20. package/dist/esm/permissionless/api/index.js +4 -4
  21. package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +41 -6
  22. package/dist/esm/permissionless/api/oracles/index.js +4 -4
  23. package/dist/esm/permissionless/api/oracles/score/formula.js +20 -24
  24. package/dist/esm/permissionless/api/oracles/score/index.js +3 -3
  25. package/dist/esm/permissionless/api/oracles/score/run.js +187 -107
  26. package/dist/esm/permissionless/api/oracles/stress/index.js +2 -2
  27. package/dist/esm/permissionless/api/oracles/stress/run.js +64 -15
  28. package/dist/esm/permissionless/bindings/bytecode-repository.js +8 -0
  29. package/dist/esm/permissionless/bindings/governance/governor.js +1 -1
  30. package/dist/esm/permissionless/bindings/index.js +4 -3
  31. package/dist/esm/permissionless/bindings/kill-switch-proxy.js +184 -0
  32. package/dist/esm/permissionless/bindings/market-configurator-factory.js +37 -0
  33. package/dist/esm/permissionless/bindings/market-configurator.js +143 -1
  34. package/dist/esm/permissionless/index.js +8 -7
  35. package/dist/esm/permissionless/utils/index.js +2 -2
  36. package/dist/esm/permissionless/utils/literals.js +2 -1
  37. package/dist/types/abi/310/iKillSwitchProxy.d.ts +114 -0
  38. package/dist/types/permissionless/api/index.d.ts +5 -5
  39. package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +30 -6
  40. package/dist/types/permissionless/api/oracles/index.d.ts +5 -5
  41. package/dist/types/permissionless/api/oracles/score/formula.d.ts +14 -17
  42. package/dist/types/permissionless/api/oracles/score/index.d.ts +4 -4
  43. package/dist/types/permissionless/api/oracles/score/run.d.ts +28 -10
  44. package/dist/types/permissionless/api/oracles/score/types.d.ts +33 -28
  45. package/dist/types/permissionless/api/oracles/stress/index.d.ts +2 -2
  46. package/dist/types/permissionless/api/oracles/stress/run.d.ts +11 -0
  47. package/dist/types/permissionless/bindings/bytecode-repository.d.ts +10 -1
  48. package/dist/types/permissionless/bindings/index.d.ts +4 -3
  49. package/dist/types/permissionless/bindings/kill-switch-proxy.d.ts +205 -0
  50. package/dist/types/permissionless/bindings/market-configurator-factory.d.ts +30 -0
  51. package/dist/types/permissionless/bindings/market-configurator.d.ts +30 -1
  52. package/dist/types/permissionless/bindings/types.d.ts +27 -0
  53. package/dist/types/permissionless/index.d.ts +9 -8
  54. package/dist/types/permissionless/utils/index.d.ts +2 -2
  55. package/dist/types/permissionless/utils/literals.d.ts +1 -0
  56. package/package.json +1 -1
@@ -7,58 +7,67 @@ let viem = require("viem");
7
7
  const ZERO_TYPE = "PRICE_FEED::ZERO";
8
8
  /** Groups simulated at once: one `eth_call` each, all against one node. */
9
9
  const CONCURRENCY = 6;
10
- const feedOf = (slot) => slot.priceFeed.toLowerCase();
10
+ const lower = (address) => address.toLowerCase();
11
11
  /**
12
- * Every asset of one chain, scored by what each of its source groups can do
13
- * to the price its running markets hold for it.
12
+ * A main feed and the reserve a slot or a caller names for it, as the score
13
+ * takes them: a reserve that is a `ZeroPriceFeed` is no reserve.
14
+ */
15
+ function pairOf(main, reserve, nodes) {
16
+ const notes = [];
17
+ if (!reserve) {
18
+ notes.push("no reserve feed");
19
+ return {
20
+ main,
21
+ reserve: void 0,
22
+ notes
23
+ };
24
+ }
25
+ if (nodes.get(reserve)?.contractType === ZERO_TYPE) {
26
+ notes.push("reserve is a ZeroPriceFeed");
27
+ return {
28
+ main,
29
+ reserve: void 0,
30
+ notes
31
+ };
32
+ }
33
+ if (reserve === main) notes.push("reserve is the main feed");
34
+ return {
35
+ main,
36
+ reserve,
37
+ notes
38
+ };
39
+ }
40
+ /**
41
+ * Every pair's final price under each source group lying, from one read of
42
+ * the LP feeds' rate contracts, one of every feed as it stands, and then one
43
+ * `eth_call` per source group with the group's sources overridden to answer
44
+ * {@link SCORE_MULTIPLIER} times what they answer now.
14
45
  *
15
- * One read of the oracles' feed trees, one of the LP feeds' rate contracts,
16
- * one of every market feed as it stands — then one `eth_call` per source
17
- * group, with the group's sources overridden to answer
18
- * {@link SCORE_MULTIPLIER} times what they answer now and every market feed
19
- * read under it.
20
- **/
21
- async function runAssetScores({ client, chainId, slots, compressor, logger }) {
22
- const running = slots.filter((slot) => !slot.isShutdown);
23
- const oracles = [...new Set(running.map((slot) => slot.priceOracle.toLowerCase()))];
24
- const { nodes, answers, unreadableOracles } = await require_permissionless_api_oracles_stress_run.readForest(client, compressor, oracles);
25
- for (const oracle of unreadableOracles) logger?.warn(`price oracle ${oracle} of chain ${chainId} would not report its feed tree; the markets on it are not scored`);
46
+ * A pair whose final price does not answer as it stands comes back `null`:
47
+ * there is no price to move.
48
+ */
49
+ async function simulatePairs({ client, chainId, nodes, answers, pairs, logger }) {
26
50
  const { targets, baselines: rateBaselines } = await require_permissionless_api_oracles_stress_run.readRateTargets(client, [...nodes.values()].filter((node) => require_permissionless_api_oracles_stress_run.RATE_FEED_TYPE.test(node.contractType)).map((node) => node.address));
27
51
  const { sources, unresolved } = require_permissionless_api_oracles_stress_classify.classifySources(chainId, [...nodes.values()], targets, (target) => rateBaselines.get(target) ?? answers.get(target) ?? null);
28
52
  for (const node of unresolved) logger?.warn(`feed ${node.address} of chain ${chainId} would not say what it reads its rate from; its protocol is not perturbed`);
29
- const slotsByPosition = /* @__PURE__ */ new Map();
30
- for (const slot of running) {
31
- const key = `${slot.market.toLowerCase()}:${slot.token.toLowerCase()}`;
32
- const entry = slotsByPosition.get(key) ?? {};
33
- entry[slot.reserve ? "reserve" : "main"] = slot;
34
- slotsByPosition.set(key, entry);
35
- }
36
- const positions = [];
37
- for (const { main, reserve } of slotsByPosition.values()) {
38
- if (!main || main.isUnderlying) continue;
39
- const mainNode = nodes.get(feedOf(main));
40
- if (!mainNode || mainNode.contractType === ZERO_TYPE) continue;
41
- const notes = [];
42
- let reserveFeed;
43
- if (!reserve) notes.push("no reserve feed");
44
- else if (nodes.get(feedOf(reserve))?.contractType === ZERO_TYPE) notes.push("reserve is a ZeroPriceFeed");
45
- else {
46
- reserveFeed = feedOf(reserve);
47
- if (reserveFeed === feedOf(main)) notes.push("reserve is the main feed");
53
+ const roots = [...new Set(pairs.flatMap(({ main, reserve }) => reserve ? [main, reserve] : [main]))];
54
+ const reach = require_permissionless_api_oracles_stress_classify.sourcesByFeed(roots, nodes, sources);
55
+ const sourceByTarget = new Map(sources.map((source) => [source.target, source]));
56
+ const drivable = (source) => source !== void 0 && source.perturbable && source.baseline !== null;
57
+ const groupsOf = (feed) => {
58
+ const out = /* @__PURE__ */ new Map();
59
+ for (const target of feed ? reach.get(feed) ?? [] : []) {
60
+ const source = sourceByTarget.get(target);
61
+ if (drivable(source)) out.set(source.group, [...out.get(source.group) ?? [], source]);
48
62
  }
49
- positions.push({
50
- main,
51
- reserve,
52
- reserveFeed,
53
- notes
54
- });
55
- }
56
- const priced = [...new Set(positions.flatMap(({ main, reserveFeed }) => reserveFeed ? [feedOf(main), reserveFeed] : [feedOf(main)]))];
57
- if (priced.length === 0) return [];
58
- const underlyingOf = (slot) => slot.underlyingPriceFeed?.toLowerCase();
59
- const roots = [.../* @__PURE__ */ new Set([...priced, ...positions.map(({ main }) => underlyingOf(main)).filter((feed) => feed !== void 0)])];
63
+ return out;
64
+ };
65
+ const sourcesOf = (feed) => [...groupsOf(feed).keys()];
66
+ if (roots.length === 0) return {
67
+ results: [],
68
+ sourcesOf
69
+ };
60
70
  const updatedAt = BigInt(Math.floor(Date.now() / 1e3));
61
- const drivable = (source) => source !== void 0 && source.perturbable && source.baseline !== null;
62
71
  const fresh = sources.filter((source) => drivable(source) && source.kind === "feed");
63
72
  const asIs = await require_permissionless_api_oracles_stress_run.readUnder(client, roots, [], 1, updatedAt);
64
73
  const stale = roots.filter((root) => (asIs.get(root) ?? null) === null);
@@ -68,72 +77,105 @@ async function runAssetScores({ client, chainId, slots, compressor, logger }) {
68
77
  const freshRoots = stale.filter((root) => (freshened.get(root) ?? null) !== null);
69
78
  /** Every root under a lie, each read the way its base was. */
70
79
  const readLying = async (members) => {
71
- const [plain, refreshed] = await Promise.all([asIsRoots.length > 0 ? require_permissionless_api_oracles_stress_run.readUnder(client, asIsRoots, members, 5, updatedAt) : /* @__PURE__ */ new Map(), freshRoots.length > 0 ? require_permissionless_api_oracles_stress_run.readUnder(client, freshRoots, members, 5, updatedAt, fresh) : /* @__PURE__ */ new Map()]);
80
+ const [plain, refreshed] = await Promise.all([asIsRoots.length > 0 ? require_permissionless_api_oracles_stress_run.readUnder(client, asIsRoots, members, 10, updatedAt) : /* @__PURE__ */ new Map(), freshRoots.length > 0 ? require_permissionless_api_oracles_stress_run.readUnder(client, freshRoots, members, 10, updatedAt, fresh) : /* @__PURE__ */ new Map()]);
72
81
  return new Map([...plain, ...refreshed]);
73
82
  };
74
- for (const position of positions) if (position.reserveFeed && (base.get(position.reserveFeed) ?? null) === null) {
75
- position.reserveFeed = void 0;
76
- position.notes.push("reserve does not answer");
77
- }
78
- const reach = require_permissionless_api_oracles_stress_classify.sourcesByFeed(roots, nodes, sources);
79
- const sourceByTarget = new Map(sources.map((source) => [source.target, source]));
83
+ const effective = pairs.map((pair) => pair.reserve && (base.get(pair.reserve) ?? null) === null ? {
84
+ ...pair,
85
+ reserve: void 0,
86
+ notes: [...pair.notes, "reserve does not answer"]
87
+ } : pair);
80
88
  const byGroup = /* @__PURE__ */ new Map();
81
89
  for (const source of sources.filter(drivable)) byGroup.set(source.group, [...byGroup.get(source.group) ?? [], source]);
82
90
  const reachedGroups = /* @__PURE__ */ new Set();
83
- for (const root of priced) for (const target of reach.get(root) ?? []) {
84
- const source = sourceByTarget.get(target);
85
- if (drivable(source)) reachedGroups.add(source.group);
86
- }
91
+ for (const root of roots) for (const group of groupsOf(root).keys()) reachedGroups.add(group);
87
92
  const stressed = /* @__PURE__ */ new Map();
88
93
  const queue = [...reachedGroups];
89
94
  for (let i = 0; i < queue.length; i += CONCURRENCY) await Promise.all(queue.slice(i, i + CONCURRENCY).map(async (group) => {
90
95
  stressed.set(group, await readLying(byGroup.get(group) ?? []));
91
96
  }));
92
- const groupsOf = (feed) => {
93
- const out = /* @__PURE__ */ new Map();
94
- for (const target of feed ? reach.get(feed) ?? [] : []) {
95
- const source = sourceByTarget.get(target);
96
- if (drivable(source)) out.set(source.group, [...out.get(source.group) ?? [], source]);
97
- }
98
- return out;
99
- };
100
- const byAsset = /* @__PURE__ */ new Map();
101
- for (const { main, reserve, reserveFeed, notes } of positions) {
102
- const mainFeed = feedOf(main);
103
- const before = require_permissionless_api_oracles_score_formula.finalPrice(base.get(mainFeed) ?? null, reserveFeed ? base.get(reserveFeed) ?? null : void 0);
104
- if (before === null || before <= 0n) {
105
- logger?.warn(`feed ${mainFeed} of chain ${chainId} does not answer; ${main.tokenSymbol ?? main.token} in ${main.marketName ?? main.market} is not scored`);
106
- continue;
107
- }
108
- const onMain = groupsOf(mainFeed);
109
- const onReserve = groupsOf(reserveFeed);
110
- const impacts = [.../* @__PURE__ */ new Set([...onMain.keys(), ...onReserve.keys()])].map((group) => {
111
- const under = stressed.get(group) ?? /* @__PURE__ */ new Map();
112
- const after = require_permissionless_api_oracles_score_formula.finalPrice(under.get(mainFeed) ?? null, reserveFeed ? under.get(reserveFeed) ?? null : void 0);
113
- const factor = require_permissionless_api_oracles_score_formula.factorOf(before, after);
114
- const underlying = underlyingOf(main);
115
- const debtBefore = underlying ? base.get(underlying) ?? null : null;
116
- const debtFactor = debtBefore === null || debtBefore <= 0n || !underlying ? null : require_permissionless_api_oracles_score_formula.factorOf(debtBefore, under.get(underlying) ?? null);
117
- const ratioFactor = factor === null || debtFactor === null || debtFactor === 0 ? null : factor / debtFactor;
118
- const members = [...onMain.get(group) ?? [], ...onReserve.get(group) ?? []];
97
+ return {
98
+ results: effective.map(({ main, reserve, notes }) => {
99
+ const before = require_permissionless_api_oracles_score_formula.finalPrice(base.get(main) ?? null, reserve ? base.get(reserve) ?? null : void 0);
100
+ if (before === null || before <= 0n) return null;
101
+ const onMain = groupsOf(main);
102
+ const onReserve = groupsOf(reserve);
103
+ const impacts = [.../* @__PURE__ */ new Set([...onMain.keys(), ...onReserve.keys()])].map((group) => {
104
+ const under = stressed.get(group) ?? /* @__PURE__ */ new Map();
105
+ const after = require_permissionless_api_oracles_score_formula.finalPrice(under.get(main) ?? null, reserve ? under.get(reserve) ?? null : void 0);
106
+ const factor = require_permissionless_api_oracles_score_formula.factorOf(before, after);
107
+ const members = [...onMain.get(group) ?? [], ...onReserve.get(group) ?? []];
108
+ return {
109
+ group,
110
+ identified: members.every((source) => source.identified),
111
+ feeds: [...new Set(members.map((source) => source.description))],
112
+ reaches: onMain.has(group) ? onReserve.has(group) ? "both" : "main" : "reserve",
113
+ factor,
114
+ score: require_permissionless_api_oracles_score_formula.scoreOfFactor(factor)
115
+ };
116
+ });
119
117
  return {
120
- group,
121
- identified: members.every((source) => source.identified),
122
- feeds: [...new Set(members.map((source) => source.description))],
123
- reaches: onMain.has(group) ? onReserve.has(group) ? "both" : "main" : "reserve",
124
- factor,
125
- ratioFactor,
126
- score: require_permissionless_api_oracles_score_formula.scoreOfFactor(factor)
118
+ hasReserve: reserve !== void 0,
119
+ price: Number((0, viem.formatUnits)(before, 8)),
120
+ unverified: reserve !== void 0 && impacts.some((impact) => !impact.identified && impact.reaches !== "both"),
121
+ notes,
122
+ ...require_permissionless_api_oracles_score_formula.worstOf(impacts)
127
123
  };
124
+ }),
125
+ sourcesOf
126
+ };
127
+ }
128
+ /**
129
+ * Every asset of one chain, scored by what each of its source groups can do
130
+ * to the price its running markets hold for it.
131
+ *
132
+ * One read of the oracles' feed trees, then the simulation of every market's
133
+ * main and reserve pair at once — see {@link simulatePairs}.
134
+ **/
135
+ async function runAssetScores({ client, chainId, slots, compressor, logger }) {
136
+ const running = slots.filter((slot) => !slot.isShutdown);
137
+ const oracles = [...new Set(running.map((slot) => lower(slot.priceOracle)))];
138
+ const { nodes, answers, unreadableOracles } = await require_permissionless_api_oracles_stress_run.readForest(client, compressor, oracles);
139
+ for (const oracle of unreadableOracles) logger?.warn(`price oracle ${oracle} of chain ${chainId} would not report its feed tree; the markets on it are not scored`);
140
+ const slotsByPosition = /* @__PURE__ */ new Map();
141
+ for (const slot of running) {
142
+ const key = `${lower(slot.market)}:${lower(slot.token)}`;
143
+ const entry = slotsByPosition.get(key) ?? {};
144
+ entry[slot.reserve ? "reserve" : "main"] = slot;
145
+ slotsByPosition.set(key, entry);
146
+ }
147
+ const positions = [];
148
+ for (const { main, reserve } of slotsByPosition.values()) {
149
+ if (!main) continue;
150
+ const mainNode = nodes.get(lower(main.priceFeed));
151
+ if (!mainNode || mainNode.contractType === ZERO_TYPE) continue;
152
+ positions.push({
153
+ main,
154
+ reserve
128
155
  });
129
- const unverified = reserveFeed !== void 0 && impacts.some((impact) => !impact.identified && impact.reaches !== "both");
130
- const side = (slot) => ({
131
- feed: feedOf(slot),
132
- name: slot.priceFeedName,
133
- contractType: nodes.get(feedOf(slot))?.contractType ?? "",
134
- sources: [...groupsOf(feedOf(slot)).keys()]
135
- });
136
- const asset = main.token.toLowerCase();
156
+ }
157
+ const { results, sourcesOf } = await simulatePairs({
158
+ client,
159
+ chainId,
160
+ nodes,
161
+ answers,
162
+ pairs: positions.map(({ main, reserve }) => pairOf(lower(main.priceFeed), reserve ? lower(reserve.priceFeed) : void 0, nodes)),
163
+ logger
164
+ });
165
+ const side = (slot) => ({
166
+ feed: lower(slot.priceFeed),
167
+ name: slot.priceFeedName,
168
+ contractType: nodes.get(lower(slot.priceFeed))?.contractType ?? "",
169
+ sources: sourcesOf(lower(slot.priceFeed))
170
+ });
171
+ const byAsset = /* @__PURE__ */ new Map();
172
+ positions.forEach(({ main, reserve }, i) => {
173
+ const result = results[i];
174
+ if (!result) {
175
+ logger?.warn(`feed ${lower(main.priceFeed)} of chain ${chainId} does not answer; ${main.tokenSymbol ?? main.token} in ${main.marketName ?? main.market} is not scored`);
176
+ return;
177
+ }
178
+ const asset = lower(main.token);
137
179
  const entry = byAsset.get(asset) ?? {
138
180
  symbol: main.tokenSymbol,
139
181
  markets: []
@@ -146,15 +188,11 @@ async function runAssetScores({ client, chainId, slots, compressor, logger }) {
146
188
  curatorName: main.curatorName,
147
189
  main: side(main),
148
190
  reserve: reserve ? side(reserve) : null,
149
- hasReserve: reserveFeed !== void 0,
150
- price: Number((0, viem.formatUnits)(before, 8)),
151
- unverified,
152
- notes,
153
- ...require_permissionless_api_oracles_score_formula.worstOf(impacts)
191
+ ...result
154
192
  });
155
- }
193
+ });
156
194
  return [...byAsset].map(([asset, { symbol, markets }]) => {
157
- const sorted = markets.sort((a, b) => a.score - b.score);
195
+ const sorted = markets.sort((a, b) => b.score - a.score);
158
196
  const worst = sorted[0] ?? null;
159
197
  return {
160
198
  chainId,
@@ -167,5 +205,48 @@ async function runAssetScores({ client, chainId, slots, compressor, logger }) {
167
205
  };
168
206
  });
169
207
  }
208
+ /**
209
+ * Any main feed and reserve on a chain, scored the way a market holding them
210
+ * would be — whether or not any market does yet.
211
+ *
212
+ * The two trees are read off the compressor by feed rather than by oracle,
213
+ * so a feed the curator has only just deployed scores as well as one in use.
214
+ *
215
+ * @throws Where there is nothing to score: the compressor cannot read a
216
+ * feed, the main feed is a `ZeroPriceFeed`, or the final price does not
217
+ * answer even with every pull feed under it freshened.
218
+ **/
219
+ async function runFeedPairScore({ client, chainId, compressor, main, reserve, logger }) {
220
+ const mainFeed = lower(main);
221
+ const reserveFeed = reserve ? lower(reserve) : void 0;
222
+ const feeds = reserveFeed && reserveFeed !== mainFeed ? [mainFeed, reserveFeed] : [mainFeed];
223
+ const { nodes, answers } = await require_permissionless_api_oracles_stress_run.readFeedTrees(client, compressor, feeds).catch((cause) => {
224
+ throw new Error(`the PriceFeedCompressor of chain ${chainId} would not read ${feeds.join(" and ")}: not a price feed it knows`, { cause });
225
+ });
226
+ for (const feed of [mainFeed, reserveFeed]) if (feed && !nodes.has(feed)) throw new Error(`feed ${feed} of chain ${chainId} is not one the PriceFeedCompressor can read`);
227
+ if (nodes.get(mainFeed)?.contractType === ZERO_TYPE) throw new Error(`the main feed ${mainFeed} is a ZeroPriceFeed: it prices nothing, so nothing can move`);
228
+ const { results, sourcesOf } = await simulatePairs({
229
+ client,
230
+ chainId,
231
+ nodes,
232
+ answers,
233
+ pairs: [pairOf(mainFeed, reserveFeed, nodes)],
234
+ logger
235
+ });
236
+ const result = results[0];
237
+ if (!result) throw new Error(`the main feed ${mainFeed} of chain ${chainId} does not answer, so there is no price to move`);
238
+ const side = (feed) => ({
239
+ feed,
240
+ name: null,
241
+ contractType: nodes.get(feed)?.contractType ?? "",
242
+ sources: sourcesOf(feed)
243
+ });
244
+ return {
245
+ main: side(mainFeed),
246
+ reserve: reserveFeed ? side(reserveFeed) : null,
247
+ ...result
248
+ };
249
+ }
170
250
  //#endregion
171
251
  exports.runAssetScores = runAssetScores;
252
+ exports.runFeedPairScore = runFeedPairScore;
@@ -12,6 +12,7 @@ exports.classifySources = require_permissionless_api_oracles_stress_classify.cla
12
12
  exports.groupOverride = require_permissionless_api_oracles_stress_override.groupOverride;
13
13
  exports.perturb = require_permissionless_api_oracles_stress_override.perturb;
14
14
  exports.rateStub = require_permissionless_api_oracles_stress_override.rateStub;
15
+ exports.readFeedTrees = require_permissionless_api_oracles_stress_run.readFeedTrees;
15
16
  exports.readForest = require_permissionless_api_oracles_stress_run.readForest;
16
17
  exports.readRateTargets = require_permissionless_api_oracles_stress_run.readRateTargets;
17
18
  exports.readUnder = require_permissionless_api_oracles_stress_run.readUnder;
@@ -72,18 +72,7 @@ async function readForest(client, compressor, oracles) {
72
72
  return;
73
73
  }
74
74
  const { priceFeedTree } = state.result;
75
- for (const node of priceFeedTree) {
76
- const address = node.baseParams.addr.toLowerCase();
77
- if (nodes.has(address)) continue;
78
- nodes.set(address, {
79
- address,
80
- contractType: typeName(node.baseParams.contractType),
81
- version: Number(node.baseParams.version),
82
- description: node.description,
83
- underlyingFeeds: node.underlyingFeeds.map((feed) => feed.toLowerCase())
84
- });
85
- answers.set(address, node.answer.success ? node.answer.price : null);
86
- }
75
+ addTree(priceFeedTree, nodes, answers);
87
76
  });
88
77
  return {
89
78
  nodes,
@@ -92,6 +81,43 @@ async function readForest(client, compressor, oracles) {
92
81
  };
93
82
  }
94
83
  /**
84
+ * The trees under a handful of feeds, flattened, whether or not any oracle
85
+ * holds them.
86
+ *
87
+ * One call for all of them: the compressor walks the feeds it is given the
88
+ * way it walks an oracle's, and answers with the same nodes.
89
+ */
90
+ async function readFeedTrees(client, compressor, feeds) {
91
+ const tree = await client.readContract({
92
+ address: compressor,
93
+ abi: require_abi_compressors_priceFeedCompressor.priceFeedCompressorAbi,
94
+ functionName: "loadPriceFeedTree",
95
+ args: [feeds]
96
+ });
97
+ const nodes = /* @__PURE__ */ new Map();
98
+ const answers = /* @__PURE__ */ new Map();
99
+ addTree(tree, nodes, answers);
100
+ return {
101
+ nodes,
102
+ answers
103
+ };
104
+ }
105
+ /** A tree's nodes into the flattened forest, the first copy of each kept. */
106
+ function addTree(tree, nodes, answers) {
107
+ for (const node of tree) {
108
+ const address = node.baseParams.addr.toLowerCase();
109
+ if (nodes.has(address)) continue;
110
+ nodes.set(address, {
111
+ address,
112
+ contractType: typeName(node.baseParams.contractType),
113
+ version: Number(node.baseParams.version),
114
+ description: node.description,
115
+ underlyingFeeds: node.underlyingFeeds.map((feed) => feed.toLowerCase())
116
+ });
117
+ answers.set(address, node.answer.success ? node.answer.price : null);
118
+ }
119
+ }
120
+ /**
95
121
  * Where each rate-bearing feed reads its rate, and what it will accept.
96
122
  *
97
123
  * The baseline a rate source is perturbed from is the feed's own `lowerBound`
@@ -153,6 +179,18 @@ async function readRateTargets(client, feeds) {
153
179
  bounds
154
180
  };
155
181
  }
182
+ /** Tries after the first before a refused read is given up on. */
183
+ const READ_RETRIES = 3;
184
+ /**
185
+ * A multicall failure that is the node's and not the feed's.
186
+ *
187
+ * A call that reverted inside the multicall is decoded from its own return
188
+ * data and carries no error of the `eth_call` around it; a chunk the node
189
+ * refused carries exactly that, for every call in the chunk.
190
+ */
191
+ function isRefusal(error) {
192
+ return error instanceof viem.BaseError && error.walk((cause) => cause instanceof viem.CallExecutionError) !== null;
193
+ }
156
194
  /**
157
195
  * What every feed answers with one group's sources overridden.
158
196
  *
@@ -165,7 +203,7 @@ async function readUnder(client, feeds, sources, multiplier, updatedAt, freshen
165
203
  const driven = require_permissionless_api_oracles_stress_override.groupOverride(sources, multiplier, updatedAt);
166
204
  const taken = new Set(driven.map((override) => override.address.toLowerCase()));
167
205
  const overrides = [...require_permissionless_api_oracles_stress_override.groupOverride(freshen, 1, updatedAt).filter((override) => !taken.has(override.address.toLowerCase())), ...driven];
168
- const results = await client.multicall({
206
+ const readOnce = () => client.multicall({
169
207
  contracts: feeds.map((address) => ({
170
208
  address,
171
209
  abi: aggregatorAbi,
@@ -174,6 +212,17 @@ async function readUnder(client, feeds, sources, multiplier, updatedAt, freshen
174
212
  allowFailure: true,
175
213
  stateOverride: overrides.length > 0 ? overrides : void 0
176
214
  });
215
+ let results = [];
216
+ for (let attempt = 0;; attempt++) {
217
+ results = await readOnce();
218
+ const refused = results.find((result) => result.status === "failure" && isRefusal(result.error));
219
+ if (!refused) break;
220
+ if (attempt >= READ_RETRIES) {
221
+ const reason = refused.error instanceof viem.BaseError ? refused.error.walk().shortMessage : String(refused.error);
222
+ throw new Error(`the node would not answer a read of ${feeds.length} feeds after 4 tries: ${reason}`, { cause: refused.error });
223
+ }
224
+ await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** attempt));
225
+ }
177
226
  return new Map(feeds.map((feed, i) => {
178
227
  const result = results[i];
179
228
  return [feed, result?.status === "success" ? result.result[1] ?? null : null];
@@ -429,6 +478,7 @@ async function runStressTest({ client, chainId, slots, compressor, scenarios = D
429
478
  //#endregion
430
479
  exports.DEFAULT_SCENARIOS = DEFAULT_SCENARIOS;
431
480
  exports.RATE_FEED_TYPE = RATE_FEED_TYPE;
481
+ exports.readFeedTrees = readFeedTrees;
432
482
  exports.readForest = readForest;
433
483
  exports.readRateTargets = readRateTargets;
434
484
  exports.readUnder = readUnder;
@@ -33,6 +33,14 @@ var BytecodeRepositoryContract = class BytecodeRepositoryContract extends requir
33
33
  async getLatestVersion(type_) {
34
34
  return await this.contract.read.getLatestVersion([(0, viem.stringToHex)(type_, { size: 32 })]);
35
35
  }
36
+ /**
37
+ * Returns the latest allowed patch version of the contract type within given minor version
38
+ * @throws if no bytecode of this contract type and minor version is allowed
39
+ */
40
+ async getLatestPatchVersion(type_, minorVersion) {
41
+ const version = await this.contract.read.getLatestPatchVersion([(0, viem.stringToHex)(type_, { size: 32 }), BigInt(minorVersion)]);
42
+ return Number(version);
43
+ }
36
44
  async getAllowedBytecodeHash(type_, version) {
37
45
  return await this.contract.read.getAllowedBytecodeHash([(0, viem.stringToHex)(type_, { size: 32 }), BigInt(version)]);
38
46
  }
@@ -6,8 +6,8 @@ require("../../../onchain/index.js");
6
6
  const require_permissionless_bindings_bytecode_repository = require("../bytecode-repository.js");
7
7
  require("../../deployment/addresses.js");
8
8
  const require_permissionless_bindings_treasury_splitter = require("../treasury-splitter.js");
9
- const require_abi_governance_governor = require("../../../abi/governance/governor.js");
10
9
  const require_permissionless_bindings_market_configurator = require("../market-configurator.js");
10
+ const require_abi_governance_governor = require("../../../abi/governance/governor.js");
11
11
  const require_permissionless_bindings_governance_batches_chain = require("./batches-chain.js");
12
12
  let viem = require("viem");
13
13
  let viem_utils = require("viem/utils");
@@ -10,10 +10,11 @@ const require_permissionless_bindings_router_routing_manager = require("./router
10
10
  require("./router/index.js");
11
11
  const require_permissionless_bindings_treasury_splitter = require("./treasury-splitter.js");
12
12
  const require_permissionless_bindings_instance_manager = require("./instance-manager.js");
13
- const require_permissionless_bindings_market_configurator_factory = require("./market-configurator-factory.js");
14
- const require_permissionless_bindings_cross_chain_multisig = require("./cross-chain-multisig.js");
15
13
  const require_permissionless_bindings_types = require("./types.js");
16
14
  const require_permissionless_bindings_market_configurator = require("./market-configurator.js");
15
+ const require_permissionless_bindings_kill_switch_proxy = require("./kill-switch-proxy.js");
16
+ const require_permissionless_bindings_market_configurator_factory = require("./market-configurator-factory.js");
17
+ const require_permissionless_bindings_cross_chain_multisig = require("./cross-chain-multisig.js");
17
18
  const require_permissionless_bindings_governance_governor = require("./governance/governor.js");
18
19
  const require_permissionless_bindings_governance_timelock = require("./governance/timelock.js");
19
20
  require("./governance/index.js");
@@ -23,6 +24,9 @@ exports.BytecodeRepositoryContract = require_permissionless_bindings_bytecode_re
23
24
  exports.CrossChainMultisigContract = require_permissionless_bindings_cross_chain_multisig.CrossChainMultisigContract;
24
25
  exports.GovernorContract = require_permissionless_bindings_governance_governor.GovernorContract;
25
26
  exports.InstanceManagerContract = require_permissionless_bindings_instance_manager.InstanceManagerContract;
27
+ exports.KILL_SWITCH_PROXY_MINOR_VERSION = require_permissionless_bindings_kill_switch_proxy.KILL_SWITCH_PROXY_MINOR_VERSION;
28
+ exports.KillSwitchProxyContract = require_permissionless_bindings_kill_switch_proxy.KillSwitchProxyContract;
29
+ exports.KillSwitchProxyNotAllowedError = require_permissionless_bindings_kill_switch_proxy.KillSwitchProxyNotAllowedError;
26
30
  exports.MarketConfiguratorContract = require_permissionless_bindings_market_configurator.MarketConfiguratorContract;
27
31
  exports.MarketConfiguratorFactoryContract = require_permissionless_bindings_market_configurator_factory.MarketConfiguratorFactoryContract;
28
32
  exports.PriceFeedStoreContract = require_permissionless_bindings_price_feed_store.PriceFeedStoreContract;
@@ -31,4 +35,5 @@ exports.TimeLockContract = require_permissionless_bindings_governance_timelock.T
31
35
  exports.TokenCompressorContract = require_permissionless_bindings_compressors_token_compressor.TokenCompressorContract;
32
36
  exports.TreasurySplitterContract = require_permissionless_bindings_treasury_splitter.TreasurySplitterContract;
33
37
  exports.WithdrawalCompressorContract = require_permissionless_bindings_compressors_withdrawal_compressor.WithdrawalCompressorContract;
38
+ exports.killSwitchProxySalt = require_permissionless_bindings_kill_switch_proxy.killSwitchProxySalt;
34
39
  exports.priceFeedSetupParams = require_permissionless_bindings_pricefeeds_index.priceFeedSetupParams;