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

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 (49) hide show
  1. package/dist/cjs/dev/AccountOpener.js +1 -1
  2. package/dist/cjs/dev/withdrawalUtils.js +1 -1
  3. package/dist/cjs/history/trace/extractTransfers.js +1 -1
  4. package/dist/cjs/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  5. package/dist/cjs/onchain/accounts/liquidations/LiquidationsService.js +1 -1
  6. package/dist/cjs/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +2 -1
  7. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +2 -1
  8. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +2 -1
  9. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  10. package/dist/cjs/onchain/base/TokensMeta.js +3 -3
  11. package/dist/cjs/onchain/core/createAddressProvider.js +1 -1
  12. package/dist/cjs/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  13. package/dist/cjs/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  14. package/dist/cjs/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  15. package/dist/cjs/onchain/market/pool/PoolV310Contract.js +1 -1
  16. package/dist/cjs/onchain/market/rwa/createDegenNFT.js +1 -1
  17. package/dist/cjs/onchain/market/zapper/IETHZapperContract.js +2 -1
  18. package/dist/cjs/onchain/market/zapper/ZapperContract.js +1 -1
  19. package/dist/cjs/onchain/preview/preview/previewPoolPositionOperation.js +2 -2
  20. package/dist/cjs/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  21. package/dist/cjs/permissionless/api/index.js +7 -3
  22. package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +11 -11
  23. package/dist/cjs/permissionless/api/oracles/index.js +7 -3
  24. package/dist/cjs/permissionless/api/oracles/score/formula.js +44 -82
  25. package/dist/cjs/permissionless/api/oracles/score/index.js +6 -3
  26. package/dist/cjs/permissionless/api/oracles/score/run.js +129 -46
  27. package/dist/cjs/permissionless/api/oracles/stress/index.js +1 -0
  28. package/dist/cjs/permissionless/api/oracles/stress/run.js +5 -2
  29. package/dist/cjs/permissionless/index.js +7 -3
  30. package/dist/esm/permissionless/api/index.js +3 -3
  31. package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +11 -11
  32. package/dist/esm/permissionless/api/oracles/index.js +3 -3
  33. package/dist/esm/permissionless/api/oracles/score/formula.js +39 -80
  34. package/dist/esm/permissionless/api/oracles/score/index.js +2 -2
  35. package/dist/esm/permissionless/api/oracles/score/run.js +131 -48
  36. package/dist/esm/permissionless/api/oracles/stress/index.js +2 -2
  37. package/dist/esm/permissionless/api/oracles/stress/run.js +5 -3
  38. package/dist/esm/permissionless/index.js +3 -3
  39. package/dist/types/permissionless/api/index.d.ts +4 -4
  40. package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +11 -11
  41. package/dist/types/permissionless/api/oracles/index.d.ts +4 -4
  42. package/dist/types/permissionless/api/oracles/score/formula.d.ts +31 -38
  43. package/dist/types/permissionless/api/oracles/score/index.d.ts +3 -3
  44. package/dist/types/permissionless/api/oracles/score/run.d.ts +7 -4
  45. package/dist/types/permissionless/api/oracles/score/types.d.ts +50 -22
  46. package/dist/types/permissionless/api/oracles/stress/index.d.ts +2 -2
  47. package/dist/types/permissionless/api/oracles/stress/run.d.ts +17 -1
  48. package/dist/types/permissionless/index.d.ts +4 -4
  49. package/package.json +1 -1
@@ -1,73 +1,156 @@
1
1
  import { classifySources, sourcesByFeed } from "../stress/classify.js";
2
- import { RATE_FEED_TYPE, readForest, readRateTargets } from "../stress/run.js";
3
- import { scoreMarket } from "./formula.js";
2
+ import { RATE_FEED_TYPE, readForest, readRateTargets, readUnder } from "../stress/run.js";
3
+ import { factorOf, finalPrice, scoreOfFactor, worstOf } from "./formula.js";
4
+ import { formatUnits } from "viem";
4
5
  //#region src/permissionless/api/oracles/score/run.ts
6
+ const ZERO_TYPE = "PRICE_FEED::ZERO";
7
+ /** Groups simulated at once: one `eth_call` each, all against one node. */
8
+ const CONCURRENCY = 6;
9
+ const feedOf = (slot) => slot.priceFeed.toLowerCase();
5
10
  /**
6
- * How a source is told apart from another when two feeds are compared.
11
+ * Every asset of one chain, scored by what each of its source groups can do
12
+ * to the price its running markets hold for it.
7
13
  *
8
- * A named source by its group, since two Chainlink aggregators are one
9
- * provider failing together. An unnamed one by its contract, since nothing
10
- * says what else it fails with — so the same contract on both sides is
11
- * shared, and two different ones are not.
12
- */
13
- function toScoreSource(source) {
14
- return {
15
- key: source.identified ? `group:${source.group}` : `contract:${source.target}`,
16
- name: source.group,
17
- identified: source.identified
18
- };
19
- }
20
- /**
21
- * Every asset of one chain, scored by how its running markets price it.
22
- *
23
- * One read of every oracle's feed tree and one of the LP feeds' rate
24
- * contracts, both multicalls: the score is a reading of the trees and needs
25
- * no simulation.
14
+ * One read of the oracles' feed trees, one of the LP feeds' rate contracts,
15
+ * one of every market feed as it stands — then one `eth_call` per source
16
+ * group, with the group's sources overridden to answer
17
+ * {@link SCORE_MULTIPLIER} times what they answer now and every market feed
18
+ * read under it.
26
19
  **/
