@gearbox-protocol/sdk 18.2.0-next.10 → 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.
- package/dist/cjs/permissionless/api/index.js +30 -0
- package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +191 -0
- package/dist/cjs/permissionless/api/oracles/index.js +32 -0
- package/dist/cjs/permissionless/api/oracles/schemas.js +17 -0
- package/dist/cjs/permissionless/api/oracles/score/formula.js +61 -0
- package/dist/cjs/permissionless/api/oracles/score/index.js +11 -0
- package/dist/cjs/permissionless/api/oracles/score/run.js +171 -0
- package/dist/cjs/permissionless/api/oracles/score/types.js +1 -0
- package/dist/cjs/permissionless/api/oracles/stress/classify.js +258 -0
- package/dist/cjs/permissionless/api/oracles/stress/index.js +23 -0
- package/dist/cjs/permissionless/api/oracles/stress/override.js +125 -0
- package/dist/cjs/permissionless/api/oracles/stress/run.js +435 -0
- package/dist/cjs/permissionless/api/oracles/stress/score.js +98 -0
- package/dist/cjs/permissionless/api/oracles/stress/types.js +1 -0
- package/dist/cjs/permissionless/index.js +31 -0
- package/dist/cjs/permissionless/utils/index.js +1 -0
- package/dist/cjs/permissionless/utils/price-update/get-prices.js +39 -0
- package/dist/cjs/permissionless/utils/price-update/index.js +1 -0
- package/dist/esm/permissionless/api/index.js +7 -1
- package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +193 -2
- package/dist/esm/permissionless/api/oracles/index.js +10 -1
- package/dist/esm/permissionless/api/oracles/schemas.js +16 -1
- package/dist/esm/permissionless/api/oracles/score/formula.js +55 -0
- package/dist/esm/permissionless/api/oracles/score/index.js +4 -0
- package/dist/esm/permissionless/api/oracles/score/run.js +170 -0
- package/dist/esm/permissionless/api/oracles/score/types.js +1 -0
- package/dist/esm/permissionless/api/oracles/stress/classify.js +256 -0
- package/dist/esm/permissionless/api/oracles/stress/index.js +6 -0
- package/dist/esm/permissionless/api/oracles/stress/override.js +119 -0
- package/dist/esm/permissionless/api/oracles/stress/run.js +429 -0
- package/dist/esm/permissionless/api/oracles/stress/score.js +95 -0
- package/dist/esm/permissionless/api/oracles/stress/types.js +1 -0
- package/dist/esm/permissionless/index.js +8 -2
- package/dist/esm/permissionless/utils/index.js +2 -2
- package/dist/esm/permissionless/utils/price-update/get-prices.js +39 -1
- package/dist/esm/permissionless/utils/price-update/index.js +2 -2
- package/dist/types/permissionless/api/index.d.ts +10 -2
- package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +115 -1
- package/dist/types/permissionless/api/oracles/index.d.ts +12 -2
- package/dist/types/permissionless/api/oracles/schemas.d.ts +47 -0
- package/dist/types/permissionless/api/oracles/score/formula.d.ts +39 -0
- package/dist/types/permissionless/api/oracles/score/index.d.ts +4 -0
- package/dist/types/permissionless/api/oracles/score/run.d.ts +24 -0
- package/dist/types/permissionless/api/oracles/score/types.d.ts +113 -0
- package/dist/types/permissionless/api/oracles/stress/classify.d.ts +57 -0
- package/dist/types/permissionless/api/oracles/stress/index.d.ts +6 -0
- package/dist/types/permissionless/api/oracles/stress/override.d.ts +59 -0
- package/dist/types/permissionless/api/oracles/stress/run.d.ts +89 -0
- package/dist/types/permissionless/api/oracles/stress/score.d.ts +57 -0
- package/dist/types/permissionless/api/oracles/stress/types.d.ts +272 -0
- package/dist/types/permissionless/api/oracles/types.d.ts +58 -0
- package/dist/types/permissionless/index.d.ts +11 -3
- package/dist/types/permissionless/utils/index.d.ts +2 -2
- package/dist/types/permissionless/utils/price-update/get-prices.d.ts +16 -0
- package/dist/types/permissionless/utils/price-update/index.d.ts +2 -2
- package/package.json +1 -1
|
@@ -5,7 +5,9 @@ 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 {
|
|
8
|
+
import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./oracles/score/types.js";
|
|
9
|
+
import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./oracles/stress/types.js";
|
|
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";
|
|
9
11
|
import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./oracles/PermissionlessOracles.js";
|
|
10
12
|
import { AddFeedsArgs, ChangeNamesArgs, ChangeStalenessPeriodsArgs, ForbidFeedsArgs, InstanceTxsArgs, PriceFeedPair } from "./transactions/types.js";
|
|
11
13
|
import { InstanceOwnerTransactions } from "./transactions/InstanceOwnerTransactions.js";
|
|
@@ -15,6 +17,12 @@ import "./chains/index.js";
|
|
|
15
17
|
import { deriveEffectiveStalenessPeriod } from "./deploy/effective-staleness.js";
|
|
16
18
|
import "./deploy/index.js";
|
|
17
19
|
import { PermissionlessChainUnreachableError, PermissionlessNotAttachedError, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError } from "./errors.js";
|
|
20
|
+
import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./oracles/score/formula.js";
|
|
21
|
+
import { runAssetScores } from "./oracles/score/run.js";
|
|
22
|
+
import { TreeNode, classifySources, sourcesByFeed } from "./oracles/stress/classify.js";
|
|
23
|
+
import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./oracles/stress/override.js";
|
|
24
|
+
import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./oracles/stress/run.js";
|
|
25
|
+
import { scoreGroup, slotHarm, verdictOf } from "./oracles/stress/score.js";
|
|
18
26
|
import "./oracles/index.js";
|
|
19
27
|
import "./transactions/index.js";
|
|
20
|
-
export { AddFeedsArgs, AssetArgs, AssetCap, AssetsArgs, AttachChainsArgs, ChainScope, ChainSummaryArgs, ChainsNamespace, ChangeNamesArgs, ChangeStalenessPeriodsArgs, DEFAULT_DEPLOY_SALT, DeployArgs, DeployContractArgs, DeployPriceFeedArgs, FeedCap, ForbidFeedsArgs, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, GearboxPermissionlessOptions, InstanceOwnerTransactions, InstanceTxsArgs, MarketCap, PermissionlessAnalytics, PermissionlessAsset, PermissionlessAssetDetails, PermissionlessAssetMarket, PermissionlessAssetMarketFeed, PermissionlessAssetQuota, PermissionlessAssetSummary, PermissionlessChain, PermissionlessChainSummary, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessIndexedAssetDetails, PermissionlessIndexedAssetSummary, PermissionlessIndexedPriceFeedDetails, PermissionlessIndexedPriceFeedMarket, PermissionlessMarket, PermissionlessNotAttachedError, PermissionlessOracles, PermissionlessPriceFeedAsset, PermissionlessPriceFeedDetails, PermissionlessPriceFeedMarket, PermissionlessPriceFeedStore, PermissionlessQuotaTotals, PreparedDeployment, PreparedPriceFeedDeployment, PriceChartArgs, PriceFeedArgs, PriceFeedChartPoint, PriceFeedNotInStoreError, PriceFeedPair, PriceFeedPrice, PriceFeedPriceChart, PriceFeedRef, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, deriveEffectiveStalenessPeriod, priceFeedSalt };
|
|
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 };
|
|
@@ -2,7 +2,9 @@ import { PriceFeedData } from "../../../model/opportunities.js";
|
|
|
2
2
|
import { AbstractOffchainNamespace } from "../../../offchain/AbstractOffchainNamespace.js";
|
|
3
3
|
import { GearboxPermissionlessOptions } from "../types.js";
|
|
4
4
|
import { ChainsNamespace } from "../chains/ChainsNamespace.js";
|
|
5
|
-
import {
|
|
5
|
+
import { AssetScore, AssetScoresArgs } from "./score/types.js";
|
|
6
|
+
import { StressReport, StressTestArgs } from "./stress/types.js";
|
|
7
|
+
import { AssetArgs, AssetCap, AssetsArgs, FeedCap, MarketCap, PermissionlessAssetDetails, PermissionlessAssetSummary, PermissionlessMarketSlot, PermissionlessPriceFeedDetails, PermissionlessPriceFeedStore, PriceFeedArgs, PriceFeedDecimals, PriceFeedPrice, PriceFeedRef } from "./types.js";
|
|
6
8
|
//#region src/permissionless/api/oracles/PermissionlessOracles.d.ts
|
|
7
9
|
export declare const GET_PRICES_GAS_LIMIT_BY_CHAIN_ID: Record<number, bigint>;
|
|
8
10
|
export declare const GET_PRICES_CHUNK_SIZE = 200;
|
|
@@ -59,6 +61,25 @@ export declare class PermissionlessOracles extends AbstractOffchainNamespace {
|
|
|
59
61
|
* ```
|
|
60
62
|
**/
|
|
61
63
|
stores({ chainId }?: AssetsArgs): Promise<PermissionlessPriceFeedStore[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Every market slot in force, with the feed holding it and what the market
|
|
66
|
+
* has riding on it.
|
|
67
|
+
*
|
|
68
|
+
* The chain's whole slot table, which {@link feed} and {@link asset} answer
|
|
69
|
+
* one subject's slice of. What reads it this way is analysis over every
|
|
70
|
+
* market at once — which feed holds which slot, and what breaks if it
|
|
71
|
+
* lies — and asking a feed at a time would be a few hundred requests for
|
|
72
|
+
* the same rows.
|
|
73
|
+
*
|
|
74
|
+
* Served as indexed, with nothing folded in: the quotas are in each
|
|
75
|
+
* market's own underlying, as everywhere else, and pricing them is
|
|
76
|
+
* {@link getPrices} over the `underlyingPriceFeed` column.
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* const slots = await permissionless.oracles.slots({ chainId: 1 });
|
|
80
|
+
* ```
|
|
81
|
+
**/
|
|
82
|
+
slots({ chainId }?: AssetsArgs): Promise<PermissionlessMarketSlot[]>;
|
|
62
83
|
/**
|
|
63
84
|
* One feed of the store: what it is, which assets it may price, and which
|
|
64
85
|
* market slots it currently fills.
|
|
@@ -127,6 +148,74 @@ export declare class PermissionlessOracles extends AbstractOffchainNamespace {
|
|
|
127
148
|
* ```
|
|
128
149
|
**/
|
|
129
150
|
assets({ chainId }?: AssetsArgs): Promise<PermissionlessAssetSummary[]>;
|
|
151
|
+
/**
|
|
152
|
+
* What each source of price information can do to the chain's markets if it
|
|
153
|
+
* lies.
|
|
154
|
+
*
|
|
155
|
+
* The feeds under a chain's oracles are partitioned by where their numbers
|
|
156
|
+
* actually come from — Chainlink, Redstone, Pyth, Lido, Curve, each vault's
|
|
157
|
+
* own issuer — on the grounds that a source fails as a whole: whoever
|
|
158
|
+
* trusts one Chainlink aggregator trusts the operator behind all of them.
|
|
159
|
+
* Each group is then made to report a multiple of what it reports now, and
|
|
160
|
+
* every feed the markets read is read again inside that same call.
|
|
161
|
+
*
|
|
162
|
+
* No fork and no harness. The lie is a state override on an `eth_call`, so
|
|
163
|
+
* the composite feeds above it, their bounds and the oracle's own checks all
|
|
164
|
+
* run as deployed, against real state, at the current block — what comes
|
|
165
|
+
* back is what the market would have read.
|
|
166
|
+
*
|
|
167
|
+
* Each slot is scored by how its collateral moved *against its market's own
|
|
168
|
+
* underlying*, because that is what an account is valued by: a group that
|
|
169
|
+
* moves both legs has moved nothing. Beyond the two unbounded ends, each
|
|
170
|
+
* group is bisected for the multiple at which something first breaks, which
|
|
171
|
+
* is the figure that separates a source held in check by a bound from one
|
|
172
|
+
* nothing is checking.
|
|
173
|
+
*
|
|
174
|
+
* ```ts
|
|
175
|
+
* const report = await permissionless.oracles.stressTest({ chainId: 1 });
|
|
176
|
+
* for (const group of report.groups) {
|
|
177
|
+
* console.log(group.verdict);
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* Report {@link StressReport.unidentifiedUsd} alongside anything taken from
|
|
182
|
+
* this: it is the quota priced through a source the partition could only
|
|
183
|
+
* name after its own description, and nothing says what that fails with.
|
|
184
|
+
*
|
|
185
|
+
* Reads the chain directly, so the client has to have attached — see
|
|
186
|
+
* {@link GearboxPermissionless.attach}. Costs one request for the slots and
|
|
187
|
+
* then, per group, one `eth_call` per scenario plus the bisection's; a whole
|
|
188
|
+
* chain comes to a minute or so, and `thresholds: false` cuts it to seconds.
|
|
189
|
+
*
|
|
190
|
+
* @throws When the client has not attached, when the chain has no client
|
|
191
|
+
* configured, or when no PriceFeedCompressor is deployed on it — none of the
|
|
192
|
+
* three leaves anything to simulate.
|
|
193
|
+
**/
|
|
194
|
+
/**
|
|
195
|
+
* Every asset's score out of 10 for how its markets guard its price.
|
|
196
|
+
*
|
|
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
|
+
* rest.
|
|
209
|
+
*
|
|
210
|
+
* ```ts
|
|
211
|
+
* const scores = await permissionless.oracles.assetScores({ chainId: 1 });
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @throws {PermissionlessNotAttachedError} before
|
|
215
|
+
* {@link GearboxPermissionless.attach} has resolved.
|
|
216
|
+
**/
|
|
217
|
+
assetScores({ chainId }?: AssetScoresArgs): Promise<AssetScore[]>;
|
|
218
|
+
stressTest({ chainId, ...options }: StressTestArgs): Promise<StressReport>;
|
|
130
219
|
/**
|
|
131
220
|
* The feeds one feed reads from, recursively.
|
|
132
221
|
*
|
|
@@ -172,6 +261,31 @@ export declare class PermissionlessOracles extends AbstractOffchainNamespace {
|
|
|
172
261
|
* the 8 decimals every Gearbox feed reports.
|
|
173
262
|
**/
|
|
174
263
|
getPrices(feeds: PriceFeedRef[]): Promise<PriceFeedPrice[]>;
|
|
264
|
+
/**
|
|
265
|
+
* The scale each of the named feeds answers in.
|
|
266
|
+
*
|
|
267
|
+
* The companion to {@link getPrices} for the callers whose feeds are not
|
|
268
|
+
* all an oracle's: everything an oracle holds reports 8 decimals, but the
|
|
269
|
+
* feeds under a composite are whatever was deployed, and a rate leg at 18
|
|
270
|
+
* read as 8 is wrong by ten orders of magnitude rather than visibly wrong.
|
|
271
|
+
*
|
|
272
|
+
* Separate from the price because the two keep different company: this is
|
|
273
|
+
* fixed for the life of the contract and worth caching for the session,
|
|
274
|
+
* while a price is re-read whenever the reader asks. One multicall per
|
|
275
|
+
* chain either way.
|
|
276
|
+
*
|
|
277
|
+
* `null` is a feed that would not answer, or one on a chain with no client
|
|
278
|
+
* configured.
|
|
279
|
+
*
|
|
280
|
+
* ```ts
|
|
281
|
+
* const [{ decimals }] = await permissionless.oracles.getDecimals([
|
|
282
|
+
* { chainId: 1, address: feed },
|
|
283
|
+
* ]);
|
|
284
|
+
* ```
|
|
285
|
+
*
|
|
286
|
+
* @returns One entry per feed asked for, in the order asked.
|
|
287
|
+
**/
|
|
288
|
+
getDecimals(feeds: PriceFeedRef[]): Promise<PriceFeedDecimals[]>;
|
|
175
289
|
/**
|
|
176
290
|
* What one chain's feeds value one chain's assets at: `price × totalSupply`
|
|
177
291
|
* per pair.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./score/types.js";
|
|
2
|
+
import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./stress/types.js";
|
|
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";
|
|
2
4
|
import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./PermissionlessOracles.js";
|
|
3
|
-
|
|
5
|
+
import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./score/formula.js";
|
|
6
|
+
import { runAssetScores } from "./score/run.js";
|
|
7
|
+
import "./score/index.js";
|
|
8
|
+
import { TreeNode, classifySources, sourcesByFeed } from "./stress/classify.js";
|
|
9
|
+
import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./stress/override.js";
|
|
10
|
+
import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./stress/run.js";
|
|
11
|
+
import { scoreGroup, slotHarm, verdictOf } from "./stress/score.js";
|
|
12
|
+
import "./stress/index.js";
|
|
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 };
|
|
@@ -129,6 +129,53 @@ export declare const priceFeedMarketSchema: z.ZodObject<{
|
|
|
129
129
|
reserve: z.ZodBoolean;
|
|
130
130
|
priceOracle: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
131
131
|
}, z.core.$loose>;
|
|
132
|
+
/**
|
|
133
|
+
* {@link PermissionlessMarketSlot} — the same slot read from the chain's end,
|
|
134
|
+
* so the feed is a column rather than what was asked for.
|
|
135
|
+
**/
|
|
136
|
+
export declare const marketSlotSchema: z.ZodObject<{
|
|
137
|
+
market: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
138
|
+
marketConfigurator: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
139
|
+
curatorName: z.ZodNullable<z.ZodString>;
|
|
140
|
+
marketName: z.ZodNullable<z.ZodString>;
|
|
141
|
+
underlyingSymbol: z.ZodNullable<z.ZodString>;
|
|
142
|
+
isUnderlying: z.ZodBoolean;
|
|
143
|
+
quotaLimit: z.ZodNullable<z.ZodNumber>;
|
|
144
|
+
quotaUsed: z.ZodNullable<z.ZodNumber>;
|
|
145
|
+
token: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
146
|
+
tokenSymbol: z.ZodNullable<z.ZodString>;
|
|
147
|
+
reserve: z.ZodBoolean;
|
|
148
|
+
priceOracle: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
149
|
+
chainId: z.ZodNumber;
|
|
150
|
+
underlyingToken: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
151
|
+
isShutdown: z.ZodBoolean;
|
|
152
|
+
priceFeed: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
153
|
+
priceFeedName: z.ZodNullable<z.ZodString>;
|
|
154
|
+
priceFeedInStore: z.ZodBoolean;
|
|
155
|
+
underlyingPriceFeed: z.ZodNullable<z.ZodCustom<`0x${string}`, `0x${string}`>>;
|
|
156
|
+
}, z.core.$loose>;
|
|
157
|
+
/** Every slot in scope, across whichever chains the read covered. */
|
|
158
|
+
export declare const marketSlotsSchema: z.ZodArray<z.ZodObject<{
|
|
159
|
+
market: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
160
|
+
marketConfigurator: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
161
|
+
curatorName: z.ZodNullable<z.ZodString>;
|
|
162
|
+
marketName: z.ZodNullable<z.ZodString>;
|
|
163
|
+
underlyingSymbol: z.ZodNullable<z.ZodString>;
|
|
164
|
+
isUnderlying: z.ZodBoolean;
|
|
165
|
+
quotaLimit: z.ZodNullable<z.ZodNumber>;
|
|
166
|
+
quotaUsed: z.ZodNullable<z.ZodNumber>;
|
|
167
|
+
token: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
168
|
+
tokenSymbol: z.ZodNullable<z.ZodString>;
|
|
169
|
+
reserve: z.ZodBoolean;
|
|
170
|
+
priceOracle: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
171
|
+
chainId: z.ZodNumber;
|
|
172
|
+
underlyingToken: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
173
|
+
isShutdown: z.ZodBoolean;
|
|
174
|
+
priceFeed: z.ZodCustom<`0x${string}`, `0x${string}`>;
|
|
175
|
+
priceFeedName: z.ZodNullable<z.ZodString>;
|
|
176
|
+
priceFeedInStore: z.ZodBoolean;
|
|
177
|
+
underlyingPriceFeed: z.ZodNullable<z.ZodCustom<`0x${string}`, `0x${string}`>>;
|
|
178
|
+
}, z.core.$loose>>;
|
|
132
179
|
/** {@link PermissionlessIndexedPriceFeedDetails} */
|
|
133
180
|
export declare const priceFeedDetailsSchema: z.ZodObject<{
|
|
134
181
|
chainId: z.ZodNumber;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GroupImpact } from "./types.js";
|
|
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;
|
|
5
|
+
/**
|
|
6
|
+
* The score of a price that stops answering under the lie.
|
|
7
|
+
*
|
|
8
|
+
* Half marks: nobody can borrow against a wrong number, but nobody can be
|
|
9
|
+
* liquidated either until the feed is replaced.
|
|
10
|
+
*/
|
|
11
|
+
export declare const REVERT_SCORE = 5;
|
|
12
|
+
/**
|
|
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.
|
|
16
|
+
*
|
|
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.
|
|
33
|
+
*/
|
|
34
|
+
export declare function worstOf(impacts: GroupImpact[]): {
|
|
35
|
+
score: number;
|
|
36
|
+
impacts: GroupImpact[];
|
|
37
|
+
worst: GroupImpact | null;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, ScoredFeed } from "./types.js";
|
|
2
|
+
import { REVERT_SCORE, SCORE_MULTIPLIER, factorOf, finalPrice, scoreOfFactor, worstOf } from "./formula.js";
|
|
3
|
+
import { runAssetScores } from "./run.js";
|
|
4
|
+
export { AssetScore, AssetScoresArgs, GroupImpact, MarketScore, REVERT_SCORE, SCORE_MULTIPLIER, ScoredFeed, factorOf, finalPrice, runAssetScores, scoreOfFactor, worstOf };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AssetScore } from "./types.js";
|
|
2
|
+
import { PermissionlessMarketSlot } from "../types.js";
|
|
3
|
+
import { Address, Chain, PublicClient, Transport } from "viem";
|
|
4
|
+
//#region src/permissionless/api/oracles/score/run.d.ts
|
|
5
|
+
/**
|
|
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.
|
|
8
|
+
*
|
|
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.
|
|
14
|
+
**/
|
|
15
|
+
export declare function runAssetScores({ client, chainId, slots, compressor, logger }: {
|
|
16
|
+
client: PublicClient<Transport, Chain>;
|
|
17
|
+
chainId: number;
|
|
18
|
+
slots: PermissionlessMarketSlot[];
|
|
19
|
+
compressor: Address;
|
|
20
|
+
logger?: {
|
|
21
|
+
warn(message: string): void;
|
|
22
|
+
};
|
|
23
|
+
}): Promise<AssetScore[]>;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Address } from "viem";
|
|
2
|
+
//#region src/permissionless/api/oracles/score/types.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* One of a market's two feeds for a token, as the score reads it.
|
|
5
|
+
**/
|
|
6
|
+
export interface ScoredFeed {
|
|
7
|
+
feed: Address;
|
|
8
|
+
/** The store's name for the feed, `null` where it has none. */
|
|
9
|
+
name: string | null;
|
|
10
|
+
/** Gearbox contract type of the feed itself, `""` for one it never deployed. */
|
|
11
|
+
contractType: string;
|
|
12
|
+
/**
|
|
13
|
+
* Every source group beneath the feed: `"Chainlink"`, `"Lido"`, or the
|
|
14
|
+
* description of a source nothing could name.
|
|
15
|
+
*/
|
|
16
|
+
sources: string[];
|
|
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
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* How well one market guards one asset's price, out of 10.
|
|
49
|
+
*
|
|
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.
|
|
54
|
+
**/
|
|
55
|
+
export interface MarketScore {
|
|
56
|
+
market: Address;
|
|
57
|
+
marketName: string | null;
|
|
58
|
+
marketConfigurator: Address;
|
|
59
|
+
curatorName: string | null;
|
|
60
|
+
main: ScoredFeed;
|
|
61
|
+
/** `null` where the market holds no reserve for the asset at all. */
|
|
62
|
+
reserve: ScoredFeed | null;
|
|
63
|
+
/**
|
|
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.
|
|
67
|
+
*/
|
|
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;
|
|
75
|
+
/**
|
|
76
|
+
* The score rests on a source nothing could name.
|
|
77
|
+
*
|
|
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.
|
|
81
|
+
*/
|
|
82
|
+
unverified: boolean;
|
|
83
|
+
score: number;
|
|
84
|
+
/** How the market is set up, where that explains its score. */
|
|
85
|
+
notes: string[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* One asset's score: its worst market's.
|
|
89
|
+
*
|
|
90
|
+
* The minimum, not an average, because the asset is only as well guarded as
|
|
91
|
+
* the most exposed market that accepts it.
|
|
92
|
+
**/
|
|
93
|
+
export interface AssetScore {
|
|
94
|
+
chainId: number;
|
|
95
|
+
asset: Address;
|
|
96
|
+
symbol: string | null;
|
|
97
|
+
/**
|
|
98
|
+
* `null` for an asset no running market accepts as collateral: every
|
|
99
|
+
* market has it switched off, or it is only ever a market's underlying.
|
|
100
|
+
*/
|
|
101
|
+
score: number | null;
|
|
102
|
+
/** Some market's score rests on a source nothing could name. */
|
|
103
|
+
unverified: boolean;
|
|
104
|
+
/** The market the score comes from. */
|
|
105
|
+
worst: MarketScore | null;
|
|
106
|
+
/** Every running market pricing the asset, worst first. */
|
|
107
|
+
markets: MarketScore[];
|
|
108
|
+
}
|
|
109
|
+
export interface AssetScoresArgs {
|
|
110
|
+
/** Omitted scores every chain the backend serves markets for. */
|
|
111
|
+
chainId?: number;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { StressSource } from "./types.js";
|
|
2
|
+
import { Address } from "viem";
|
|
3
|
+
//#region src/permissionless/api/oracles/stress/classify.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Who a price ultimately comes from, worked out from what the chain says
|
|
6
|
+
* about the feed reporting it.
|
|
7
|
+
*
|
|
8
|
+
* There is no registry on chain of which aggregator belongs to whom, so this
|
|
9
|
+
* is a reading of three things the chain does carry: the Gearbox contract
|
|
10
|
+
* type, which names the provider outright for the feeds Gearbox deploys; the
|
|
11
|
+
* `description()` every Chainlink-shaped feed returns; and the interface
|
|
12
|
+
* version, which separates Chainlink's own proxies from the many contracts
|
|
13
|
+
* that imitate them.
|
|
14
|
+
*
|
|
15
|
+
* Every rule is open to inspection and every unmatched feed is reported as
|
|
16
|
+
* unmatched, because the alternative — guessing from a symbol, or sweeping
|
|
17
|
+
* the remainder into one bucket — would understate exactly the part of the
|
|
18
|
+
* answer a reader should distrust.
|
|
19
|
+
**/
|
|
20
|
+
/** One node of an oracle's feed tree, as the compressor reports it. */
|
|
21
|
+
export interface TreeNode {
|
|
22
|
+
address: Address;
|
|
23
|
+
/** Gearbox contract type, `""` for a contract it never deployed. */
|
|
24
|
+
contractType: string;
|
|
25
|
+
/** `AggregatorV3Interface.version()`, or 0 where there is none. */
|
|
26
|
+
version: number;
|
|
27
|
+
description: string;
|
|
28
|
+
/** The feeds it reads, lowercased. Empty makes it a leaf. */
|
|
29
|
+
underlyingFeeds: Address[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Every source behind a chain's feed trees.
|
|
33
|
+
*
|
|
34
|
+
* A source per contract, not per feed: two of an oracle's trees carrying the
|
|
35
|
+
* same aggregator is one source read twice, and the test varies it once.
|
|
36
|
+
*
|
|
37
|
+
* `rateTargets` says which contract each rate-bearing feed reads its rate
|
|
38
|
+
* from — the feed's own `lpContract`, read separately because the compressor
|
|
39
|
+
* does not report it. A feed missing from it is left out with a reason, since
|
|
40
|
+
* perturbing the whole feed instead would charge its protocol with a failure
|
|
41
|
+
* of whatever prices the thing the rate is denominated in.
|
|
42
|
+
**/
|
|
43
|
+
export declare function classifySources(chainId: number, nodes: TreeNode[], rateTargets: Map<Address, Address>, baselineOf: (target: Address) => bigint | null): {
|
|
44
|
+
sources: StressSource[];
|
|
45
|
+
unresolved: TreeNode[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The sources each of the given feeds is priced through, following the trees
|
|
49
|
+
* down from every one of them.
|
|
50
|
+
*
|
|
51
|
+
* A feed is reached by a source when the source sits anywhere beneath it, at
|
|
52
|
+
* any depth: a market reading a composite of a composite of a Chainlink
|
|
53
|
+
* aggregator is a market Chainlink can move. Memoised over the whole forest,
|
|
54
|
+
* so a node shared by twenty trees is walked once.
|
|
55
|
+
**/
|
|
56
|
+
export declare function sourcesByFeed(roots: Address[], nodes: Map<Address, TreeNode>, sources: StressSource[]): Map<Address, Set<Address>>;
|
|
57
|
+
//#endregion
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BreakingPoint, FeedOutcome, GroupReport, GroupScenarioOutcome, SlotOutcome, StressReport, StressScenario, StressSource, StressSourceKind, StressTestArgs } from "./types.js";
|
|
2
|
+
import { TreeNode, classifySources, sourcesByFeed } from "./classify.js";
|
|
3
|
+
import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./override.js";
|
|
4
|
+
import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./run.js";
|
|
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, readUnder, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { StressSource } from "./types.js";
|
|
2
|
+
import { Hex, StateOverride } from "viem";
|
|
3
|
+
//#region src/permissionless/api/oracles/stress/override.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Runtime code answering every call with the same five words.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately selector-blind. A price feed is read through more than
|
|
8
|
+
* `latestRoundData` — `decimals`, `version`, sometimes `latestAnswer` — and
|
|
9
|
+
* dispatching on each would mean knowing which of them every caller in a tree
|
|
10
|
+
* reaches for. Five words decode as the round data; a caller expecting one
|
|
11
|
+
* word reads the first, which is why `roundId` is set to the 8 decimals every
|
|
12
|
+
* Gearbox feed reports rather than to a round number.
|
|
13
|
+
*
|
|
14
|
+
* `answeredInRound` is the same 8, so the `answeredInRound >= roundId` check
|
|
15
|
+
* the older consumers make passes.
|
|
16
|
+
**/
|
|
17
|
+
export declare function roundDataStub(answer: bigint, updatedAt: bigint): Hex;
|
|
18
|
+
/**
|
|
19
|
+
* Runtime code answering every call with one word.
|
|
20
|
+
*
|
|
21
|
+
* What an exchange rate getter returns, whichever of them the feed calls:
|
|
22
|
+
* `stEthPerToken`, `convertToAssets`, `get_virtual_price`, `pricePerShare`
|
|
23
|
+
* and `getRate` all answer a single `uint256`, so the stub does not need to
|
|
24
|
+
* know which.
|
|
25
|
+
**/
|
|
26
|
+
export declare function rateStub(rate: bigint): Hex;
|
|
27
|
+
/**
|
|
28
|
+
* The two unbounded ends, as far as the types involved can carry them.
|
|
29
|
+
*
|
|
30
|
+
* "Infinitely large" has to stop somewhere: a price is an `int256` and the
|
|
31
|
+
* arithmetic above it multiplies, so a genuinely enormous answer overflows
|
|
32
|
+
* inside the composite feed and reverts — which would be scored as the
|
|
33
|
+
* oracle resisting, when all that happened is that the test asked something
|
|
34
|
+
* the type cannot hold. `1e12` times the current answer is past any bound,
|
|
35
|
+
* any sanity check and any collateral ratio, and still multiplies.
|
|
36
|
+
*/
|
|
37
|
+
export declare const UNBOUNDED_UP = 1000000000000;
|
|
38
|
+
/** The other end: a thousandth of a trillionth, which is as near zero. */
|
|
39
|
+
export declare const UNBOUNDED_DOWN = 1e-12;
|
|
40
|
+
/**
|
|
41
|
+
* What a source should answer under a multiplier, given what it answers now.
|
|
42
|
+
*
|
|
43
|
+
* Floored at one rather than at zero: a multiplier small enough to round the
|
|
44
|
+
* baseline away would be testing a feed reporting nothing, which is the revert
|
|
45
|
+
* case and is a different scenario with a different meaning.
|
|
46
|
+
*/
|
|
47
|
+
export declare function perturb(baseline: bigint, multiplier: number): bigint;
|
|
48
|
+
/**
|
|
49
|
+
* The state override that makes every source of a group answer a multiple of
|
|
50
|
+
* what it answers now.
|
|
51
|
+
*
|
|
52
|
+
* One override per contract, so a group whose sources share an aggregator
|
|
53
|
+
* perturbs it once. Sources with nothing behind them and sources that are not
|
|
54
|
+
* answering are both left out, for the reasons on
|
|
55
|
+
* {@link StressSource.perturbable} and {@link StressSource.baseline} — an
|
|
56
|
+
* empty result is a group there is nothing to simulate about.
|
|
57
|
+
**/
|
|
58
|
+
export declare function groupOverride(sources: StressSource[], multiplier: number, updatedAt: bigint): StateOverride;
|
|
59
|
+
//#endregion
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { StressReport, StressScenario, StressSource } from "./types.js";
|
|
2
|
+
import { PermissionlessMarketSlot } from "../types.js";
|
|
3
|
+
import { TreeNode } from "./classify.js";
|
|
4
|
+
import { Address, Chain, PublicClient, Transport } from "viem";
|
|
5
|
+
//#region src/permissionless/api/oracles/stress/run.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The two ends the question is asked at, which is what "infinitely large and
|
|
8
|
+
* infinitely small" comes to once a type has to hold it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_SCENARIOS: StressScenario[];
|
|
11
|
+
/**
|
|
12
|
+
* The Gearbox LP feeds: a protocol's exchange rate, clamped to a window the
|
|
13
|
+
* feed holds, times the price of what the rate is denominated in.
|
|
14
|
+
*/
|
|
15
|
+
export declare const RATE_FEED_TYPE: RegExp;
|
|
16
|
+
/**
|
|
17
|
+
* The feed trees of every oracle the chain's markets use, flattened.
|
|
18
|
+
*
|
|
19
|
+
* One call per oracle and not per feed: the compressor answers an oracle's
|
|
20
|
+
* entire tree at once, with every node's type, description, children and
|
|
21
|
+
* current answer, which is the whole of what this test needs off the chain.
|
|
22
|
+
* A node carried by several oracles is one node — the trees overlap heavily,
|
|
23
|
+
* since the aggregators under them are the same handful.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readForest(client: PublicClient<Transport, Chain>, compressor: Address, oracles: Address[]): Promise<{
|
|
26
|
+
nodes: Map<Address, TreeNode>;
|
|
27
|
+
answers: Map<Address, bigint | null>;
|
|
28
|
+
unreadableOracles: Address[];
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Where each rate-bearing feed reads its rate, and what it will accept.
|
|
32
|
+
*
|
|
33
|
+
* The baseline a rate source is perturbed from is the feed's own `lowerBound`
|
|
34
|
+
* and not the rate itself. Reading the rate would mean knowing which getter
|
|
35
|
+
* each protocol publishes it under — `stEthPerToken`, `convertToAssets`,
|
|
36
|
+
* `get_virtual_price`, `pricePerShare` — and in exactly which units, which is
|
|
37
|
+
* a table that goes stale. The bound is within the feed's own accepted window
|
|
38
|
+
* of the live rate by construction, so it is the same figure to within that
|
|
39
|
+
* window, which is two percent or so. That is immaterial against the
|
|
40
|
+
* multipliers the ends of the sweep use, and at the fine end of the threshold
|
|
41
|
+
* search the bound *is* the answer: an LP feed clamps the rate it accepts, so
|
|
42
|
+
* where it breaks is where its bound is.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readRateTargets(client: PublicClient<Transport, Chain>, feeds: Address[]): Promise<{
|
|
45
|
+
targets: Map<Address, Address>;
|
|
46
|
+
baselines: Map<Address, bigint>;
|
|
47
|
+
bounds: Map<Address, {
|
|
48
|
+
lower: bigint;
|
|
49
|
+
upper: bigint;
|
|
50
|
+
}>;
|
|
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>>;
|
|
68
|
+
/**
|
|
69
|
+
* Every source behind one chain's markets, put through the sweep a group at a
|
|
70
|
+
* time.
|
|
71
|
+
*
|
|
72
|
+
* Takes the slots as read, so the caller decides whether that read came from
|
|
73
|
+
* the backend or from somewhere else, and takes a client rather than making
|
|
74
|
+
* one, so the whole thing is testable against a stub.
|
|
75
|
+
**/
|
|
76
|
+
export declare function runStressTest({ client, chainId, slots, compressor, scenarios, thresholds, groups: only, logger }: {
|
|
77
|
+
client: PublicClient<Transport, Chain>;
|
|
78
|
+
chainId: number;
|
|
79
|
+
slots: PermissionlessMarketSlot[];
|
|
80
|
+
compressor: Address;
|
|
81
|
+
scenarios?: StressScenario[];
|
|
82
|
+
thresholds?: boolean;
|
|
83
|
+
groups?: string[];
|
|
84
|
+
logger?: {
|
|
85
|
+
warn(message: string): void;
|
|
86
|
+
debug?(message: string): void;
|
|
87
|
+
};
|
|
88
|
+
}): Promise<StressReport>;
|
|
89
|
+
//#endregion
|