@gearbox-protocol/sdk 12.9.9 → 12.10.1
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/history/errors.js +2 -0
- package/dist/cjs/permissionless/utils/price-update/get-prices.js +0 -3
- package/dist/cjs/permissionless/utils/price-update/get-updatable-feeds.js +2 -23
- package/dist/cjs/sdk/GearboxSDK.js +5 -25
- package/dist/cjs/sdk/market/pricefeeds/PriceFeedsRegister.js +8 -6
- package/dist/cjs/sdk/market/pricefeeds/PythPriceFeed.js +3 -27
- package/dist/cjs/sdk/market/pricefeeds/RedstonePriceFeed.js +2 -0
- package/dist/cjs/sdk/market/pricefeeds/index.js +0 -2
- package/dist/cjs/sdk/market/pricefeeds/updates/RedstoneUpdater.js +3 -202
- package/dist/cjs/sdk/market/pricefeeds/updates/fetchRedstonePayloads.js +4 -126
- package/dist/cjs/sdk/market/pricefeeds/updates/index.js +0 -7
- package/dist/cjs/sdk/options.js +1 -5
- package/dist/esm/history/errors.js +2 -0
- package/dist/esm/permissionless/utils/price-update/get-prices.js +0 -3
- package/dist/esm/permissionless/utils/price-update/get-updatable-feeds.js +2 -27
- package/dist/esm/sdk/GearboxSDK.js +5 -25
- package/dist/esm/sdk/market/pricefeeds/PriceFeedsRegister.js +8 -6
- package/dist/esm/sdk/market/pricefeeds/PythPriceFeed.js +4 -28
- package/dist/esm/sdk/market/pricefeeds/RedstonePriceFeed.js +2 -0
- package/dist/esm/sdk/market/pricefeeds/index.js +0 -2
- package/dist/esm/sdk/market/pricefeeds/updates/RedstoneUpdater.js +3 -202
- package/dist/esm/sdk/market/pricefeeds/updates/fetchRedstonePayloads.js +4 -130
- package/dist/esm/sdk/market/pricefeeds/updates/index.js +0 -4
- package/dist/esm/sdk/options.js +2 -9
- package/dist/types/permissionless/utils/price-update/get-updatable-feeds.d.ts +4 -3
- package/dist/types/sdk/market/pricefeeds/PriceFeedsRegister.d.ts +2 -3
- package/dist/types/sdk/market/pricefeeds/PythPriceFeed.d.ts +2 -25
- package/dist/types/sdk/market/pricefeeds/index.d.ts +1 -1
- package/dist/types/sdk/market/pricefeeds/updates/RedstoneUpdater.d.ts +5 -10
- package/dist/types/sdk/market/pricefeeds/updates/fetchRedstonePayloads.d.ts +2 -6
- package/dist/types/sdk/market/pricefeeds/updates/index.d.ts +0 -2
- package/dist/types/sdk/options.d.ts +0 -6
- package/package.json +6 -7
- package/dist/cjs/sdk/market/pricefeeds/updates/PriceUpdateTx.js +0 -55
- package/dist/cjs/sdk/market/pricefeeds/updates/PriceUpdatesCache.js +0 -78
- package/dist/cjs/sdk/market/pricefeeds/updates/PythAccumulatorUpdateData.js +0 -195
- package/dist/cjs/sdk/market/pricefeeds/updates/PythUpdater.js +0 -183
- package/dist/cjs/sdk/market/pricefeeds/updates/fetchPythPayloads.js +0 -135
- package/dist/esm/sdk/market/pricefeeds/updates/PriceUpdateTx.js +0 -31
- package/dist/esm/sdk/market/pricefeeds/updates/PriceUpdatesCache.js +0 -54
- package/dist/esm/sdk/market/pricefeeds/updates/PythAccumulatorUpdateData.js +0 -157
- package/dist/esm/sdk/market/pricefeeds/updates/PythUpdater.js +0 -158
- package/dist/esm/sdk/market/pricefeeds/updates/fetchPythPayloads.js +0 -115
- package/dist/types/sdk/market/pricefeeds/updates/PriceUpdateTx.d.ts +0 -13
- package/dist/types/sdk/market/pricefeeds/updates/PriceUpdatesCache.d.ts +0 -24
- package/dist/types/sdk/market/pricefeeds/updates/PythAccumulatorUpdateData.d.ts +0 -41
- package/dist/types/sdk/market/pricefeeds/updates/PythUpdater.d.ts +0 -26
- package/dist/types/sdk/market/pricefeeds/updates/fetchPythPayloads.d.ts +0 -45
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
bytes32ToString,
|
|
5
|
-
PythPriceFeed,
|
|
6
|
-
RedstonePriceFeedContract
|
|
7
|
-
} from "../../../sdk/index.js";
|
|
8
|
-
async function getUpdatablePriceFeeds(args) {
|
|
9
|
-
const { sdk, client, pfCompressor, priceFeeds } = args;
|
|
10
|
-
const priceFeedCompressor = getContract({
|
|
11
|
-
address: pfCompressor,
|
|
12
|
-
abi: priceFeedCompressorAbi,
|
|
13
|
-
client
|
|
14
|
-
});
|
|
15
|
-
const updatablePriceFeeds = (await priceFeedCompressor.read.loadPriceFeedTree([
|
|
16
|
-
priceFeeds
|
|
17
|
-
])).filter(
|
|
18
|
-
(data) => ["PRICE_FEED::PYTH", "PRICE_FEED::REDSTONE"].includes(
|
|
19
|
-
bytes32ToString(data.baseParams.contractType)
|
|
20
|
-
)
|
|
21
|
-
).map((data) => {
|
|
22
|
-
if (bytes32ToString(data.baseParams.contractType) === "PRICE_FEED::PYTH") {
|
|
23
|
-
return new PythPriceFeed(sdk, data);
|
|
24
|
-
}
|
|
25
|
-
return new RedstonePriceFeedContract(sdk, data);
|
|
26
|
-
});
|
|
27
|
-
return updatablePriceFeeds;
|
|
1
|
+
async function getUpdatablePriceFeeds(_args) {
|
|
2
|
+
return [];
|
|
28
3
|
}
|
|
29
4
|
export {
|
|
30
5
|
getUpdatablePriceFeeds
|
|
@@ -99,7 +99,6 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
99
99
|
plugins,
|
|
100
100
|
blockNumber,
|
|
101
101
|
redstone,
|
|
102
|
-
pyth,
|
|
103
102
|
ignoreUpdateablePrices,
|
|
104
103
|
ignoreMarkets,
|
|
105
104
|
marketConfigurators: mcs,
|
|
@@ -124,8 +123,7 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
124
123
|
ignoreUpdateablePrices,
|
|
125
124
|
ignoreMarkets,
|
|
126
125
|
marketConfigurators,
|
|
127
|
-
redstone
|
|
128
|
-
pyth
|
|
126
|
+
redstone
|
|
129
127
|
});
|
|
130
128
|
}
|
|
131
129
|
static hydrate(options, state) {
|
|
@@ -162,8 +160,7 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
162
160
|
ignoreUpdateablePrices,
|
|
163
161
|
ignoreMarkets,
|
|
164
162
|
marketConfigurators,
|
|
165
|
-
redstone
|
|
166
|
-
pyth
|
|
163
|
+
redstone
|
|
167
164
|
} = opts;
|
|
168
165
|
const re = this.#attachConfig ? "re" : "";
|
|
169
166
|
this.logger?.info(
|
|
@@ -177,16 +174,6 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
177
174
|
},
|
|
178
175
|
`${re}attaching gearbox sdk`
|
|
179
176
|
);
|
|
180
|
-
if (!!blockNumber && !opts.redstone?.historicTimestamp) {
|
|
181
|
-
this.logger?.warn(
|
|
182
|
-
`${re}attaching to fixed block number, but redstone historicTimestamp is not set. price updates might fail`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (!!blockNumber && !opts.pyth?.historicTimestamp) {
|
|
186
|
-
this.logger?.warn(
|
|
187
|
-
`${re}attaching to fixed block number, but pyth historicTimestamp is not set. price updates might fail`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
177
|
this.#attachConfig = opts;
|
|
191
178
|
const time = Date.now();
|
|
192
179
|
const block = await this.client.getBlock(
|
|
@@ -196,7 +183,7 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
196
183
|
);
|
|
197
184
|
this.#currentBlock = block.number;
|
|
198
185
|
this.#timestamp = block.timestamp;
|
|
199
|
-
this.#priceFeeds = new PriceFeedRegister(this, { redstone
|
|
186
|
+
this.#priceFeeds = new PriceFeedRegister(this, { redstone });
|
|
200
187
|
this.logger?.debug(
|
|
201
188
|
`${re}attach block number ${this.currentBlock} timestamp ${this.timestamp}`
|
|
202
189
|
);
|
|
@@ -246,8 +233,7 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
246
233
|
this.#currentBlock = state.currentBlock;
|
|
247
234
|
this.#timestamp = state.timestamp;
|
|
248
235
|
this.#priceFeeds = new PriceFeedRegister(this, {
|
|
249
|
-
redstone: opts.redstone
|
|
250
|
-
pyth: opts.pyth
|
|
236
|
+
redstone: opts.redstone
|
|
251
237
|
});
|
|
252
238
|
this.#addressProvider = hydrateAddressProvider(this, state.addressProvider);
|
|
253
239
|
this.logger?.debug(
|
|
@@ -300,8 +286,7 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
300
286
|
}
|
|
301
287
|
const opts = {
|
|
302
288
|
ignoreUpdateablePrices: this.#attachConfig.ignoreUpdateablePrices,
|
|
303
|
-
redstone: this.#attachConfig.redstone
|
|
304
|
-
pyth: this.#attachConfig.pyth
|
|
289
|
+
redstone: this.#attachConfig.redstone
|
|
305
290
|
};
|
|
306
291
|
this.#hydrate(opts, state);
|
|
307
292
|
await this.#hooks.triggerHooks("rehydrate", {
|
|
@@ -359,11 +344,6 @@ class GearboxSDK extends ChainContractsRegister {
|
|
|
359
344
|
timestamp,
|
|
360
345
|
ignoreUpdateablePrices = this.#attachConfig?.ignoreUpdateablePrices
|
|
361
346
|
} = opts ?? {};
|
|
362
|
-
if (this.#attachConfig?.redstone?.historicTimestamp || this.#attachConfig?.pyth?.historicTimestamp) {
|
|
363
|
-
throw new Error(
|
|
364
|
-
"syncState is not supported with redstone or pyth historicTimestamp"
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
347
|
if (!blockNumber || !timestamp) {
|
|
368
348
|
const block = await this.client.getBlock({
|
|
369
349
|
blockTag: "latest"
|
|
@@ -27,7 +27,6 @@ import { MellowLRTPriceFeedContract } from "./MellowLRTPriceFeed.js";
|
|
|
27
27
|
import { PendleTWAPPTPriceFeed } from "./PendleTWAPPTPriceFeed.js";
|
|
28
28
|
import { PythPriceFeed } from "./PythPriceFeed.js";
|
|
29
29
|
import { RedstonePriceFeedContract } from "./RedstonePriceFeed.js";
|
|
30
|
-
import { PythUpdater, RedstoneUpdater } from "./updates/index.js";
|
|
31
30
|
import { WstETHPriceFeedContract } from "./WstETHPriceFeed.js";
|
|
32
31
|
import { YearnPriceFeedContract } from "./YearnPriceFeed.js";
|
|
33
32
|
import { ZeroPriceFeedContract } from "./ZeroPriceFeed.js";
|
|
@@ -36,12 +35,9 @@ class PriceFeedRegister extends SDKConstruct {
|
|
|
36
35
|
#feeds = new AddressMap(void 0, "priceFeeds");
|
|
37
36
|
#latestUpdate;
|
|
38
37
|
updaters;
|
|
39
|
-
constructor(sdk,
|
|
38
|
+
constructor(sdk, _opts = {}) {
|
|
40
39
|
super(sdk);
|
|
41
|
-
this.updaters = [
|
|
42
|
-
new RedstoneUpdater(sdk, opts?.redstone),
|
|
43
|
-
new PythUpdater(sdk, opts?.pyth)
|
|
44
|
-
];
|
|
40
|
+
this.updaters = [];
|
|
45
41
|
}
|
|
46
42
|
addHook = this.#hooks.addHook.bind(this.#hooks);
|
|
47
43
|
removeHook = this.#hooks.removeHook.bind(this.#hooks);
|
|
@@ -133,6 +129,9 @@ class PriceFeedRegister extends SDKConstruct {
|
|
|
133
129
|
* @returns
|
|
134
130
|
*/
|
|
135
131
|
async generateExternalPriceFeedsUpdateTxs(feeds, block) {
|
|
132
|
+
if (this.updaters.length === 0) {
|
|
133
|
+
return this.generatePriceFeedsUpdateTxs([]);
|
|
134
|
+
}
|
|
136
135
|
const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
|
|
137
136
|
AP_PRICE_FEED_COMPRESSOR,
|
|
138
137
|
VERSION_RANGE_310
|
|
@@ -187,6 +186,9 @@ class PriceFeedRegister extends SDKConstruct {
|
|
|
187
186
|
* Discovered price feeds are not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
|
|
188
187
|
*/
|
|
189
188
|
async getPartialUpdatablePriceFeeds(configurators, pools) {
|
|
189
|
+
if (this.updaters.length === 0) {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
190
192
|
const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
|
|
191
193
|
AP_PRICE_FEED_COMPRESSOR,
|
|
192
194
|
VERSION_RANGE_310
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { decodeAbiParameters
|
|
1
|
+
import { decodeAbiParameters } from "viem";
|
|
2
2
|
import { pythPriceFeedAbi } from "../../abi/oracles.js";
|
|
3
3
|
import { AbstractPriceFeedContract } from "./AbstractPriceFeed.js";
|
|
4
4
|
const abi = pythPriceFeedAbi;
|
|
5
|
-
const iPythAbi = parseAbi([
|
|
6
|
-
"function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount)"
|
|
7
|
-
]);
|
|
8
5
|
class PythPriceFeed extends AbstractPriceFeedContract {
|
|
9
6
|
token;
|
|
10
7
|
priceFeedId;
|
|
@@ -13,6 +10,9 @@ class PythPriceFeed extends AbstractPriceFeedContract {
|
|
|
13
10
|
constructor(options, args) {
|
|
14
11
|
super(options, {
|
|
15
12
|
...args,
|
|
13
|
+
// the sdk does not push pyth price updates, so these feeds are never
|
|
14
|
+
// treated as updatable even though the contract implements the interface
|
|
15
|
+
updatable: false,
|
|
16
16
|
name: "PythPriceFeed",
|
|
17
17
|
abi
|
|
18
18
|
});
|
|
@@ -44,30 +44,6 @@ class PythPriceFeed extends AbstractPriceFeedContract {
|
|
|
44
44
|
this.pyth = decoded[2];
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
createPriceUpdateTx(data) {
|
|
48
|
-
return this.createRawTx({
|
|
49
|
-
functionName: "updatePrice",
|
|
50
|
-
args: [data],
|
|
51
|
-
description: `updating pyth price for ${this.priceFeedId} [${this.labelAddress(this.address)}]`
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Returns contract function parameters for the getUpdateFee function on original Pyth contract
|
|
56
|
-
* @param calldata
|
|
57
|
-
* @returns
|
|
58
|
-
*/
|
|
59
|
-
getUpdateFeeParams(calldata) {
|
|
60
|
-
const [, updateData] = decodeAbiParameters(
|
|
61
|
-
[{ type: "uint256" }, { type: "bytes[]" }],
|
|
62
|
-
calldata
|
|
63
|
-
);
|
|
64
|
-
return {
|
|
65
|
-
address: this.pyth,
|
|
66
|
-
abi: iPythAbi,
|
|
67
|
-
functionName: "getUpdateFee",
|
|
68
|
-
args: [updateData]
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
47
|
}
|
|
72
48
|
export {
|
|
73
49
|
PythPriceFeed
|
|
@@ -13,6 +13,8 @@ class RedstonePriceFeedContract extends AbstractPriceFeedContract {
|
|
|
13
13
|
constructor(options, args) {
|
|
14
14
|
super(options, {
|
|
15
15
|
...args,
|
|
16
|
+
// RedStone updates are no longer pushed by the SDK.
|
|
17
|
+
updatable: false,
|
|
16
18
|
name: "RedstonePriceFeed",
|
|
17
19
|
abi: redstonePriceFeedAbi
|
|
18
20
|
});
|
|
@@ -19,13 +19,11 @@ export * from "./PythPriceFeed.js";
|
|
|
19
19
|
export * from "./RedstonePriceFeed.js";
|
|
20
20
|
export * from "./types.js";
|
|
21
21
|
import {
|
|
22
|
-
fetchPythPayloads,
|
|
23
22
|
fetchRedstonePayloads
|
|
24
23
|
} from "./updates/index.js";
|
|
25
24
|
export * from "./WstETHPriceFeed.js";
|
|
26
25
|
export * from "./YearnPriceFeed.js";
|
|
27
26
|
export * from "./ZeroPriceFeed.js";
|
|
28
27
|
export {
|
|
29
|
-
fetchPythPayloads,
|
|
30
28
|
fetchRedstonePayloads
|
|
31
29
|
};
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
2
|
import { SDKConstruct } from "../../../base/index.js";
|
|
3
|
-
import { AddressMap } from "../../../utils/index.js";
|
|
4
|
-
import { fetchRedstonePayloads } from "./fetchRedstonePayloads.js";
|
|
5
|
-
import { PriceUpdatesCache } from "./PriceUpdatesCache.js";
|
|
6
|
-
import { PriceUpdateTx } from "./PriceUpdateTx.js";
|
|
7
|
-
class RedstoneUpdateTx extends PriceUpdateTx {
|
|
8
|
-
name = "redstone";
|
|
9
|
-
}
|
|
10
3
|
const RedstoneOptions = z.object({
|
|
11
4
|
/**
|
|
12
5
|
* Fixed redstone historic timestamp in ms
|
|
@@ -34,204 +27,12 @@ const RedstoneOptions = z.object({
|
|
|
34
27
|
enableLogging: z.boolean().optional()
|
|
35
28
|
});
|
|
36
29
|
class RedstoneUpdater extends SDKConstruct {
|
|
37
|
-
|
|
38
|
-
#historicalTimestampMs;
|
|
39
|
-
#gateways;
|
|
40
|
-
#ignoreMissingFeeds;
|
|
41
|
-
#enableLogging;
|
|
42
|
-
constructor(sdk, opts = {}) {
|
|
30
|
+
constructor(sdk, _opts = {}) {
|
|
43
31
|
super(sdk);
|
|
44
|
-
this.#ignoreMissingFeeds = opts.ignoreMissingFeeds;
|
|
45
|
-
this.#enableLogging = opts.enableLogging;
|
|
46
|
-
this.#gateways = opts.gateways?.length ? opts.gateways : void 0;
|
|
47
|
-
let ts = opts.historicTimestamp;
|
|
48
|
-
if (ts) {
|
|
49
|
-
ts = ts === true ? Number(this.sdk.timestamp) * 1e3 : ts;
|
|
50
|
-
this.#historicalTimestampMs = 6e4 * Math.floor(ts / 6e4);
|
|
51
|
-
this.logger?.debug(
|
|
52
|
-
`using historical timestamp ${this.#historicalTimestampMs}`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
this.#cache = PriceUpdatesCache.get("redstone", {
|
|
56
|
-
// currently staleness period is 240 seconds on all networks, add some buffer
|
|
57
|
-
// this period of 4 minutes is selected based on time that is required for user to sign transaction with wallet
|
|
58
|
-
// so it's unlikely to decrease
|
|
59
|
-
ttl: opts.cacheTTL ?? 225 * 1e3,
|
|
60
|
-
historical: !!ts
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
async getUpdateTxs(feeds) {
|
|
64
|
-
if (feeds.length === 0) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
this.logger?.debug(
|
|
68
|
-
`generating update transactions for ${feeds.length} redstone price feeds`
|
|
69
|
-
);
|
|
70
|
-
const groupedFeeds = {};
|
|
71
|
-
const priceFeeds = /* @__PURE__ */ new Map();
|
|
72
|
-
for (const feed of feeds) {
|
|
73
|
-
if (!isRedstone(feed)) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const key = `${feed.dataServiceId}:${feed.signersThreshold}`;
|
|
77
|
-
if (!groupedFeeds[key]) {
|
|
78
|
-
groupedFeeds[key] = /* @__PURE__ */ new Set();
|
|
79
|
-
}
|
|
80
|
-
groupedFeeds[key].add(feed.dataId);
|
|
81
|
-
const pfsForDataId = priceFeeds.get(feed.dataId) ?? new AddressMap();
|
|
82
|
-
pfsForDataId.upsert(feed.address, feed);
|
|
83
|
-
priceFeeds.set(feed.dataId, pfsForDataId);
|
|
84
|
-
}
|
|
85
|
-
const results = [];
|
|
86
|
-
let [minTimestamp, maxTimestamp] = [Number.POSITIVE_INFINITY, 0];
|
|
87
|
-
for (const [key, group] of Object.entries(groupedFeeds)) {
|
|
88
|
-
const [dataServiceId, signersStr] = key.split(":");
|
|
89
|
-
const uniqueSignersCount = parseInt(signersStr, 10);
|
|
90
|
-
const payloads = await this.#safeGetPayloads(
|
|
91
|
-
dataServiceId,
|
|
92
|
-
group,
|
|
93
|
-
uniqueSignersCount
|
|
94
|
-
);
|
|
95
|
-
for (const { dataFeedId, data, timestamp, cached } of payloads) {
|
|
96
|
-
const pfsForDataId = priceFeeds.get(dataFeedId);
|
|
97
|
-
if (!pfsForDataId) {
|
|
98
|
-
throw new Error(`cannot get price feed addresses for ${dataFeedId}`);
|
|
99
|
-
}
|
|
100
|
-
for (const priceFeed of pfsForDataId.values()) {
|
|
101
|
-
minTimestamp = Math.min(minTimestamp, timestamp);
|
|
102
|
-
maxTimestamp = Math.max(maxTimestamp, timestamp);
|
|
103
|
-
results.push(
|
|
104
|
-
new RedstoneUpdateTx(priceFeed.createPriceUpdateTx(data), {
|
|
105
|
-
dataFeedId,
|
|
106
|
-
dataServiceId,
|
|
107
|
-
priceFeed: priceFeed.address,
|
|
108
|
-
timestamp,
|
|
109
|
-
cached
|
|
110
|
-
})
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
let tsRange = "";
|
|
116
|
-
if (results.length) {
|
|
117
|
-
const minDelta = BigInt(minTimestamp) - this.sdk.timestamp;
|
|
118
|
-
tsRange = `, timestamps ${minTimestamp} (${minDelta})`;
|
|
119
|
-
if (minTimestamp !== maxTimestamp) {
|
|
120
|
-
const maxDelta = BigInt(maxTimestamp) - this.sdk.timestamp;
|
|
121
|
-
tsRange = `${tsRange} - ${maxTimestamp} (${maxDelta})`;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
this.logger?.debug(
|
|
125
|
-
`generated ${results.length} update transactions for redstone price feeds: ${Array.from(priceFeeds.keys()).join(", ")}${tsRange}`
|
|
126
|
-
);
|
|
127
|
-
return results;
|
|
128
32
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
* @param dataServiceId
|
|
132
|
-
* @param dataFeedsIds
|
|
133
|
-
* @param uniqueSignersCount
|
|
134
|
-
* @returns
|
|
135
|
-
*/
|
|
136
|
-
async #safeGetPayloads(dataServiceId, dataFeedsIds, uniqueSignersCount) {
|
|
137
|
-
let result = await this.#getPayloads(
|
|
138
|
-
dataServiceId,
|
|
139
|
-
dataFeedsIds,
|
|
140
|
-
uniqueSignersCount
|
|
141
|
-
);
|
|
142
|
-
if (this.#historicalTimestampMs) {
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
let expired = false;
|
|
146
|
-
for (const { timestamp, dataFeedId } of result) {
|
|
147
|
-
const delta = Number(this.sdk.timestamp) - timestamp;
|
|
148
|
-
if (delta >= 240 && delta < 255) {
|
|
149
|
-
this.logger?.warn(
|
|
150
|
-
`payload for ${dataFeedId} has expired by ${delta} seconds`
|
|
151
|
-
);
|
|
152
|
-
expired = true;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (expired) {
|
|
156
|
-
result = await this.#getPayloads(
|
|
157
|
-
dataServiceId,
|
|
158
|
-
dataFeedsIds,
|
|
159
|
-
uniqueSignersCount
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
return result;
|
|
33
|
+
async getUpdateTxs(_feeds) {
|
|
34
|
+
return [];
|
|
163
35
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Gets redstone payloads in one request for multiple feeds with the same dataServiceId and uniqueSignersCount
|
|
166
|
-
* If historicalTimestamp is set, responses will be cached
|
|
167
|
-
* @param dataServiceId
|
|
168
|
-
* @param dataFeedsIds
|
|
169
|
-
* @param uniqueSignersCount
|
|
170
|
-
* @returns
|
|
171
|
-
*/
|
|
172
|
-
async #getPayloads(dataServiceId, dataFeedsIds, uniqueSignersCount) {
|
|
173
|
-
this.logger?.debug(
|
|
174
|
-
`getting redstone payloads for ${dataFeedsIds.size} data feeds in ${dataServiceId} with ${uniqueSignersCount} signers: ${Array.from(dataFeedsIds).join(", ")}`
|
|
175
|
-
);
|
|
176
|
-
const fromCache = [];
|
|
177
|
-
const uncached = [];
|
|
178
|
-
for (const dataFeedId of dataFeedsIds) {
|
|
179
|
-
const cached = this.#cache.get(
|
|
180
|
-
dataServiceId,
|
|
181
|
-
dataFeedId,
|
|
182
|
-
uniqueSignersCount
|
|
183
|
-
);
|
|
184
|
-
if (cached) {
|
|
185
|
-
fromCache.push({ ...cached, cached: true });
|
|
186
|
-
} else {
|
|
187
|
-
uncached.push(dataFeedId);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const fromRedstone = await this.#fetchPayloads(
|
|
191
|
-
dataServiceId,
|
|
192
|
-
new Set(uncached),
|
|
193
|
-
uniqueSignersCount
|
|
194
|
-
);
|
|
195
|
-
for (const resp of fromRedstone) {
|
|
196
|
-
this.#cache.set(resp, dataServiceId, resp.dataFeedId, uniqueSignersCount);
|
|
197
|
-
}
|
|
198
|
-
this.logger?.debug(
|
|
199
|
-
`got ${fromRedstone.length} new redstone updates and ${fromCache.length} from cache`
|
|
200
|
-
);
|
|
201
|
-
return [...fromCache, ...fromRedstone];
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Fetches redstone payloads in one request for multiple feeds with the same dataServiceId and uniqueSignersCount
|
|
205
|
-
* Payloads are loaded in one request to avoid redstone rate limit
|
|
206
|
-
* @param dataServiceId
|
|
207
|
-
* @param dataFeedsIds
|
|
208
|
-
* @param uniqueSignersCount
|
|
209
|
-
* @returns
|
|
210
|
-
*/
|
|
211
|
-
async #fetchPayloads(dataServiceId, dataFeedsIds, uniqueSignersCount) {
|
|
212
|
-
if (dataFeedsIds.size === 0) {
|
|
213
|
-
return [];
|
|
214
|
-
}
|
|
215
|
-
const dataPackagesIds = Array.from(dataFeedsIds);
|
|
216
|
-
const tsStr = this.#historicalTimestampMs ? ` with historical timestamp ${this.#historicalTimestampMs}` : "";
|
|
217
|
-
this.logger?.debug(
|
|
218
|
-
`fetching redstone payloads for ${dataFeedsIds.size} data feeds in ${dataServiceId} with ${uniqueSignersCount} signers: ${dataPackagesIds.join(", ")}${tsStr}`
|
|
219
|
-
);
|
|
220
|
-
return fetchRedstonePayloads({
|
|
221
|
-
dataServiceId,
|
|
222
|
-
dataFeedsIds,
|
|
223
|
-
uniqueSignersCount,
|
|
224
|
-
historicalTimestampMs: this.#historicalTimestampMs,
|
|
225
|
-
gateways: this.#gateways,
|
|
226
|
-
ignoreMissingFeeds: this.#ignoreMissingFeeds,
|
|
227
|
-
enableLogging: this.#enableLogging,
|
|
228
|
-
logger: this.logger,
|
|
229
|
-
metadataTimestampMs: Number(this.sdk.timestamp) * 1e3
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function isRedstone(pf) {
|
|
234
|
-
return pf.contractType === "PRICE_FEED::REDSTONE";
|
|
235
36
|
}
|
|
236
37
|
export {
|
|
237
38
|
RedstoneOptions,
|
|
@@ -1,136 +1,10 @@
|
|
|
1
|
-
import { DataServiceWrapper } from "@redstone-finance/evm-connector";
|
|
2
|
-
import {
|
|
3
|
-
RedstonePayload
|
|
4
|
-
} from "@redstone-finance/protocol";
|
|
5
|
-
import {
|
|
6
|
-
getSignersForDataServiceId
|
|
7
|
-
} from "@redstone-finance/sdk";
|
|
8
|
-
import { MathUtils } from "@redstone-finance/utils";
|
|
9
|
-
import { encodeAbiParameters, toBytes } from "viem";
|
|
10
|
-
import { retry } from "../../../utils/index.js";
|
|
11
1
|
async function fetchRedstonePayloads(options) {
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
uniqueSignersCount,
|
|
16
|
-
historicalTimestampMs,
|
|
17
|
-
gateways,
|
|
18
|
-
ignoreMissingFeeds,
|
|
19
|
-
enableLogging,
|
|
20
|
-
logger,
|
|
21
|
-
returnPrices
|
|
22
|
-
} = options;
|
|
23
|
-
const metadataTimestampMs = historicalTimestampMs ?? options.metadataTimestampMs;
|
|
24
|
-
const dataPackagesIds = Array.from(new Set(dataFeedsIds));
|
|
25
|
-
if (dataPackagesIds.length === 0) {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
const wrapper = new DataServiceWrapper({
|
|
29
|
-
dataServiceId,
|
|
30
|
-
dataPackagesIds,
|
|
31
|
-
uniqueSignersCount,
|
|
32
|
-
authorizedSigners: getSignersForDataServiceId(
|
|
33
|
-
dataServiceId
|
|
34
|
-
),
|
|
35
|
-
historicalTimestamp: historicalTimestampMs,
|
|
36
|
-
urls: gateways,
|
|
37
|
-
ignoreMissingFeed: ignoreMissingFeeds,
|
|
38
|
-
enableEnhancedLogs: enableLogging
|
|
39
|
-
});
|
|
40
|
-
if (metadataTimestampMs) {
|
|
41
|
-
wrapper.setMetadataTimestamp(metadataTimestampMs);
|
|
42
|
-
}
|
|
43
|
-
const dataPayload = await retry(() => wrapper.prepareRedstonePayload(true), {
|
|
44
|
-
attempts: 5,
|
|
45
|
-
interval: historicalTimestampMs ? 30500 : 250
|
|
46
|
-
});
|
|
47
|
-
const parsed = RedstonePayload.parse(toBytes(`0x${dataPayload}`));
|
|
48
|
-
const packagesByDataFeedId = groupDataPackages(parsed.signedDataPackages);
|
|
49
|
-
const result = [];
|
|
50
|
-
for (const dataFeedId of dataFeedsIds) {
|
|
51
|
-
const signedDataPackages = packagesByDataFeedId[dataFeedId];
|
|
52
|
-
if (!signedDataPackages) {
|
|
53
|
-
if (ignoreMissingFeeds) {
|
|
54
|
-
logger?.warn(`cannot find data packages for ${dataFeedId}`);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
throw new Error(`cannot find data packages for ${dataFeedId}`);
|
|
58
|
-
}
|
|
59
|
-
if (signedDataPackages.length !== uniqueSignersCount) {
|
|
60
|
-
if (ignoreMissingFeeds) {
|
|
61
|
-
logger?.warn(
|
|
62
|
-
`got ${signedDataPackages.length} data packages for ${dataFeedId}, but expected ${uniqueSignersCount}`
|
|
63
|
-
);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
throw new Error(
|
|
67
|
-
`got ${signedDataPackages.length} data packages for ${dataFeedId}, but expected ${uniqueSignersCount}`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
result.push(
|
|
71
|
-
getCalldataWithTimestamp(
|
|
72
|
-
dataFeedId,
|
|
73
|
-
signedDataPackages,
|
|
74
|
-
wrapper.getUnsignedMetadata(),
|
|
75
|
-
returnPrices
|
|
76
|
-
)
|
|
2
|
+
for (const _feed of options.dataFeedsIds) {
|
|
3
|
+
throw new Error(
|
|
4
|
+
"RedStone price updates are no longer supported by this SDK"
|
|
77
5
|
);
|
|
78
6
|
}
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
function groupDataPackages(signedDataPackages) {
|
|
82
|
-
const packagesByDataFeedId = {};
|
|
83
|
-
for (const p of signedDataPackages) {
|
|
84
|
-
const { dataPoints } = p.dataPackage;
|
|
85
|
-
const dataFeedId0 = dataPoints[0].dataFeedId;
|
|
86
|
-
for (const dp of dataPoints) {
|
|
87
|
-
if (dp.dataFeedId !== dataFeedId0) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`data package contains data points with different dataFeedIds: ${dp.dataFeedId} and ${dataFeedId0}`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (!packagesByDataFeedId[dataFeedId0]) {
|
|
94
|
-
packagesByDataFeedId[dataFeedId0] = [];
|
|
95
|
-
}
|
|
96
|
-
packagesByDataFeedId[dataFeedId0].push(p);
|
|
97
|
-
}
|
|
98
|
-
return packagesByDataFeedId;
|
|
99
|
-
}
|
|
100
|
-
function getCalldataWithTimestamp(dataFeedId, packages, unsignedMetadata, returnPrices) {
|
|
101
|
-
const originalPayload = RedstonePayload.prepare(packages, unsignedMetadata);
|
|
102
|
-
const originalPayloadLength = originalPayload.length / 2;
|
|
103
|
-
const bytesToAdd = 32 - originalPayloadLength % 32;
|
|
104
|
-
const newUnsignedMetadata = unsignedMetadata + "_".repeat(bytesToAdd);
|
|
105
|
-
const payload = RedstonePayload.prepare(packages, newUnsignedMetadata);
|
|
106
|
-
let timestamp = 0;
|
|
107
|
-
for (const p of packages) {
|
|
108
|
-
const newTimestamp = p.dataPackage.timestampMilliseconds / 1e3;
|
|
109
|
-
if (timestamp === 0) {
|
|
110
|
-
timestamp = newTimestamp;
|
|
111
|
-
} else if (timestamp !== newTimestamp) {
|
|
112
|
-
throw new Error("Timestamps are not equal");
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const base = {
|
|
116
|
-
dataFeedId,
|
|
117
|
-
data: encodeAbiParameters(
|
|
118
|
-
[{ type: "uint256" }, { type: "bytes" }],
|
|
119
|
-
[BigInt(timestamp), `0x${payload}`]
|
|
120
|
-
),
|
|
121
|
-
timestamp,
|
|
122
|
-
cached: false
|
|
123
|
-
};
|
|
124
|
-
if (returnPrices) {
|
|
125
|
-
const prices = packages.flatMap((p) => p.dataPackage.dataPoints).map((dp) => BigInt(`0x${Buffer.from(dp.value).toString("hex")}`));
|
|
126
|
-
const medianPrice = BigInt(MathUtils.getMedianOfBigNumbers(prices));
|
|
127
|
-
return {
|
|
128
|
-
...base,
|
|
129
|
-
price: medianPrice,
|
|
130
|
-
decimals: 8
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
return base;
|
|
7
|
+
return [];
|
|
134
8
|
}
|
|
135
9
|
export {
|
|
136
10
|
fetchRedstonePayloads
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
export * from "./fetchPythPayloads.js";
|
|
2
1
|
export * from "./fetchRedstonePayloads.js";
|
|
3
|
-
import { PythOptions, PythUpdater } from "./PythUpdater.js";
|
|
4
2
|
import { RedstoneOptions, RedstoneUpdater } from "./RedstoneUpdater.js";
|
|
5
3
|
export {
|
|
6
|
-
PythOptions,
|
|
7
|
-
PythUpdater,
|
|
8
4
|
RedstoneOptions,
|
|
9
5
|
RedstoneUpdater
|
|
10
6
|
};
|
package/dist/esm/sdk/options.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
|
-
import {
|
|
3
|
-
PythOptions,
|
|
4
|
-
RedstoneOptions
|
|
5
|
-
} from "./market/pricefeeds/updates/index.js";
|
|
2
|
+
import { RedstoneOptions } from "./market/pricefeeds/updates/index.js";
|
|
6
3
|
import { ZodAddress } from "./utils/index.js";
|
|
7
4
|
const SDKOptions = z.object({
|
|
8
5
|
/**
|
|
@@ -39,13 +36,9 @@ const SDKOptions = z.object({
|
|
|
39
36
|
*/
|
|
40
37
|
logger: z.any(),
|
|
41
38
|
/**
|
|
42
|
-
*
|
|
39
|
+
* @deprecated RedStone options are accepted for compatibility and ignored.
|
|
43
40
|
*/
|
|
44
41
|
redstone: RedstoneOptions.optional(),
|
|
45
|
-
/**
|
|
46
|
-
* Options related to pyth price feeds
|
|
47
|
-
*/
|
|
48
|
-
pyth: PythOptions.optional(),
|
|
49
42
|
/**
|
|
50
43
|
* Explicit gas limit for read eth_calls
|
|
51
44
|
* Null to disable explicitly setting gas limit, undefined to use default sdk value
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type { Address, PublicClient } from "viem";
|
|
2
|
+
import type { GearboxSDK, IPriceFeedContract } from "../../../sdk/index.js";
|
|
3
|
+
/** @deprecated Support for updatable price feeds is disabled. */
|
|
4
|
+
export declare function getUpdatablePriceFeeds(_args: {
|
|
4
5
|
sdk: GearboxSDK;
|
|
5
6
|
client: PublicClient;
|
|
6
7
|
pfCompressor: Address;
|
|
@@ -6,7 +6,7 @@ import type { RawTx } from "../../types/index.js";
|
|
|
6
6
|
import type { IHooks } from "../../utils/internal/index.js";
|
|
7
7
|
import { type PartialPriceFeedTreeNode } from "./AbstractPriceFeed.js";
|
|
8
8
|
import type { IPriceFeedContract, PriceUpdateV310, UpdatePriceFeedsResult } from "./types.js";
|
|
9
|
-
import type { IPriceUpdater, IPriceUpdateTask,
|
|
9
|
+
import type { IPriceUpdater, IPriceUpdateTask, RedstoneOptions } from "./updates/index.js";
|
|
10
10
|
export type PriceFeedRegisterHooks = {
|
|
11
11
|
/**
|
|
12
12
|
* Emitted when transactions to update price feeds have been generated, but before they're used anywhere
|
|
@@ -15,7 +15,6 @@ export type PriceFeedRegisterHooks = {
|
|
|
15
15
|
};
|
|
16
16
|
export interface PriceFeedRegisterOptions {
|
|
17
17
|
redstone?: RedstoneOptions;
|
|
18
|
-
pyth?: PythOptions;
|
|
19
18
|
}
|
|
20
19
|
export interface LatestUpdate {
|
|
21
20
|
timestamp: number;
|
|
@@ -29,7 +28,7 @@ export interface LatestUpdate {
|
|
|
29
28
|
export declare class PriceFeedRegister extends SDKConstruct implements IHooks<PriceFeedRegisterHooks> {
|
|
30
29
|
#private;
|
|
31
30
|
readonly updaters: IPriceUpdater[];
|
|
32
|
-
constructor(sdk: GearboxSDK,
|
|
31
|
+
constructor(sdk: GearboxSDK, _opts?: PriceFeedRegisterOptions);
|
|
33
32
|
addHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
|
|
34
33
|
removeHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
|
|
35
34
|
/**
|