27
20
  async function runAssetScores({ client, chainId, slots, compressor, logger }) {
28
21
  const running = slots.filter((slot) => !slot.isShutdown);
29
22
  const oracles = [...new Set(running.map((slot) => slot.priceOracle.toLowerCase()))];
30
- const { nodes, unreadableOracles } = await readForest(client, compressor, oracles);
23
+ const { nodes, answers, unreadableOracles } = await readForest(client, compressor, oracles);
31
24
  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`);
32
- const { targets } = await readRateTargets(client, [...nodes.values()].filter((node) => RATE_FEED_TYPE.test(node.contractType)).map((node) => node.address));
33
- const { sources } = classifySources(chainId, [...nodes.values()], targets, () => null);
34
- const byTarget = new Map(sources.filter((source) => source.perturbable).map((source) => [source.target, toScoreSource(source)]));
35
- const feedOf = (slot) => slot.priceFeed.toLowerCase();
36
- const reach = sourcesByFeed([...new Set(running.map(feedOf))], nodes, sources);
37
- const sourcesOf = (feed) => [...reach.get(feed) ?? []].map((target) => byTarget.get(target)).filter((source) => source !== void 0);
38
- const positions = /* @__PURE__ */ new Map();
25
+ const { targets, baselines: rateBaselines } = await readRateTargets(client, [...nodes.values()].filter((node) => RATE_FEED_TYPE.test(node.contractType)).map((node) => node.address));
26
+ const { sources, unresolved } = classifySources(chainId, [...nodes.values()], targets, (target) => rateBaselines.get(target) ?? answers.get(target) ?? null);
27
+ 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`);
28
+ const slotsByPosition = /* @__PURE__ */ new Map();
39
29
  for (const slot of running) {
40
30
  const key = `${slot.market.toLowerCase()}:${slot.token.toLowerCase()}`;
41
- const position = positions.get(key) ?? {};
42
- position[slot.reserve ? "reserve" : "main"] = slot;
43
- positions.set(key, position);
31
+ const entry = slotsByPosition.get(key) ?? {};
32
+ entry[slot.reserve ? "reserve" : "main"] = slot;
33
+ slotsByPosition.set(key, entry);
44
34
  }
