@gearbox-protocol/sdk 18.2.0-next.15 → 18.2.0-next.16
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/oracles/PermissionlessOracles.js +53 -7
- package/dist/cjs/permissionless/api/oracles/onchain-feed.js +25 -0
- package/dist/cjs/permissionless/api/transactions/InstanceOwnerTransactions.js +14 -3
- package/dist/cjs/permissionless/api/transactions/lp-price-feeds.js +10 -4
- package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +55 -9
- package/dist/esm/permissionless/api/oracles/onchain-feed.js +24 -0
- package/dist/esm/permissionless/api/transactions/InstanceOwnerTransactions.js +14 -3
- package/dist/esm/permissionless/api/transactions/lp-price-feeds.js +10 -4
- package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +8 -2
- package/dist/types/permissionless/api/oracles/onchain-feed.d.ts +15 -0
- package/dist/types/permissionless/api/oracles/types.d.ts +12 -3
- package/dist/types/permissionless/api/transactions/lp-price-feeds.d.ts +9 -1
- package/package.json +1 -1
|
@@ -4,11 +4,13 @@ const require_onchain_constants_address_provider = require("../../../onchain/con
|
|
|
4
4
|
const require_model_opportunities_schema = require("../../../model/opportunities.schema.js");
|
|
5
5
|
const require_abi_compressors_priceFeedCompressor = require("../../../abi/compressors/priceFeedCompressor.js");
|
|
6
6
|
require("../../../onchain/index.js");
|
|
7
|
+
const require_offchain_errors_OffchainStatusError = require("../../../offchain/errors/OffchainStatusError.js");
|
|
7
8
|
const require_offchain_AbstractOffchainNamespace = require("../../../offchain/AbstractOffchainNamespace.js");
|
|
8
9
|
require("../../deployment/addresses.js");
|
|
9
10
|
const require_permissionless_bindings_address_provider = require("../../bindings/address-provider.js");
|
|
10
11
|
const require_permissionless_utils_price_update_get_prices = require("../../utils/price-update/get-prices.js");
|
|
11
12
|
const require_permissionless_api_oracles_enrich = require("./enrich.js");
|
|
13
|
+
const require_permissionless_api_oracles_onchain_feed = require("./onchain-feed.js");
|
|
12
14
|
const require_permissionless_api_oracles_schemas = require("./schemas.js");
|
|
13
15
|
const require_permissionless_api_oracles_stress_run = require("./stress/run.js");
|
|
14
16
|
const require_permissionless_api_oracles_score_run = require("./score/run.js");
|
|
@@ -28,6 +30,10 @@ function slotKey(oracle, token, reserve) {
|
|
|
28
30
|
function describe(e) {
|
|
29
31
|
return e instanceof Error ? e.message : String(e);
|
|
30
32
|
}
|
|
33
|
+
/** The backend answered that it has no such feed. */
|
|
34
|
+
function isNotFound(e) {
|
|
35
|
+
return e instanceof require_offchain_errors_OffchainStatusError.OffchainStatusError && e.status === 404;
|
|
36
|
+
}
|
|
31
37
|
/**
|
|
32
38
|
* The price feeds of one chain: the PriceFeedStore as the backend has indexed
|
|
33
39
|
* it, and the prices behind it.
|
|
@@ -139,25 +145,65 @@ var PermissionlessOracles = class extends require_offchain_AbstractOffchainNames
|
|
|
139
145
|
* oracle holds for the slot, read off the chain here — see
|
|
140
146
|
* {@link PermissionlessPriceFeedMarket.marketStalenessPeriod}.
|
|
141
147
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
148
|
+
* A feed the store does not carry is read off the chain instead, through
|
|
149
|
+
* the PriceFeedCompressor, and answered with `inStore: false`: its type,
|
|
150
|
+
* version and description are the chain's, while what only the store knows
|
|
151
|
+
* — who deployed it, its staleness period, the assets it may price and the
|
|
152
|
+
* markets it fills — is answered as unknown or empty.
|
|
153
|
+
*
|
|
154
|
+
* @throws When neither the store nor the chain knows the feed. A private
|
|
155
|
+
* draft is not here — it lives in whichever app wrote it.
|
|
144
156
|
**/
|
|
145
157
|
async feed({ chainId, address }) {
|
|
146
158
|
const feed = address.toLowerCase();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
let indexed;
|
|
160
|
+
try {
|
|
161
|
+
indexed = await this.getData({
|
|
162
|
+
path: `/pfs/${chainId}/price-feed/${address}`,
|
|
163
|
+
schema: require_permissionless_api_oracles_schemas.priceFeedDetailsSchema
|
|
164
|
+
});
|
|
165
|
+
} catch (e) {
|
|
166
|
+
const node = isNotFound(e) ? await this.#onchainNode(chainId, feed) : void 0;
|
|
167
|
+
if (!node) throw e;
|
|
168
|
+
return {
|
|
169
|
+
chainId,
|
|
170
|
+
priceFeed: require_permissionless_api_oracles_onchain_feed.onchainPriceFeed(node),
|
|
171
|
+
assets: [],
|
|
172
|
+
markets: [],
|
|
173
|
+
inStore: false
|
|
174
|
+
};
|
|
175
|
+
}
|
|
151
176
|
const periods = await this.#stalenessPeriods(chainId, feed, indexed.markets);
|
|
152
177
|
return {
|
|
153
178
|
...indexed,
|
|
154
179
|
markets: indexed.markets.map((market) => ({
|
|
155
180
|
...market,
|
|
156
181
|
marketStalenessPeriod: periods.get(slotKey(market.priceOracle, market.token, market.reserve)) ?? null
|
|
157
|
-
}))
|
|
182
|
+
})),
|
|
183
|
+
inStore: true
|
|
158
184
|
};
|
|
159
185
|
}
|
|
160
186
|
/**
|
|
187
|
+
* One feed as the PriceFeedCompressor reads it, or nothing when the chain
|
|
188
|
+
* cannot be read or the compressor does not know the feed.
|
|
189
|
+
*
|
|
190
|
+
* Failures are warnings and not errors: this only ever stands in for a
|
|
191
|
+
* store that did not have the feed, and the caller reports that miss
|
|
192
|
+
* rather than whatever kept the chain from answering in its place.
|
|
193
|
+
*/
|
|
194
|
+
async #onchainNode(chainId, feed) {
|
|
195
|
+
try {
|
|
196
|
+
const client = this.#chains.client(chainId);
|
|
197
|
+
const compressor = await new require_permissionless_bindings_address_provider.AddressProviderContract("0xF7f0a609BfAb9a0A98786951ef10e5FE26cC1E38", client).getLatestVersionAddress(require_onchain_constants_address_provider.AP_PRICE_FEED_COMPRESSOR);
|
|
198
|
+
if (compressor === viem.zeroAddress) return;
|
|
199
|
+
const { nodes } = await require_permissionless_api_oracles_stress_run.readFeedTrees(client, compressor, [feed]);
|
|
200
|
+
return nodes.get(feed);
|
|
201
|
+
} catch (e) {
|
|
202
|
+
this.logger?.warn(`could not read price feed ${feed} off chain ${chainId}: ${describe(e)}`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
161
207
|
* Staleness period each market's own oracle holds for the feed's slots,
|
|
162
208
|
* keyed by {@link slotKey}.
|
|
163
209
|
*
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let viem = require("viem");
|
|
3
|
+
//#region src/permissionless/api/oracles/onchain-feed.ts
|
|
4
|
+
/**
|
|
5
|
+
* What the store would say about a feed, from what the chain says instead.
|
|
6
|
+
*
|
|
7
|
+
* Two fields are the store's alone and are answered as unknown rather than
|
|
8
|
+
* guessed: who deployed it is a store row, so it is the zero address, and the
|
|
9
|
+
* staleness period is a store setting a feed that was never added does not
|
|
10
|
+
* have, so it is 0. The compressor reports `PRICE_FEED::EXTERNAL` for a
|
|
11
|
+
* contract it cannot otherwise place; an empty type is the same thing said
|
|
12
|
+
* less clearly.
|
|
13
|
+
*/
|
|
14
|
+
function onchainPriceFeed(node) {
|
|
15
|
+
return {
|
|
16
|
+
address: (0, viem.getAddress)(node.address),
|
|
17
|
+
contractType: node.contractType.trim() || "PRICE_FEED::EXTERNAL",
|
|
18
|
+
version: node.version,
|
|
19
|
+
deployedBy: viem.zeroAddress,
|
|
20
|
+
stalenessPeriod: 0,
|
|
21
|
+
name: node.description.trim()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
exports.onchainPriceFeed = onchainPriceFeed;
|
|
@@ -137,10 +137,21 @@ var InstanceOwnerTransactions = class {
|
|
|
137
137
|
const batch = await this.#batchContext(chainId);
|
|
138
138
|
const pfCompressor = await this.#priceFeedCompressor(batch.client);
|
|
139
139
|
const known = await batch.store.getKnownPriceFeeds();
|
|
140
|
+
const isKnown = new Set(known.map(lower));
|
|
140
141
|
const feeds = await require_permissionless_api_transactions_lp_price_feeds.getFeedsToUpdate(batch.client, pfCompressor, known);
|
|
142
|
+
const addTxs = [];
|
|
143
|
+
const added = [];
|
|
141
144
|
const configureTxs = [];
|
|
142
|
-
for (const feed of feeds)
|
|
143
|
-
|
|
145
|
+
for (const feed of feeds) {
|
|
146
|
+
const boundTxs = await require_permissionless_api_transactions_lp_price_feeds.updateBounds(batch.client, feed.address);
|
|
147
|
+
if (boundTxs.length === 0) continue;
|
|
148
|
+
if (!isKnown.has(feed.address)) {
|
|
149
|
+
addTxs.push(batch.store.addPriceFeedTx(feed.address, 0, feed.description));
|
|
150
|
+
added.push(feed.address);
|
|
151
|
+
}
|
|
152
|
+
configureTxs.push(...boundTxs);
|
|
153
|
+
}
|
|
154
|
+
const txs = [...addTxs, batch.store.configurePriceFeeds(configureTxs.map((tx) => ({
|
|
144
155
|
target: tx.to,
|
|
145
156
|
callData: tx.callData
|
|
146
157
|
})))];
|
|
@@ -149,7 +160,7 @@ var InstanceOwnerTransactions = class {
|
|
|
149
160
|
author: owner,
|
|
150
161
|
name: "update-bounds",
|
|
151
162
|
txs,
|
|
152
|
-
touchedFeeds:
|
|
163
|
+
touchedFeeds: added
|
|
153
164
|
});
|
|
154
165
|
}
|
|
155
166
|
/**
|
|
@@ -52,9 +52,12 @@ async function updateBounds(client, address) {
|
|
|
52
52
|
/**
|
|
53
53
|
* Of the whole feed tree behind `feeds`, the ones that are LP feeds: the
|
|
54
54
|
* probe keeps whatever answers `getLPExchangeRate`.
|
|
55
|
+
*
|
|
56
|
+
* The trees reach past `feeds` themselves, so an LP feed read inside one of
|
|
57
|
+
* them is among the results whether or not it is one of `feeds`.
|
|
55
58
|
**/
|
|
56
59
|
async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
57
|
-
const updateableFeeds = /* @__PURE__ */ new
|
|
60
|
+
const updateableFeeds = /* @__PURE__ */ new Map();
|
|
58
61
|
const { data } = await client.call({
|
|
59
62
|
to: pfCompressor,
|
|
60
63
|
data: (0, viem.encodeFunctionData)({
|
|
@@ -69,8 +72,8 @@ async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
|
69
72
|
functionName: "loadPriceFeedTree",
|
|
70
73
|
data: data ?? "0x"
|
|
71
74
|
});
|
|
72
|
-
for (const tree of trees) updateableFeeds.
|
|
73
|
-
const candidates = Array.from(updateableFeeds);
|
|
75
|
+
for (const tree of trees) updateableFeeds.set(tree.baseParams.addr.toLowerCase(), tree.description);
|
|
76
|
+
const candidates = Array.from(updateableFeeds.keys());
|
|
74
77
|
const { results } = await require_onchain_utils_viem_simulateMulticall.simulateMulticall(client, {
|
|
75
78
|
contracts: candidates.map((feed) => ({
|
|
76
79
|
address: feed,
|
|
@@ -79,7 +82,10 @@ async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
|
79
82
|
})),
|
|
80
83
|
allowFailure: true
|
|
81
84
|
});
|
|
82
|
-
return candidates.filter((_, index) => results[index]?.status === "success")
|
|
85
|
+
return candidates.filter((_, index) => results[index]?.status === "success").map((address) => ({
|
|
86
|
+
address,
|
|
87
|
+
description: updateableFeeds.get(address) ?? ""
|
|
88
|
+
}));
|
|
83
89
|
}
|
|
84
90
|
//#endregion
|
|
85
91
|
exports.getFeedsToUpdate = getFeedsToUpdate;
|
|
@@ -3,15 +3,17 @@ import { AP_PRICE_FEED_COMPRESSOR } from "../../../onchain/constants/address-pro
|
|
|
3
3
|
import { priceFeedDataSchema } from "../../../model/opportunities.schema.js";
|
|
4
4
|
import { priceFeedCompressorAbi } from "../../../abi/compressors/priceFeedCompressor.js";
|
|
5
5
|
import "../../../onchain/index.js";
|
|
6
|
+
import { OffchainStatusError } from "../../../offchain/errors/OffchainStatusError.js";
|
|
6
7
|
import { AbstractOffchainNamespace } from "../../../offchain/AbstractOffchainNamespace.js";
|
|
7
8
|
import "../../deployment/addresses.js";
|
|
8
9
|
import { AddressProviderContract } from "../../bindings/address-provider.js";
|
|
9
10
|
import { getFeedDecimals, getLatestPrices, getPrices } from "../../utils/price-update/get-prices.js";
|
|
10
11
|
import { byCentrality, feedKey, pricesByFeed, toCap, totalQuotas } from "./enrich.js";
|
|
12
|
+
import { onchainPriceFeed } from "./onchain-feed.js";
|
|
11
13
|
import { assetDetailsSchema, assetSummariesSchema, marketSlotsSchema, priceFeedDetailsSchema, priceFeedStoresSchema } from "./schemas.js";
|
|
12
|
-
import { runStressTest } from "./stress/run.js";
|
|
14
|
+
import { readFeedTrees, runStressTest } from "./stress/run.js";
|
|
13
15
|
import { runAssetScores, runFeedPairScore } from "./score/run.js";
|
|
14
|
-
import { erc20Abi } from "viem";
|
|
16
|
+
import { erc20Abi, zeroAddress } from "viem";
|
|
15
17
|
//#region src/permissionless/api/oracles/PermissionlessOracles.ts
|
|
16
18
|
const GET_PRICES_GAS_LIMIT_BY_CHAIN_ID = { 143: 100000000n };
|
|
17
19
|
const GET_PRICES_CHUNK_SIZE = 200;
|
|
@@ -27,6 +29,10 @@ function slotKey(oracle, token, reserve) {
|
|
|
27
29
|
function describe(e) {
|
|
28
30
|
return e instanceof Error ? e.message : String(e);
|
|
29
31
|
}
|
|
32
|
+
/** The backend answered that it has no such feed. */
|
|
33
|
+
function isNotFound(e) {
|
|
34
|
+
return e instanceof OffchainStatusError && e.status === 404;
|
|
35
|
+
}
|
|
30
36
|
/**
|
|
31
37
|
* The price feeds of one chain: the PriceFeedStore as the backend has indexed
|
|
32
38
|
* it, and the prices behind it.
|
|
@@ -138,25 +144,65 @@ var PermissionlessOracles = class extends AbstractOffchainNamespace {
|
|
|
138
144
|
* oracle holds for the slot, read off the chain here — see
|
|
139
145
|
* {@link PermissionlessPriceFeedMarket.marketStalenessPeriod}.
|
|
140
146
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
147
|
+
* A feed the store does not carry is read off the chain instead, through
|
|
148
|
+
* the PriceFeedCompressor, and answered with `inStore: false`: its type,
|
|
149
|
+
* version and description are the chain's, while what only the store knows
|
|
150
|
+
* — who deployed it, its staleness period, the assets it may price and the
|
|
151
|
+
* markets it fills — is answered as unknown or empty.
|
|
152
|
+
*
|
|
153
|
+
* @throws When neither the store nor the chain knows the feed. A private
|
|
154
|
+
* draft is not here — it lives in whichever app wrote it.
|
|
143
155
|
**/
|
|
144
156
|
async feed({ chainId, address }) {
|
|
145
157
|
const feed = address.toLowerCase();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
let indexed;
|
|
159
|
+
try {
|
|
160
|
+
indexed = await this.getData({
|
|
161
|
+
path: `/pfs/${chainId}/price-feed/${address}`,
|
|
162
|
+
schema: priceFeedDetailsSchema
|
|
163
|
+
});
|
|
164
|
+
} catch (e) {
|
|
165
|
+
const node = isNotFound(e) ? await this.#onchainNode(chainId, feed) : void 0;
|
|
166
|
+
if (!node) throw e;
|
|
167
|
+
return {
|
|
168
|
+
chainId,
|
|
169
|
+
priceFeed: onchainPriceFeed(node),
|
|
170
|
+
assets: [],
|
|
171
|
+
markets: [],
|
|
172
|
+
inStore: false
|
|
173
|
+
};
|
|
174
|
+
}
|
|
150
175
|
const periods = await this.#stalenessPeriods(chainId, feed, indexed.markets);
|
|
151
176
|
return {
|
|
152
177
|
...indexed,
|
|
153
178
|
markets: indexed.markets.map((market) => ({
|
|
154
179
|
...market,
|
|
155
180
|
marketStalenessPeriod: periods.get(slotKey(market.priceOracle, market.token, market.reserve)) ?? null
|
|
156
|
-
}))
|
|
181
|
+
})),
|
|
182
|
+
inStore: true
|
|
157
183
|
};
|
|
158
184
|
}
|
|
159
185
|
/**
|
|
186
|
+
* One feed as the PriceFeedCompressor reads it, or nothing when the chain
|
|
187
|
+
* cannot be read or the compressor does not know the feed.
|
|
188
|
+
*
|
|
189
|
+
* Failures are warnings and not errors: this only ever stands in for a
|
|
190
|
+
* store that did not have the feed, and the caller reports that miss
|
|
191
|
+
* rather than whatever kept the chain from answering in its place.
|
|
192
|
+
*/
|
|
193
|
+
async #onchainNode(chainId, feed) {
|
|
194
|
+
try {
|
|
195
|
+
const client = this.#chains.client(chainId);
|
|
196
|
+
const compressor = await new AddressProviderContract("0xF7f0a609BfAb9a0A98786951ef10e5FE26cC1E38", client).getLatestVersionAddress(AP_PRICE_FEED_COMPRESSOR);
|
|
197
|
+
if (compressor === zeroAddress) return;
|
|
198
|
+
const { nodes } = await readFeedTrees(client, compressor, [feed]);
|
|
199
|
+
return nodes.get(feed);
|
|
200
|
+
} catch (e) {
|
|
201
|
+
this.logger?.warn(`could not read price feed ${feed} off chain ${chainId}: ${describe(e)}`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
160
206
|
* Staleness period each market's own oracle holds for the feed's slots,
|
|
161
207
|
* keyed by {@link slotKey}.
|
|
162
208
|
*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getAddress, zeroAddress } from "viem";
|
|
2
|
+
//#region src/permissionless/api/oracles/onchain-feed.ts
|
|
3
|
+
/**
|
|
4
|
+
* What the store would say about a feed, from what the chain says instead.
|
|
5
|
+
*
|
|
6
|
+
* Two fields are the store's alone and are answered as unknown rather than
|
|
7
|
+
* guessed: who deployed it is a store row, so it is the zero address, and the
|
|
8
|
+
* staleness period is a store setting a feed that was never added does not
|
|
9
|
+
* have, so it is 0. The compressor reports `PRICE_FEED::EXTERNAL` for a
|
|
10
|
+
* contract it cannot otherwise place; an empty type is the same thing said
|
|
11
|
+
* less clearly.
|
|
12
|
+
*/
|
|
13
|
+
function onchainPriceFeed(node) {
|
|
14
|
+
return {
|
|
15
|
+
address: getAddress(node.address),
|
|
16
|
+
contractType: node.contractType.trim() || "PRICE_FEED::EXTERNAL",
|
|
17
|
+
version: node.version,
|
|
18
|
+
deployedBy: zeroAddress,
|
|
19
|
+
stalenessPeriod: 0,
|
|
20
|
+
name: node.description.trim()
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { onchainPriceFeed };
|
|
@@ -136,10 +136,21 @@ var InstanceOwnerTransactions = class {
|
|
|
136
136
|
const batch = await this.#batchContext(chainId);
|
|
137
137
|
const pfCompressor = await this.#priceFeedCompressor(batch.client);
|
|
138
138
|
const known = await batch.store.getKnownPriceFeeds();
|
|
139
|
+
const isKnown = new Set(known.map(lower));
|
|
139
140
|
const feeds = await getFeedsToUpdate(batch.client, pfCompressor, known);
|
|
141
|
+
const addTxs = [];
|
|
142
|
+
const added = [];
|
|
140
143
|
const configureTxs = [];
|
|
141
|
-
for (const feed of feeds)
|
|
142
|
-
|
|
144
|
+
for (const feed of feeds) {
|
|
145
|
+
const boundTxs = await updateBounds(batch.client, feed.address);
|
|
146
|
+
if (boundTxs.length === 0) continue;
|
|
147
|
+
if (!isKnown.has(feed.address)) {
|
|
148
|
+
addTxs.push(batch.store.addPriceFeedTx(feed.address, 0, feed.description));
|
|
149
|
+
added.push(feed.address);
|
|
150
|
+
}
|
|
151
|
+
configureTxs.push(...boundTxs);
|
|
152
|
+
}
|
|
153
|
+
const txs = [...addTxs, batch.store.configurePriceFeeds(configureTxs.map((tx) => ({
|
|
143
154
|
target: tx.to,
|
|
144
155
|
callData: tx.callData
|
|
145
156
|
})))];
|
|
@@ -148,7 +159,7 @@ var InstanceOwnerTransactions = class {
|
|
|
148
159
|
author: owner,
|
|
149
160
|
name: "update-bounds",
|
|
150
161
|
txs,
|
|
151
|
-
touchedFeeds:
|
|
162
|
+
touchedFeeds: added
|
|
152
163
|
});
|
|
153
164
|
}
|
|
154
165
|
/**
|
|
@@ -51,9 +51,12 @@ async function updateBounds(client, address) {
|
|
|
51
51
|
/**
|
|
52
52
|
* Of the whole feed tree behind `feeds`, the ones that are LP feeds: the
|
|
53
53
|
* probe keeps whatever answers `getLPExchangeRate`.
|
|
54
|
+
*
|
|
55
|
+
* The trees reach past `feeds` themselves, so an LP feed read inside one of
|
|
56
|
+
* them is among the results whether or not it is one of `feeds`.
|
|
54
57
|
**/
|
|
55
58
|
async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
56
|
-
const updateableFeeds = /* @__PURE__ */ new
|
|
59
|
+
const updateableFeeds = /* @__PURE__ */ new Map();
|
|
57
60
|
const { data } = await client.call({
|
|
58
61
|
to: pfCompressor,
|
|
59
62
|
data: encodeFunctionData({
|
|
@@ -68,8 +71,8 @@ async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
|
68
71
|
functionName: "loadPriceFeedTree",
|
|
69
72
|
data: data ?? "0x"
|
|
70
73
|
});
|
|
71
|
-
for (const tree of trees) updateableFeeds.
|
|
72
|
-
const candidates = Array.from(updateableFeeds);
|
|
74
|
+
for (const tree of trees) updateableFeeds.set(tree.baseParams.addr.toLowerCase(), tree.description);
|
|
75
|
+
const candidates = Array.from(updateableFeeds.keys());
|
|
73
76
|
const { results } = await simulateMulticall(client, {
|
|
74
77
|
contracts: candidates.map((feed) => ({
|
|
75
78
|
address: feed,
|
|
@@ -78,7 +81,10 @@ async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
|
78
81
|
})),
|
|
79
82
|
allowFailure: true
|
|
80
83
|
});
|
|
81
|
-
return candidates.filter((_, index) => results[index]?.status === "success")
|
|
84
|
+
return candidates.filter((_, index) => results[index]?.status === "success").map((address) => ({
|
|
85
|
+
address,
|
|
86
|
+
description: updateableFeeds.get(address) ?? ""
|
|
87
|
+
}));
|
|
82
88
|
}
|
|
83
89
|
//#endregion
|
|
84
90
|
export { getFeedsToUpdate, updateBounds };
|
|
@@ -99,8 +99,14 @@ export declare class PermissionlessOracles extends AbstractOffchainNamespace {
|
|
|
99
99
|
* oracle holds for the slot, read off the chain here — see
|
|
100
100
|
* {@link PermissionlessPriceFeedMarket.marketStalenessPeriod}.
|
|
101
101
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
102
|
+
* A feed the store does not carry is read off the chain instead, through
|
|
103
|
+
* the PriceFeedCompressor, and answered with `inStore: false`: its type,
|
|
104
|
+
* version and description are the chain's, while what only the store knows
|
|
105
|
+
* — who deployed it, its staleness period, the assets it may price and the
|
|
106
|
+
* markets it fills — is answered as unknown or empty.
|
|
107
|
+
*
|
|
108
|
+
* @throws When neither the store nor the chain knows the feed. A private
|
|
109
|
+
* draft is not here — it lives in whichever app wrote it.
|
|
104
110
|
**/
|
|
105
111
|
feed({ chainId, address }: PriceFeedArgs): Promise<PermissionlessPriceFeedDetails>;
|
|
106
112
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PriceFeed } from "../../core/pricefeed.js";
|
|
2
|
+
import { TreeNode } from "./stress/classify.js";
|
|
3
|
+
//#region src/permissionless/api/oracles/onchain-feed.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* What the store would say about a feed, from what the chain says instead.
|
|
6
|
+
*
|
|
7
|
+
* Two fields are the store's alone and are answered as unknown rather than
|
|
8
|
+
* guessed: who deployed it is a store row, so it is the zero address, and the
|
|
9
|
+
* staleness period is a store setting a feed that was never added does not
|
|
10
|
+
* have, so it is 0. The compressor reports `PRICE_FEED::EXTERNAL` for a
|
|
11
|
+
* contract it cannot otherwise place; an empty type is the same thing said
|
|
12
|
+
* less clearly.
|
|
13
|
+
*/
|
|
14
|
+
export declare function onchainPriceFeed(node: TreeNode): PriceFeed;
|
|
15
|
+
//#endregion
|
|
@@ -170,9 +170,10 @@ export interface PermissionlessMarketSlot extends PermissionlessIndexedPriceFeed
|
|
|
170
170
|
/**
|
|
171
171
|
* One feed of the store, with everywhere the store lets it be used.
|
|
172
172
|
*
|
|
173
|
-
* Feeds a tree carries but the store never added have no identity to report
|
|
174
|
-
* no status history, hence no name or staleness — so the
|
|
175
|
-
*
|
|
173
|
+
* Feeds a tree carries but the store never added have no identity to report
|
|
174
|
+
* here — no status history, hence no name or staleness — so the backend does
|
|
175
|
+
* not answer for them; the SDK reads them off the chain instead, see
|
|
176
|
+
* {@link PermissionlessPriceFeedDetails.inStore}.
|
|
176
177
|
**/
|
|
177
178
|
export interface PermissionlessIndexedPriceFeedDetails {
|
|
178
179
|
chainId: number;
|
|
@@ -190,6 +191,14 @@ export interface PermissionlessIndexedPriceFeedDetails {
|
|
|
190
191
|
**/
|
|
191
192
|
export interface PermissionlessPriceFeedDetails extends PermissionlessIndexedPriceFeedDetails {
|
|
192
193
|
markets: PermissionlessPriceFeedMarket[];
|
|
194
|
+
/**
|
|
195
|
+
* Whether the answer is the store's.
|
|
196
|
+
*
|
|
197
|
+
* `false` is a deployed feed the store never added, read off the chain
|
|
198
|
+
* instead: `deployedBy` is the zero address, `stalenessPeriod` is 0, and
|
|
199
|
+
* `assets` and `markets` are empty, because all of them are store rows.
|
|
200
|
+
*/
|
|
201
|
+
inStore: boolean;
|
|
193
202
|
}
|
|
194
203
|
/**
|
|
195
204
|
* One feed as a market slot holds it.
|
|
@@ -8,9 +8,17 @@ import { Address, PublicClient } from "viem";
|
|
|
8
8
|
* transaction. Returns nothing when the bound still fits.
|
|
9
9
|
**/
|
|
10
10
|
export declare function updateBounds(client: PublicClient, address: Address): Promise<RawTx[]>;
|
|
11
|
+
/** An LP feed found in a feed tree, with what it calls itself on chain. */
|
|
12
|
+
export interface LPFeed {
|
|
13
|
+
address: Address;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
11
16
|
/**
|
|
12
17
|
* Of the whole feed tree behind `feeds`, the ones that are LP feeds: the
|
|
13
18
|
* probe keeps whatever answers `getLPExchangeRate`.
|
|
19
|
+
*
|
|
20
|
+
* The trees reach past `feeds` themselves, so an LP feed read inside one of
|
|
21
|
+
* them is among the results whether or not it is one of `feeds`.
|
|
14
22
|
**/
|
|
15
|
-
export declare function getFeedsToUpdate(client: PublicClient, pfCompressor: Address, feeds: Address[]): Promise<
|
|
23
|
+
export declare function getFeedsToUpdate(client: PublicClient, pfCompressor: Address, feeds: Address[]): Promise<LPFeed[]>;
|
|
16
24
|
//#endregion
|