45
- const byAsset = /* @__PURE__ */ new Map();
46
- for (const { main, reserve } of positions.values()) {
35
+ const positions = [];
36
+ for (const { main, reserve } of slotsByPosition.values()) {
47
37
  if (!main || main.isUnderlying) continue;
38
+ const mainNode = nodes.get(feedOf(main));
39
+ if (!mainNode || mainNode.contractType === ZERO_TYPE) continue;
40
+ const notes = [];
41
+ let reserveFeed;
42
+ if (!reserve) notes.push("no reserve feed");
43
+ else if (nodes.get(feedOf(reserve))?.contractType === ZERO_TYPE) notes.push("reserve is a ZeroPriceFeed");
44
+ else {
45
+ reserveFeed = feedOf(reserve);
46
+ if (reserveFeed === feedOf(main)) notes.push("reserve is the main feed");
47
+ }
48
+ positions.push({
49
+ main,
50
+ reserve,
51
+ reserveFeed,
52
+ notes
53
+ });
54
+ }
55
+ const priced = [...new Set(positions.flatMap(({ main, reserveFeed }) => reserveFeed ? [feedOf(main), reserveFeed] : [feedOf(main)]))];
56
+ if (priced.length === 0) return [];
57
+ const underlyingOf = (slot) => slot.underlyingPriceFeed?.toLowerCase();
58
+ const roots = [.../* @__PURE__ */ new Set([...priced, ...positions.map(({ main }) => underlyingOf(main)).filter((feed) => feed !== void 0)])];
59
+ const updatedAt = BigInt(Math.floor(Date.now() / 1e3));
60
+ const drivable = (source) => source !== void 0 && source.perturbable && source.baseline !== null;
61
+ const fresh = sources.filter((source) => drivable(source) && source.kind === "feed");
62
+ const asIs = await readUnder(client, roots, [], 1, updatedAt);
63
+ const stale = roots.filter((root) => (asIs.get(root) ?? null) === null);
64
+ const freshened = stale.length > 0 ? await readUnder(client, stale, [], 1, updatedAt, fresh) : /* @__PURE__ */ new Map();
65
+ const base = new Map(roots.map((root) => [root, asIs.get(root) ?? freshened.get(root) ?? null]));
66
+ const asIsRoots = roots.filter((root) => (asIs.get(root) ?? null) !== null);
67
+ const freshRoots = stale.filter((root) => (freshened.get(root) ?? null) !== null);
68
+ /** Every root under a lie, each read the way its base was. */
69
+ const readLying = async (members) => {
70
+ const [plain, refreshed] = await Promise.all([asIsRoots.length > 0 ? readUnder(client, asIsRoots, members, 5, updatedAt) : /* @__PURE__ */ new Map(), freshRoots.length > 0 ? readUnder(client, freshRoots, members, 5, updatedAt, fresh) : /* @__PURE__ */ new Map()]);
71
+ return new Map([...plain, ...refreshed]);
72
+ };
73
+ for (const position of positions) if (position.reserveFeed && (base.get(position.reserveFeed) ?? null) === null) {
74
+ position.reserveFeed = void 0;
75
+ position.notes.push("reserve does not answer");
76
+ }
77
+ const reach = sourcesByFeed(roots, nodes, sources);
78
+ const sourceByTarget = new Map(sources.map((source) => [source.target, source]));
79
+ const byGroup = /* @__PURE__ */ new Map();
80
+ for (const source of sources.filter(drivable)) byGroup.set(source.group, [...byGroup.get(source.group) ?? [], source]);
81
+ const reachedGroups = /* @__PURE__ */ new Set();
82
+ for (const root of priced) for (const target of reach.get(root) ?? []) {
83
+ const source = sourceByTarget.get(target);
84
+ if (drivable(source)) reachedGroups.add(source.group);
85
+ }
86
+ const stressed = /* @__PURE__ */ new Map();
87
+ const queue = [...reachedGroups];
88
+ for (let i = 0; i < queue.length; i += CONCURRENCY) await Promise.all(queue.slice(i, i + CONCURRENCY).map(async (group) => {
89
+ stressed.set(group, await readLying(byGroup.get(group) ?? []));
90
+ }));
91
+ const groupsOf = (feed) => {
92
+ const out = /* @__PURE__ */ new Map();
93
+ for (const target of feed ? reach.get(feed) ?? [] : []) {
94
+ const source = sourceByTarget.get(target);
95
+ if (drivable(source)) out.set(source.group, [...out.get(source.group) ?? [], source]);
96
+ }
97
+ return out;
98
+ };
99
+ const byAsset = /* @__PURE__ */ new Map();
100
+ for (const { main, reserve, reserveFeed, notes } of positions) {
101
+ const mainFeed = feedOf(main);
102
+ const before = finalPrice(base.get(mainFeed) ?? null, reserveFeed ? base.get(reserveFeed) ?? null : void 0);
103
+ if (before === null || before <= 0n) {
104
+ logger?.warn(`feed ${mainFeed} of chain ${chainId} does not answer; ${main.tokenSymbol ?? main.token} in ${main.marketName ?? main.market} is not scored`);
105
+ continue;
106
+ }
107
+ const onMain = groupsOf(mainFeed);
108
+ const onReserve = groupsOf(reserveFeed);
109
+ const impacts = [.../* @__PURE__ */ new Set([...onMain.keys(), ...onReserve.keys()])].map((group) => {
110
+ const under = stressed.get(group) ?? /* @__PURE__ */ new Map();
111
+ const after = finalPrice(under.get(mainFeed) ?? null, reserveFeed ? under.get(reserveFeed) ?? null : void 0);
112
+ const factor = factorOf(before, after);
113
+ const underlying = underlyingOf(main);
114
+ const debtBefore = underlying ? base.get(underlying) ?? null : null;
115
+ const debtFactor = debtBefore === null || debtBefore <= 0n || !underlying ? null : factorOf(debtBefore, under.get(underlying) ?? null);
116
+ const ratioFactor = factor === null || debtFactor === null || debtFactor === 0 ? null : factor / debtFactor;
117
+ const members = [...onMain.get(group) ?? [], ...onReserve.get(group) ?? []];
118
+ return {
119
+ group,
120
+ identified: members.every((source) => source.identified),
121
+ feeds: [...new Set(members.map((source) => source.description))],
122
+ reaches: onMain.has(group) ? onReserve.has(group) ? "both" : "main" : "reserve",
123
+ factor,
124
+ ratioFactor,
125
+ score: scoreOfFactor(factor)
126
+ };
127
+ });
128
+ const unverified = reserveFeed !== void 0 && impacts.some((impact) => !impact.identified && impact.reaches !== "both");
129
+ const side = (slot) => ({
130
+ feed: feedOf(slot),
131
+ name: slot.priceFeedName,
132
+ contractType: nodes.get(feedOf(slot))?.contractType ?? "",
133
+ sources: [...groupsOf(feedOf(slot)).keys()]
134
+ });
48
135
  const asset = main.token.toLowerCase();
49
136
  const entry = byAsset.get(asset) ?? {
50
137
  symbol: main.tokenSymbol,
51
138
  markets: []
52
139
  };
53
140
  byAsset.set(asset, entry);
54
- const mainNode = nodes.get(feedOf(main));
55
- if (!mainNode) continue;
56
- if (mainNode.contractType === "PRICE_FEED::ZERO") continue;
57
- const side = (slot) => ({
58
- feed: {
59
- feed: feedOf(slot),
60
- name: slot.priceFeedName,
61
- contractType: nodes.get(feedOf(slot))?.contractType ?? ""
62
- },
63
- sources: sourcesOf(feedOf(slot))
64
- });
65
- entry.markets.push(scoreMarket({
141
+ entry.markets.push({
66
142
  market: main.market,
67
143
  marketName: main.marketName,
68
144
  marketConfigurator: main.marketConfigurator,
69
- curatorName: main.curatorName
70
- }, side(main), reserve ? side(reserve) : null));
145
+ curatorName: main.curatorName,
146
+ main: side(main),
147
+ reserve: reserve ? side(reserve) : null,
148
+ hasReserve: reserveFeed !== void 0,
149
+ price: Number(formatUnits(before, 8)),
150
+ unverified,
151
+ notes,
152
+ ...worstOf(impacts)
153
+ });
71
154
  }
72
155
  return [...byAsset].map(([asset, { symbol, markets }]) => {
73
156
  const sorted = markets.sort((a, b) => a.score - b.score);
@@ -1,6 +1,6 @@
1
1
  import { classifySources, sourcesByFeed } from "./classify.js";
2
2
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./override.js";
3
3
  import { scoreGroup, slotHarm, verdictOf } from "./score.js";
4
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./run.js";
4
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./run.js";
5
5
  import "./types.js";
6
- export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
6
+ export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
@@ -160,8 +160,10 @@ async function readRateTargets(client, feeds) {
160
160
  * scenario rather than a round trip per feed. A feed that reverts comes back
161
161
  * `null`, which is an outcome and not an error.
162
162
  */
163
- async function readUnder(client, feeds, sources, multiplier, updatedAt) {
164
- const overrides = groupOverride(sources, multiplier, updatedAt);
163
+ async function readUnder(client, feeds, sources, multiplier, updatedAt, freshen = []) {
164
+ const driven = groupOverride(sources, multiplier, updatedAt);
165
+ const taken = new Set(driven.map((override) => override.address.toLowerCase()));
166
+ const overrides = [...groupOverride(freshen, 1, updatedAt).filter((override) => !taken.has(override.address.toLowerCase())), ...driven];
165
167
  const results = await client.multicall({
166
168
  contracts: feeds.map((address) => ({
167
169
  address,
@@ -424,4 +426,4 @@ async function runStressTest({ client, chainId, slots, compressor, scenarios = D
424
426
  };
425
427
  }
426
428
  //#endregion
427
- export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest };
429
+ export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest };
@@ -15,8 +15,8 @@ import { getFeedDecimals, getLatestPrices, getPrices } from "./utils/price-updat
15
15
  import { classifySources, sourcesByFeed } from "./api/oracles/stress/classify.js";
16
16
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./api/oracles/stress/override.js";
17
17
  import { scoreGroup, slotHarm, verdictOf } from "./api/oracles/stress/score.js";
18
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./api/oracles/stress/run.js";
19
- import { BOUND_WEIGHT, INDEPENDENCE_WEIGHT, scoreMarket } from "./api/oracles/score/formula.js";
18
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./api/oracles/stress/run.js";
19
+ import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./api/oracles/score/formula.js";
20
20
  import { runAssetScores } from "./api/oracles/score/run.js";
21
21
  import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./api/oracles/PermissionlessOracles.js";
22
22
  import { TokenCompressorContract } from "./bindings/compressors/token-compressor.js";
@@ -47,4 +47,4 @@ import { getBlockNumberByTimestamp } from "./utils/block-utils.js";
47
47
  import { convertQueueBatchToExecuteTx } from "./utils/governance/timelock-txs.js";
48
48
  import { getPriceUpdateTx } from "./utils/price-update/get-price-update-tx.js";
49
49
  import "./utils/index.js";
50
- export { AccessMode, AddressProviderContract, Addresses, ArchiveTransport, BOUND_WEIGHT, BYTECODE_REPOSITORY, BytecodeRepositoryContract, CROSS_CHAIN_GOVERNANCE, CROSS_CHAIN_MULTISIG, ChainsNamespace, CrossChainMultisigContract, DEFAULT_DEPLOY_SALT, DEFAULT_SCENARIOS, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, GovernorContract, INDEPENDENCE_WEIGHT, INSTANCE_MANAGER, InstanceManagerContract, InstanceOwnerTransactions, MAINNET_CCM_CONSTRUCTOR_PARAMS, MAINNET_DEPLOYMENT_BLOCK, MAINNET_DEPLOYMENT_CCM_SALT, MAINNET_DEPLOYMENT_IM_SALT, MARKET_CONFIGURATOR_FACTORY, MarketConfiguratorContract, MarketConfiguratorFactoryContract, PRICE_FEED_STORE, PUBLIC_CREATE2_FACTORY, PermissionlessAnalytics, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessNotAttachedError, PermissionlessOracles, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, PriceFeedStoreContract, RATE_FEED_TYPE, RoutingManagerContract, TOKEN_COMPRESSOR, TimeLockContract, TokenCompressorContract, TreasurySplitterContract, UNBOUNDED_DOWN, UNBOUNDED_UP, WithdrawalCompressorContract, calculateChecksum, checkCreate2Deployment, checkCreate2DeploymentAddress, chunkedLogsTransport, classifySources, convertPercent, convertQueueBatchToExecuteTx, convertRawTxToSafeMultisigTx, createCallData, createCreate2DeployRawTx, deepJsonParse, deployUsingPublicCreate2, deriveEffectiveStalenessPeriod, encodeFunctionParams, formatBytecodeSize, formatBytecodeVersion, getBlockNumberByTimestamp, getCreate2AddressPublicFactory, getCreate2DeploymentAddress, getFeedDecimals, getLatestPrices, getPriceUpdateTx, getPrices, getSafeBatch, getSafeBatches, getUpdatablePriceFeeds, groupOverride, handleSalt, normalizeSignature, perturb, priceFeedSalt, priceFeedSetupParams, rateStub, readForest, readRateTargets, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreMarket, shortenHash, significantTrunc, slotHarm, sourcesByFeed, verdictOf };
50
+ export { AccessMode, AddressProviderContract, Addresses, ArchiveTransport, BYTECODE_REPOSITORY, BytecodeRepositoryContract, CROSS_CHAIN_GOVERNANCE, CROSS_CHAIN_MULTISIG, ChainsNamespace, CrossChainMultisigContract, DEFAULT_DEPLOY_SALT, DEFAULT_SCENARIOS, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, GovernorContract, INSTANCE_MANAGER, InstanceManagerContract, InstanceOwnerTransactions, MAINNET_CCM_CONSTRUCTOR_PARAMS, MAINNET_DEPLOYMENT_BLOCK, MAINNET_DEPLOYMENT_CCM_SALT, MAINNET_DEPLOYMENT_IM_SALT, MARKET_CONFIGURATOR_FACTORY, MarketConfiguratorContract, MarketConfiguratorFactoryContract, PRICE_FEED_STORE, PUBLIC_CREATE2_FACTORY, PermissionlessAnalytics, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessNotAttachedError, PermissionlessOracles, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, PriceFeedStoreContract, RATE_FEED_TYPE, REVERT_SCORE, RoutingManagerContract, SCORE_MULTIPLIER, TOKEN_COMPRESSOR, TimeLockContract, TokenCompressorContract, TreasurySplitterContract, UNBOUNDED_DOWN, UNBOUNDED_UP, WithdrawalCompressorContract, calculateChecksum, checkCreate2Deployment, checkCreate2DeploymentAddress, chunkedLogsTransport, classifySources, convertPercent, convertQueueBatchToExecuteTx, convertRawTxToSafeMultisigTx, createCallData, createCreate2DeployRawTx, deepJsonParse, deployUsingPublicCreate2, deriveEffectiveStalenessPeriod, encodeFunctionParams, factorOf, finalPrice, formatBytecodeSize, formatBytecodeVersion, getBlockNumberByTimestamp, getCreate2AddressPublicFactory, getCreate2DeploymentAddress, getFeedDecimals, getLatestPrices, getPriceUpdateTx, getPrices, getSafeBatch, getSafeBatches, getUpdatablePriceFeeds, groupOverride, handleSalt, normalizeSignature, perturb, priceFeedSalt, priceFeedSetupParams, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreOfFactor, shortenHash, significantTrunc, slotHarm, sourcesByFeed, verdictOf, worstOf };
@@ -5,7 +5,7 @@ import { AttachChainsArgs, ChainScope, ChainSummaryArgs, PermissionlessChain, Pe
5
5
  import { ChainsNamespace } from "./chains/ChainsNamespace.js";
6
6
  import { DeployArgs, DeployContractArgs, DeployPriceFeedArgs, PreparedDeployment, PreparedPriceFeedDeployment } from "./deploy/types.js";
7
7
  import { DEFAULT_DEPLOY_SALT, PermissionlessDeploy, priceFeedSalt } from "./deploy/PermissionlessDeploy.js";
8
- import { AssetScore, AssetScoresArgs, MarketScore, ScoredFeed } from "./oracles/score/types.js";
8
+ import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./oracles/score/types.js";
9
9
  import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./oracles/stress/types.js";
10
10
  import { AssetArgs, AssetCap, AssetsArgs, FeedCap, MarketCap, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PriceFeedArgs, PriceFeedDecimals, PriceFeedPrice, PriceFeedRef } from "./oracles/types.js";
11
11
  import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./oracles/PermissionlessOracles.js";
@@ -17,12 +17,12 @@ import "./chains/index.js";
17
17
  import { deriveEffectiveStalenessPeriod } from "./deploy/effective-staleness.js";
18
18
  import "./deploy/index.js";
19
19
  import { PermissionlessChainUnreachableError, PermissionlessNotAttachedError, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError } from "./errors.js";
20
- import { BOUND_WEIGHT, INDEPENDENCE_WEIGHT, ScoreSource, scoreMarket } from "./oracles/score/formula.js";
20
+ import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./oracles/score/formula.js";
21
21
  import { runAssetScores } from "./oracles/score/run.js";
22
22
  import { TreeNode, classifySources, sourcesByFeed } from "./oracles/stress/classify.js";
23
23
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./oracles/stress/override.js";
24
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./oracles/stress/run.js";
24
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./oracles/stress/run.js";
25
25
  import { scoreGroup, slotHarm, verdictOf } from "./oracles/stress/score.js";
26
26
  import "./oracles/index.js";
27
27
  import "./transactions/index.js";
28
- export { AddFeedsArgs, AssetArgs, AssetCap, AssetScore, AssetScoresArgs, AssetsArgs, AttachChainsArgs, BOUND_WEIGHT, BreakingPoint, ChainScope, ChainSummaryArgs, ChainsNamespace, ChangeNamesArgs, ChangeStalenessPeriodsArgs, DEFAULT_DEPLOY_SALT, DEFAULT_SCENARIOS, DeployArgs, DeployContractArgs, DeployPriceFeedArgs, FeedCap, FeedOutcome, ForbidFeedsArgs, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, GearboxPermissionlessOptions, GroupReport, GroupScenarioOutcome, INDEPENDENCE_WEIGHT, InstanceOwnerTransactions, InstanceTxsArgs, MarketCap, MarketScore, PermissionlessAnalytics, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessChain, PermissionlessChainSummary, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessNotAttachedError, PermissionlessOracles, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PreparedDeployment, PreparedPriceFeedDeployment, PriceChartArgs, PriceFeedArgs, PriceFeedChartPoint, PriceFeedDecimals, PriceFeedNotInStoreError, PriceFeedPair, PriceFeedPrice, PriceFeedPriceChart, PriceFeedRef, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, RATE_FEED_TYPE, ScoreSource, ScoredFeed, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, deriveEffectiveStalenessPeriod, groupOverride, perturb, priceFeedSalt, rateStub, readForest, readRateTargets, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreMarket, slotHarm, sourcesByFeed, verdictOf };
28
+ export { AddFeedsArgs, AssetArgs, AssetCap, AssetScore, AssetScoresArgs, AssetsArgs, AttachChainsArgs, BreakingPoint, ChainScope, ChainSummaryArgs, ChainsNamespace, ChangeNamesArgs, ChangeStalenessPeriodsArgs, DEFAULT_DEPLOY_SALT, DEFAULT_SCENARIOS, DeployArgs, DeployContractArgs, DeployPriceFeedArgs, FeedCap, FeedOutcome, ForbidFeedsArgs, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, GearboxPermissionlessOptions, GroupImpact, GroupReport, GroupScenarioOutcome, InstanceOwnerTransactions, InstanceTxsArgs, MarketCap, MarketScore, PermissionlessAnalytics, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessChain, PermissionlessChainSummary, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessNotAttachedError, PermissionlessOracles, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PreparedDeployment, PreparedPriceFeedDeployment, PriceChartArgs, PriceFeedArgs, PriceFeedChartPoint, PriceFeedDecimals, PriceFeedNotInStoreError, PriceFeedPair, PriceFeedPrice, PriceFeedPriceChart, PriceFeedRef, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, RATE_FEED_TYPE, REVERT_SCORE, SCORE_MULTIPLIER, ScoredFeed, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, deriveEffectiveStalenessPeriod, factorOf, finalPrice, groupOverride, perturb, priceFeedSalt, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreOfFactor, slotHarm, sourcesByFeed, verdictOf, worstOf };
@@ -194,17 +194,17 @@ export declare class PermissionlessOracles extends AbstractOffchainNamespace {
194
194
  /**
195
195
  * Every asset's score out of 10 for how its markets guard its price.
196
196
  *
197
- * A market scores 10 when its reserve feed is a second feed on other
198
- * sources than the main one, and one of the two is bounded — a
199
- * `BoundedPriceFeed` or a Gearbox LP feed. It loses 6 points as the two
200
- * feeds' sources overlap — all of them if there is no reserve, it is a
201
- * `ZeroPriceFeed`, or it is the main feed again — and 5 if neither is
202
- * bounded. An asset scores its worst running market that takes it as
203
- * collateral.
204
- *
205
- * Read off the chain, one multicall of feed trees per chain, so the score
206
- * is of the trees as they stand rather than as last indexed. A chain with
207
- * no client configured is left out with a warning rather than failing the
197
+ * Each source group behind an asset's feeds — Chainlink, Redstone, Lido and
198
+ * so on — is made in turn to answer five times what it answers now, and a
199
+ * market scores by how far that moves its final price, `min(main,
200
+ * reserve)`: 10 for not at all, 0 for the full five times, on a log scale.
201
+ * A price that stops answering scores 5. An asset scores its worst running
202
+ * market that takes it as collateral.
203
+ *
204
+ * Simulated on the chain as it stands, through state overrides in
205
+ * `eth_call`: one call per source group per chain, so the node behind the
206
+ * client has to accept overrides. A chain with no client configured, or
207
+ * whose node refuses, is left out with a warning rather than failing the
208
208
  * rest.
209
209
  *
210
210
  * ```ts
@@ -1,13 +1,13 @@
1
- import { AssetScore, AssetScoresArgs, MarketScore, ScoredFeed } from "./score/types.js";
1
+ import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./score/types.js";
2
2
  import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./stress/types.js";
3
3
  import { AssetArgs, AssetCap, AssetsArgs, FeedCap, MarketCap, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PriceFeedArgs, PriceFeedDecimals, PriceFeedPrice, PriceFeedRef } from "./types.js";
4
4
  import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./PermissionlessOracles.js";
5
- import { BOUND_WEIGHT, INDEPENDENCE_WEIGHT, ScoreSource, scoreMarket } from "./score/formula.js";
5
+ import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./score/formula.js";
6
6
  import { runAssetScores } from "./score/run.js";
7
7
  import "./score/index.js";
8
8
  import { TreeNode, classifySources, sourcesByFeed } from "./stress/classify.js";
9
9
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./stress/override.js";
10
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./stress/run.js";
10
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./stress/run.js";
11
11
  import { scoreGroup, slotHarm, verdictOf } from "./stress/score.js";
12
12
  import "./stress/index.js";
13
- export { AssetArgs, AssetCap, AssetScore, AssetScoresArgs, AssetsArgs, BOUND_WEIGHT, BreakingPoint, DEFAULT_SCENARIOS, FeedCap, FeedOutcome, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GroupReport, GroupScenarioOutcome, INDEPENDENCE_WEIGHT, MarketCap, MarketScore, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessOracles, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PriceFeedArgs, PriceFeedDecimals, PriceFeedPrice, PriceFeedRef, RATE_FEED_TYPE, ScoreSource, ScoredFeed, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreMarket, slotHarm, sourcesByFeed, verdictOf };
13
+ export { AssetArgs, AssetCap, AssetScore, AssetScoresArgs, AssetsArgs, BreakingPoint, DEFAULT_SCENARIOS, FeedCap, FeedOutcome, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GroupImpact, GroupReport, GroupScenarioOutcome, MarketCap, MarketScore, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessMarketSlot, PermissionlessOracles, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PriceFeedArgs, PriceFeedDecimals, PriceFeedPrice, PriceFeedRef, RATE_FEED_TYPE, REVERT_SCORE, SCORE_MULTIPLIER, ScoredFeed, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, factorOf, finalPrice, groupOverride, perturb, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runAssetScores, runStressTest, scoreGroup, scoreOfFactor, slotHarm, sourcesByFeed, verdictOf, worstOf };
@@ -1,46 +1,39 @@
1
- import { MarketScore, ScoredFeed } from "./types.js";
1
+ import { GroupImpact } from "./types.js";
2
2
  //#region src/permissionless/api/oracles/score/formula.d.ts
3
+ /** What every source of a group is made to answer, as a multiple of now. */
4
+ export declare const SCORE_MULTIPLIER = 5;
3
5
  /**
4
- * How well a market guards an asset's price, from its two feeds alone.
6
+ * The score of a price that stops answering under the lie.
5
7
  *
6
- * Two questions, and the points are lost for each separately:
7
- *
8
- * - Would the reserve disagree with a main feed that has gone wrong? Only if
9
- * it is a second feed on other sources. A missing reserve, a
10
- * `ZeroPriceFeed`, the main feed again, and a different feed on the same
11
- * sources all answer no.
12
- * - Is either feed bounded? A `BoundedPriceFeed` caps the answer from above,
13
- * and a Gearbox LP feed clamps its exchange rate to a window.
14
- *
15
- * Independence costs more than the bound, 6 points against 5, because it is
16
- * the broader protection: a bounded feed caps only from above, and an LP
17
- * feed's window holds only its rate, not the price of what the rate is in.
18
- * Either failure alone lands a market at 4 or 5; both together at 0.
8
+ * Half marks: nobody can borrow against a wrong number, but nobody can be
9
+ * liquidated either until the feed is replaced.
19
10
  */
20
- export declare const INDEPENDENCE_WEIGHT = 6;
21
- export declare const BOUND_WEIGHT = 5;
22
- /** A source, as the overlap between two feeds is counted. */
23
- export interface ScoreSource {
24
- /** What two feeds have to share to be on the same source. */
25
- key: string;
26
- /** What a reader is told. */
27
- name: string;
28
- /** A source the classifier named, rather than one grouped alone. */
29
- identified: boolean;
30
- }
11
+ export declare const REVERT_SCORE = 5;
31
12
  /**
32
- * One market's score for one asset.
13
+ * 0 to 10 by how far the final price moved, on a log scale against the lie
14
+ * itself: 10 for a price that did not move, 0 for one that moved by the full
15
+ * {@link SCORE_MULTIPLIER} or more, either way. Rounded to a half point.
33
16
  *
34
- * `reserve` is `null` where the market holds no reserve for the asset at all.
35
- * A source is passed per side and keyed by the caller, which is where it is
36
- * decided that an unnamed source is its own contract and a named one is its
37
- * group.
17
+ * A bound holding a price within a few percent keeps a 10; a price that let
18
+ * half the lie through, ×√5, scores 5.
19
+ */
20
+ export declare function scoreOfFactor(factor: number | null): number;
21
+ /**
22
+ * The price a market settles on: the lower of main and reserve, or the main
23
+ * feed's alone where there is no reserve to compare it with. `null` where a
24
+ * feed it needs does not answer.
25
+ */
26
+ export declare function finalPrice(main: bigint | null, reserve: bigint | null | undefined): bigint | null;
27
+ /** `stressed / base` as a float, `null` where the stressed price is gone. */
28
+ export declare function factorOf(base: bigint, stressed: bigint | null): number | null;
29
+ /**
30
+ * The market's score and the group it comes from: the lowest of its groups,
31
+ * ties going to the group that moved the price further. 10 where no source
32
+ * feeds the price at all, since nothing can be made to lie about it.
38
33
  */
39
- export declare function scoreMarket(market: Omit<MarketScore, "main" | "reserve" | "hasReserve" | "independence" | "bounded" | "sharedSources" | "unverified" | "score" | "reasons">, main: {
40
- feed: Omit<ScoredFeed, "sources">;
41
- sources: ScoreSource[];
42
- }, reserve: {
43
- feed: Omit<ScoredFeed, "sources">;
44
- sources: ScoreSource[];
45
- } | null): MarketScore;
34
+ export declare function worstOf(impacts: GroupImpact[]): {
35
+ score: number;
36
+ impacts: GroupImpact[];
37
+ worst: GroupImpact | null;
38
+ };
46
39
  //#endregion
@@ -1,4 +1,4 @@
1
- import { AssetScore, AssetScoresArgs, MarketScore, ScoredFeed } from "./types.js";
2
- import { BOUND_WEIGHT, INDEPENDENCE_WEIGHT, ScoreSource, scoreMarket } from "./formula.js";
1
+ import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./types.js";
2
+ import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./formula.js";
3
3
  import { runAssetScores } from "./run.js";
4
- export { AssetScore, AssetScoresArgs, BOUND_WEIGHT, INDEPENDENCE_WEIGHT, MarketScore, ScoreSource, ScoredFeed, runAssetScores, scoreMarket };
4
+ export { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, REVERT_SCORE, SCORE_MULTIPLIER, ScoredFeed, factorOf, finalPrice, runAssetScores, scoreOfFactor, worstOf };
@@ -3,11 +3,14 @@ import { PermissionlessMarketSlot } from "../types.js";
3
3
  import { Address, Chain, PublicClient, Transport } from "viem";
4
4
  //#region src/permissionless/api/oracles/score/run.d.ts
5
5
  /**
6
- * Every asset of one chain, scored by how its running markets price it.
6
+ * Every asset of one chain, scored by what each of its source groups can do
7
+ * to the price its running markets hold for it.
7
8
  *
8
- * One read of every oracle's feed tree and one of the LP feeds' rate
9
- * contracts, both multicalls: the score is a reading of the trees and needs
10
- * no simulation.
9
+ * One read of the oracles' feed trees, one of the LP feeds' rate contracts,
10
+ * one of every market feed as it stands — then one `eth_call` per source
11
+ * group, with the group's sources overridden to answer
12
+ * {@link SCORE_MULTIPLIER} times what they answer now and every market feed
13
+ * read under it.
11
14
  **/
12
15
  export declare function runAssetScores({ client, chainId, slots, compressor, logger }: {
13
16
  client: PublicClient<Transport, Chain>;
@@ -10,16 +10,47 @@ export interface ScoredFeed {
10
10
  /** Gearbox contract type of the feed itself, `""` for one it never deployed. */
11
11
  contractType: string;
12
12
  /**
13
- * Every price source beneath the feed, by name: `"Chainlink"`, `"Lido"`, or
14
- * the description of a source nothing could name.
13
+ * Every source group beneath the feed: `"Chainlink"`, `"Lido"`, or the
14
+ * description of a source nothing could name.
15
15
  */
16
16
  sources: string[];
17
17
  }
18
+ /**
19
+ * What one source group, lying, does to the price a market holds for an
20
+ * asset.
21
+ **/
22
+ export interface GroupImpact {
23
+ group: string;
24
+ /** The group was recognised rather than inferred from one feed's name. */
25
+ identified: boolean;
26
+ /**
27
+ * The group's sources this market's feeds read, by what they say they are:
28
+ * `"stETH / ETH"`, `"ETH / USD"`.
29
+ */
30
+ feeds: string[];
31
+ /** Which of the market's feeds read the group. */
32
+ reaches: "main" | "reserve" | "both";
33
+ /**
34
+ * The final price under the lie over the final price now, in USD. `null` is
35
+ * a price that stopped answering.
36
+ */
37
+ factor: number | null;
38
+ /**
39
+ * The same, against the market's own underlying: what an account is
40
+ * valued by. A lie that moves collateral and underlying alike leaves it at
41
+ * 1. `null` where either price stopped answering.
42
+ */
43
+ ratioFactor: number | null;
44
+ /** 0 to 10, by {@link scoreOfFactor}. */
45
+ score: number;
46
+ }
18
47
  /**
19
48
  * How well one market guards one asset's price, out of 10.
20
49
  *
21
- * `score = max(0, 10 − 6·(1 − independence) − 5·(1 − bounded))`, rounded down
22
- * to a half point.
50
+ * Every source group behind the asset's feeds is made, one group at a time,
51
+ * to answer {@link SCORE_MULTIPLIER} times what it answers now. The market
52
+ * scores by the group that moves its final price — `min(main, reserve)` —
53
+ * the furthest.
23
54
  **/
24
55
  export interface MarketScore {
25
56
  market: Address;
@@ -30,31 +61,28 @@ export interface MarketScore {
30
61
  /** `null` where the market holds no reserve for the asset at all. */
31
62
  reserve: ScoredFeed | null;
32
63
  /**
33
- * The reserve is a second feed: present, not a `ZeroPriceFeed`, and not
34
- * the main feed again.
64
+ * The final price is `min(main, reserve)`: a reserve is present, is not a
65
+ * `ZeroPriceFeed`, and answers. Where it is not, the final price is the
66
+ * main feed's alone.
35
67
  */
36
68
  hasReserve: boolean;
69
+ /** The final price now, in USD. */
70
+ price: number;
71
+ /** Every group behind the two feeds, the most damaging first. */
72
+ impacts: GroupImpact[];
73
+ /** The group the score comes from; `null` where no source feeds the price. */
74
+ worst: GroupImpact | null;
37
75
  /**
38
- * The share of the two feeds' sources they do not have in common:
39
- * `1 − |shared| / |all|`. 1 for feeds with nothing in common, 0 for feeds
40
- * on the same sources — and 0 where {@link hasReserve} is false.
41
- */
42
- independence: number;
43
- /** Main or reserve is a `BoundedPriceFeed` or a Gearbox LP feed. */
44
- bounded: boolean;
45
- /** The sources the two feeds have in common, by name. */
46
- sharedSources: string[];
47
- /**
48
- * The independence counted here rests on a source nothing could name.
76
+ * The score rests on a source nothing could name.
49
77
  *
50
- * Such a source counts as its own, so two of them look independent even
51
- * if one operator stands behind both. The same unnamed source on both
52
- * sides — the same contract — is still counted as shared.
78
+ * Such a source is a group of its own and is perturbed alone, so where one
79
+ * feed reads it and the other does not, the other is taken to hold — which
80
+ * it would not if one operator stood behind both.
53
81
  */
54
82
  unverified: boolean;
55
83
  score: number;
56
- /** What cost the market its points, for a reader to act on. Empty at 10. */
57
- reasons: string[];
84
+ /** How the market is set up, where that explains its score. */
85
+ notes: string[];
58
86
  }
59
87
  /**
60
88
  * One asset's score: its worst market's.
@@ -1,6 +1,6 @@
1
1
  import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./types.js";
2
2
  import { TreeNode, classifySources, sourcesByFeed } from "./classify.js";
3
3
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./override.js";
4
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./run.js";
4
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./run.js";
5
5
  import { scoreGroup, slotHarm, verdictOf } from "./score.js";
6
- export { BreakingPoint, DEFAULT_SCENARIOS, FeedOutcome, GroupReport, GroupScenarioOutcome, RATE_FEED_TYPE, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
6
+ export { BreakingPoint, DEFAULT_SCENARIOS, FeedOutcome, GroupReport, GroupScenarioOutcome, RATE_FEED_TYPE, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs, TreeNode, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
@@ -1,4 +1,4 @@
1
- import { StressReport, StressScenario } from "./types.js";
1
+ import { StressReport, StressScenario, StressSource } from "./types.js";
2
2
  import { PermissionlessMarketSlot } from "../types.js";
3
3
  import { TreeNode } from "./classify.js";
4
4
  import { Address, Chain, PublicClient, Transport } from "viem";
@@ -49,6 +49,22 @@ export declare function readRateTargets(client: PublicClient<Transport, Chain>,
49
49
  upper: bigint;
50
50
  }>;
51
51
  }>;
52
+ /**
53
+ * What every feed answers with one group's sources overridden.
54
+ *
55
+ * One `eth_call` for the whole chain: the overrides go in once and the feeds
56
+ * are read through a multicall inside them, so a group costs a round trip per
57
+ * scenario rather than a round trip per feed. A feed that reverts comes back
58
+ * `null`, which is an outcome and not an error.
59
+ */
60
+ export declare function readUnder(client: PublicClient<Transport, Chain>, feeds: Address[], sources: StressSource[], multiplier: number, updatedAt: bigint,
61
+ /**
62
+ * Sources re-answered as they stand, under a fresh timestamp: a pull feed
63
+ * nobody has pushed to lately is stale, and a feed over it reverts on that
64
+ * before anything about the price is learnt. `sources` win where the two
65
+ * overlap.
66
+ */
67
+ freshen?: StressSource[]): Promise<Map<Address, bigint | null>>;
52
68
  /**
53
69
  * Every source behind one chain's markets, put through the sweep a group at a
54
70
  * time.