@gearbox-protocol/sdk 6.0.2 → 7.0.0-next.2
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/sdk/GearboxSDK.js +34 -30
- package/dist/cjs/sdk/market/MarketRegister.js +5 -2
- package/dist/cjs/sdk/market/MarketSuite.js +3 -0
- package/dist/cjs/sdk/market/oracle/PriceOracleBaseContract.js +1 -3
- package/dist/cjs/sdk/market/pool/PoolSuite.js +7 -0
- package/dist/cjs/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -4
- package/dist/cjs/sdk/market/pricefeeds/RedstoneCache.js +61 -0
- package/dist/cjs/sdk/market/pricefeeds/RedstoneUpdater.js +33 -40
- package/dist/cjs/sdk/sdk-legacy/core/endpoint.js +2 -2
- package/dist/cjs/sdk/utils/index.js +3 -1
- package/dist/cjs/sdk/utils/type-utils.js +16 -0
- package/dist/esm/sdk/GearboxSDK.js +34 -30
- package/dist/esm/sdk/market/MarketRegister.js +5 -2
- package/dist/esm/sdk/market/MarketSuite.js +3 -0
- package/dist/esm/sdk/market/oracle/PriceOracleBaseContract.js +1 -3
- package/dist/esm/sdk/market/pool/PoolSuite.js +7 -0
- package/dist/esm/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -4
- package/dist/esm/sdk/market/pricefeeds/RedstoneCache.js +37 -0
- package/dist/esm/sdk/market/pricefeeds/RedstoneUpdater.js +33 -40
- package/dist/esm/sdk/sdk-legacy/core/endpoint.js +2 -2
- package/dist/esm/sdk/utils/index.js +1 -0
- package/dist/esm/sdk/utils/type-utils.js +0 -0
- package/dist/types/sdk/GearboxSDK.d.ts +9 -13
- package/dist/types/sdk/market/pool/PoolSuite.d.ts +3 -0
- package/dist/types/sdk/market/pricefeeds/PriceFeedsRegister.d.ts +6 -3
- package/dist/types/sdk/market/pricefeeds/RedstoneCache.d.ts +25 -0
- package/dist/types/sdk/market/pricefeeds/RedstoneUpdater.d.ts +27 -9
- package/dist/types/sdk/utils/index.d.ts +1 -0
- package/dist/types/sdk/utils/type-utils.d.ts +1 -0
- package/package.json +3 -2
|
@@ -50,16 +50,13 @@ class GearboxSDK {
|
|
|
50
50
|
#attachConfig;
|
|
51
51
|
// Collection of markets
|
|
52
52
|
#marketRegister;
|
|
53
|
+
#priceFeeds;
|
|
53
54
|
logger;
|
|
54
55
|
/**
|
|
55
56
|
* Interest rate models can be reused across chain (and SDK operates on chain level)
|
|
56
57
|
* TODO: use whatever interface is necessary for InterestRateModels
|
|
57
58
|
*/
|
|
58
59
|
interestRateModels = new import_utils.AddressMap();
|
|
59
|
-
/**
|
|
60
|
-
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
61
|
-
*/
|
|
62
|
-
priceFeeds;
|
|
63
60
|
/**
|
|
64
61
|
* Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
|
|
65
62
|
*/
|
|
@@ -82,9 +79,10 @@ class GearboxSDK {
|
|
|
82
79
|
logger,
|
|
83
80
|
plugins,
|
|
84
81
|
blockNumber,
|
|
85
|
-
|
|
82
|
+
redstone,
|
|
86
83
|
ignoreUpdateablePrices,
|
|
87
|
-
marketConfigurators: mcs
|
|
84
|
+
marketConfigurators: mcs,
|
|
85
|
+
strictContractTypes
|
|
88
86
|
} = options;
|
|
89
87
|
let { networkType, addressProvider, chainId } = options;
|
|
90
88
|
const attachClient = (0, import_viem.createPublicClient)({
|
|
@@ -108,28 +106,33 @@ class GearboxSDK {
|
|
|
108
106
|
return new GearboxSDK({
|
|
109
107
|
provider,
|
|
110
108
|
logger,
|
|
111
|
-
plugins
|
|
109
|
+
plugins,
|
|
110
|
+
strictContractTypes
|
|
112
111
|
}).#attach({
|
|
113
112
|
addressProvider,
|
|
114
113
|
blockNumber,
|
|
115
|
-
redstoneHistoricTimestamp,
|
|
116
114
|
ignoreUpdateablePrices,
|
|
117
|
-
marketConfigurators
|
|
115
|
+
marketConfigurators,
|
|
116
|
+
redstone
|
|
118
117
|
});
|
|
119
118
|
}
|
|
120
119
|
static hydrate(options, state) {
|
|
121
|
-
const { logger, plugins, ...rest } = options;
|
|
120
|
+
const { logger, plugins, strictContractTypes, ...rest } = options;
|
|
122
121
|
const provider = new import_chain.Provider({
|
|
123
122
|
...rest,
|
|
124
123
|
chainId: state.chainId,
|
|
125
124
|
networkType: state.network
|
|
126
125
|
});
|
|
127
|
-
return new GearboxSDK({
|
|
126
|
+
return new GearboxSDK({
|
|
127
|
+
provider,
|
|
128
|
+
plugins,
|
|
129
|
+
logger,
|
|
130
|
+
strictContractTypes
|
|
131
|
+
}).#hydrate(rest, state);
|
|
128
132
|
}
|
|
129
133
|
constructor(options) {
|
|
130
134
|
this.#provider = options.provider;
|
|
131
135
|
this.logger = options.logger;
|
|
132
|
-
this.priceFeeds = new import_pricefeeds.PriceFeedRegister(this);
|
|
133
136
|
this.strictContractTypes = options.strictContractTypes ?? false;
|
|
134
137
|
const pluginsInstances = {};
|
|
135
138
|
for (const [name, Plugin] of import_utils.TypedObjectUtils.entries(
|
|
@@ -143,9 +146,9 @@ class GearboxSDK {
|
|
|
143
146
|
const {
|
|
144
147
|
addressProvider,
|
|
145
148
|
blockNumber,
|
|
146
|
-
redstoneHistoricTimestamp,
|
|
147
149
|
ignoreUpdateablePrices,
|
|
148
|
-
marketConfigurators
|
|
150
|
+
marketConfigurators,
|
|
151
|
+
redstone
|
|
149
152
|
} = opts;
|
|
150
153
|
const re = this.#attachConfig ? "re" : "";
|
|
151
154
|
this.logger?.info(
|
|
@@ -157,7 +160,7 @@ class GearboxSDK {
|
|
|
157
160
|
},
|
|
158
161
|
`${re}attaching gearbox sdk`
|
|
159
162
|
);
|
|
160
|
-
if (!!blockNumber && !
|
|
163
|
+
if (!!blockNumber && !opts.redstone?.historicTimestamp) {
|
|
161
164
|
this.logger?.warn(
|
|
162
165
|
`${re}attaching to fixed block number, but redstoneHistoricTimestamp is not set. price updates might fail`
|
|
163
166
|
);
|
|
@@ -171,7 +174,7 @@ class GearboxSDK {
|
|
|
171
174
|
);
|
|
172
175
|
this.#currentBlock = block.number;
|
|
173
176
|
this.#timestamp = block.timestamp;
|
|
174
|
-
this.#
|
|
177
|
+
this.#priceFeeds = new import_pricefeeds.PriceFeedRegister(this, { redstone });
|
|
175
178
|
this.logger?.debug(
|
|
176
179
|
`${re}attach block number ${this.currentBlock} timestamp ${this.timestamp}`
|
|
177
180
|
);
|
|
@@ -207,7 +210,7 @@ class GearboxSDK {
|
|
|
207
210
|
return this;
|
|
208
211
|
}
|
|
209
212
|
#hydrate(options, state) {
|
|
210
|
-
const { logger: _logger, ...opts } = options;
|
|
213
|
+
const { logger: _logger, redstone, ...opts } = options;
|
|
211
214
|
if (state.version !== STATE_VERSION) {
|
|
212
215
|
throw new Error(
|
|
213
216
|
`hydrated state version is ${state.version}, but expected ${STATE_VERSION}`
|
|
@@ -215,7 +218,7 @@ class GearboxSDK {
|
|
|
215
218
|
}
|
|
216
219
|
this.#currentBlock = state.currentBlock;
|
|
217
220
|
this.#timestamp = state.timestamp;
|
|
218
|
-
this.#
|
|
221
|
+
this.#priceFeeds = new import_pricefeeds.PriceFeedRegister(this, { redstone });
|
|
219
222
|
this.#addressProvider = (0, import_core.hydrateAddressProvider)(this, state.addressProvider);
|
|
220
223
|
this.logger?.debug(
|
|
221
224
|
`address provider version: ${this.#addressProvider.version}`
|
|
@@ -241,15 +244,6 @@ class GearboxSDK {
|
|
|
241
244
|
}
|
|
242
245
|
return this;
|
|
243
246
|
}
|
|
244
|
-
#confugureRedstone(opts) {
|
|
245
|
-
const { redstoneGateways, redstoneHistoricTimestamp } = opts;
|
|
246
|
-
if (redstoneHistoricTimestamp) {
|
|
247
|
-
this.priceFeeds.redstoneUpdater.historicalTimestamp = redstoneHistoricTimestamp === true ? Number(this.timestamp) * 1e3 : redstoneHistoricTimestamp;
|
|
248
|
-
}
|
|
249
|
-
if (redstoneGateways?.length) {
|
|
250
|
-
this.priceFeeds.redstoneUpdater.gateways = redstoneGateways;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
247
|
/**
|
|
254
248
|
* Reattach SDK with the same config as before, without re-creating instance. Will load all state from scratch
|
|
255
249
|
* Be mindful of block number, for example
|
|
@@ -351,7 +345,7 @@ class GearboxSDK {
|
|
|
351
345
|
*/
|
|
352
346
|
async syncState(opts) {
|
|
353
347
|
let { blockNumber, timestamp, skipPriceUpdate } = opts ?? {};
|
|
354
|
-
if (this.#attachConfig?.
|
|
348
|
+
if (this.#attachConfig?.redstone?.historicTimestamp) {
|
|
355
349
|
throw new Error(
|
|
356
350
|
"syncState is not supported with redstoneHistoricTimestamp"
|
|
357
351
|
);
|
|
@@ -376,11 +370,12 @@ class GearboxSDK {
|
|
|
376
370
|
...Array.from(this.marketRegister.watchAddresses),
|
|
377
371
|
this.addressProvider.address
|
|
378
372
|
];
|
|
373
|
+
const fromBlock = this.currentBlock + 1n;
|
|
379
374
|
this.logger?.debug(
|
|
380
|
-
`getting logs from ${watchAddresses.length} addresses in [${
|
|
375
|
+
`getting logs from ${watchAddresses.length} addresses in [${fromBlock}:${blockNumber}]`
|
|
381
376
|
);
|
|
382
377
|
const logs = await (0, import_viem2.getLogsSafe)(this.provider.publicClient, {
|
|
383
|
-
fromBlock
|
|
378
|
+
fromBlock,
|
|
384
379
|
toBlock: blockNumber,
|
|
385
380
|
address: watchAddresses
|
|
386
381
|
});
|
|
@@ -434,6 +429,15 @@ class GearboxSDK {
|
|
|
434
429
|
}
|
|
435
430
|
return this.#timestamp;
|
|
436
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
434
|
+
*/
|
|
435
|
+
get priceFeeds() {
|
|
436
|
+
if (this.#priceFeeds === void 0) {
|
|
437
|
+
throw ERR_NOT_ATTACHED;
|
|
438
|
+
}
|
|
439
|
+
return this.#priceFeeds;
|
|
440
|
+
}
|
|
437
441
|
get gear() {
|
|
438
442
|
try {
|
|
439
443
|
const g = this.addressProvider.getAddress(import_constants.AP_GEAR_TOKEN, import_constants.NO_VERSION);
|
|
@@ -64,14 +64,17 @@ class MarketRegister extends import_base.SDKConstruct {
|
|
|
64
64
|
const nonDirtyOracles = [];
|
|
65
65
|
for (const m of this.markets) {
|
|
66
66
|
if (m.dirty) {
|
|
67
|
-
dirtyPools.push(m.pool
|
|
67
|
+
dirtyPools.push(m.pool);
|
|
68
68
|
} else {
|
|
69
69
|
nonDirtyOracles.push(m.priceOracle.address);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
if (dirtyPools.length) {
|
|
73
73
|
this.#logger?.debug(`need to reload ${dirtyPools.length} markets`);
|
|
74
|
-
await this.#loadMarkets(
|
|
74
|
+
await this.#loadMarkets(
|
|
75
|
+
Array.from(new Set(dirtyPools.map((p) => p.marketConfigurator.address))),
|
|
76
|
+
dirtyPools.map((p) => p.pool.address)
|
|
77
|
+
);
|
|
75
78
|
} else if (!skipPriceUpdate && nonDirtyOracles.length) {
|
|
76
79
|
this.#logger?.debug(
|
|
77
80
|
`syncing prices on ${nonDirtyOracles.length} oracles`
|
|
@@ -59,6 +59,9 @@ class MarketSuite extends import_base.SDKConstruct {
|
|
|
59
59
|
this.creditManagers.push(new import_credit.CreditSuite(sdk, marketData, i));
|
|
60
60
|
}
|
|
61
61
|
this.priceOracle = (0, import_oracle.getOrCreatePriceOracle)(sdk, marketData.priceOracle);
|
|
62
|
+
sdk.logger?.debug(
|
|
63
|
+
`oracle ${this.labelAddress(this.priceOracle.address)} has ${this.priceOracle.mainPriceFeeds.size} main and ${this.priceOracle.reservePriceFeeds.size} reserve price feeds`
|
|
64
|
+
);
|
|
62
65
|
}
|
|
63
66
|
get dirty() {
|
|
64
67
|
return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
|
|
@@ -294,9 +294,7 @@ class PriceOracleBaseContract extends import_base.BaseContract {
|
|
|
294
294
|
}
|
|
295
295
|
this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
|
|
296
296
|
}
|
|
297
|
-
this.
|
|
298
|
-
`Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
|
|
299
|
-
);
|
|
297
|
+
this.dirty = false;
|
|
300
298
|
}
|
|
301
299
|
#labelPriceFeed(address, usage, token) {
|
|
302
300
|
this.sdk.provider.addressLabels.set(address, (oldLabel) => {
|
|
@@ -44,6 +44,7 @@ class PoolSuite extends import_base.SDKConstruct {
|
|
|
44
44
|
pqk;
|
|
45
45
|
rateKeeper;
|
|
46
46
|
interestRateModel;
|
|
47
|
+
#marketConfigurator;
|
|
47
48
|
constructor(sdk, data) {
|
|
48
49
|
super(sdk);
|
|
49
50
|
this.pool = (0, import_createPool.default)(sdk, data.pool);
|
|
@@ -53,6 +54,7 @@ class PoolSuite extends import_base.SDKConstruct {
|
|
|
53
54
|
sdk,
|
|
54
55
|
data.interestRateModel
|
|
55
56
|
);
|
|
57
|
+
this.#marketConfigurator = data.configurator;
|
|
56
58
|
}
|
|
57
59
|
get gauge() {
|
|
58
60
|
if (this.rateKeeper instanceof import_GaugeContract.GaugeContract) {
|
|
@@ -78,6 +80,11 @@ class PoolSuite extends import_base.SDKConstruct {
|
|
|
78
80
|
`Interest rate model is not a linear model, but a ${this.interestRateModel.contractType}`
|
|
79
81
|
);
|
|
80
82
|
}
|
|
83
|
+
get marketConfigurator() {
|
|
84
|
+
return this.sdk.contracts.mustGet(
|
|
85
|
+
this.#marketConfigurator
|
|
86
|
+
);
|
|
87
|
+
}
|
|
81
88
|
get underlying() {
|
|
82
89
|
return this.pool.underlying;
|
|
83
90
|
}
|
|
@@ -50,10 +50,10 @@ class PriceFeedRegister extends import_base.SDKConstruct {
|
|
|
50
50
|
#feeds = new import_utils.AddressMap(void 0, "priceFeeds");
|
|
51
51
|
#latestUpdate;
|
|
52
52
|
redstoneUpdater;
|
|
53
|
-
constructor(sdk) {
|
|
53
|
+
constructor(sdk, opts = {}) {
|
|
54
54
|
super(sdk);
|
|
55
55
|
this.logger = (0, import_utils.childLogger)("PriceFeedRegister", sdk.logger);
|
|
56
|
-
this.redstoneUpdater = new import_RedstoneUpdater.RedstoneUpdater(sdk);
|
|
56
|
+
this.redstoneUpdater = new import_RedstoneUpdater.RedstoneUpdater(sdk, opts?.redstone);
|
|
57
57
|
}
|
|
58
58
|
addHook = this.#hooks.addHook.bind(this.#hooks);
|
|
59
59
|
removeHook = this.#hooks.removeHook.bind(this.#hooks);
|
|
@@ -126,12 +126,11 @@ class PriceFeedRegister extends import_base.SDKConstruct {
|
|
|
126
126
|
* Loads PARTIAL information about all updatable price feeds from MarketCompressor
|
|
127
127
|
* This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
|
|
128
128
|
*/
|
|
129
|
-
async getPartialUpdatablePriceFeeds(
|
|
129
|
+
async getPartialUpdatablePriceFeeds(configurators, pools) {
|
|
130
130
|
const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
|
|
131
131
|
import_constants.AP_PRICE_FEED_COMPRESSOR,
|
|
132
132
|
import_constants.VERSION_RANGE_310
|
|
133
133
|
);
|
|
134
|
-
const configurators = marketConfigurators ?? this.sdk.marketRegister.marketConfigurators.map((mc) => mc.address);
|
|
135
134
|
this.logger?.debug(
|
|
136
135
|
{ configurators, pools },
|
|
137
136
|
`calling getUpdatablePriceFeeds in block ${this.sdk.currentBlock}`
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var RedstoneCache_exports = {};
|
|
20
|
+
__export(RedstoneCache_exports, {
|
|
21
|
+
RedstoneCache: () => RedstoneCache
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(RedstoneCache_exports);
|
|
24
|
+
class RedstoneCache {
|
|
25
|
+
#cache = /* @__PURE__ */ new Map();
|
|
26
|
+
#ttlMs;
|
|
27
|
+
#historical;
|
|
28
|
+
constructor(opts) {
|
|
29
|
+
this.#ttlMs = opts.ttl;
|
|
30
|
+
this.#historical = opts.historical;
|
|
31
|
+
}
|
|
32
|
+
get(dataServiceId, dataFeedId, uniqueSignersCount) {
|
|
33
|
+
const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
|
|
34
|
+
const data = this.#cache.get(key);
|
|
35
|
+
if (!data) {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
if (this.#expired(data)) {
|
|
39
|
+
this.#cache.delete(key);
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
set(dataServiceId, dataFeedId, uniqueSignersCount, value) {
|
|
45
|
+
const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
|
|
46
|
+
this.#cache.set(key, value);
|
|
47
|
+
}
|
|
48
|
+
#expired(value) {
|
|
49
|
+
if (this.#historical) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return value.timestamp * 1e3 + this.#ttlMs < Date.now();
|
|
53
|
+
}
|
|
54
|
+
#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount) {
|
|
55
|
+
return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
59
|
+
0 && (module.exports = {
|
|
60
|
+
RedstoneCache
|
|
61
|
+
});
|
|
@@ -27,6 +27,7 @@ var import_protocol = require("@redstone-finance/protocol");
|
|
|
27
27
|
var import_viem = require("viem");
|
|
28
28
|
var import_base = require("../../base/index.js");
|
|
29
29
|
var import_utils = require("../../utils/index.js");
|
|
30
|
+
var import_RedstoneCache = require("./RedstoneCache.js");
|
|
30
31
|
const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
|
|
31
32
|
const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
|
|
32
33
|
class RedstoneUpdateTx {
|
|
@@ -54,24 +55,29 @@ class RedstoneUpdateTx {
|
|
|
54
55
|
}
|
|
55
56
|
class RedstoneUpdater extends import_base.SDKConstruct {
|
|
56
57
|
#logger;
|
|
57
|
-
#cache
|
|
58
|
+
#cache;
|
|
58
59
|
#historicalTimestampMs;
|
|
59
60
|
#gateways;
|
|
60
|
-
|
|
61
|
+
#ignoreMissingFeeds;
|
|
62
|
+
#enableLogging;
|
|
63
|
+
constructor(sdk, opts = {}) {
|
|
61
64
|
super(sdk);
|
|
62
65
|
this.#logger = (0, import_utils.childLogger)("RedstoneUpdater", sdk.logger);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
this.#ignoreMissingFeeds = opts.ignoreMissingFeeds;
|
|
67
|
+
this.#enableLogging = opts.enableLogging;
|
|
68
|
+
this.#gateways = opts.gateways?.length ? opts.gateways : void 0;
|
|
69
|
+
let ts = opts.historicTimestamp;
|
|
70
|
+
if (ts) {
|
|
71
|
+
ts = ts === true ? Number(this.sdk.timestamp) * 1e3 : ts;
|
|
72
|
+
this.#historicalTimestampMs = 6e4 * Math.floor(ts / 6e4);
|
|
73
|
+
}
|
|
74
|
+
this.#cache = new import_RedstoneCache.RedstoneCache({
|
|
75
|
+
// currently staleness period is 240 seconds on all networks, add some buffer
|
|
76
|
+
// this period of 4 minutes is selected based on time that is required for user to sign transaction with wallet
|
|
77
|
+
// so it's unlikely to decrease
|
|
78
|
+
ttl: opts.cacheTTL ?? 225 * 1e3,
|
|
79
|
+
historical: !!ts
|
|
80
|
+
});
|
|
75
81
|
}
|
|
76
82
|
async getUpdateTxs(feeds, logContext = {}) {
|
|
77
83
|
this.#logger?.debug(
|
|
@@ -145,40 +151,28 @@ class RedstoneUpdater extends import_base.SDKConstruct {
|
|
|
145
151
|
const fromCache = [];
|
|
146
152
|
const uncached = [];
|
|
147
153
|
for (const dataFeedId of dataFeedsIds) {
|
|
148
|
-
const
|
|
154
|
+
const cached = this.#cache.get(
|
|
149
155
|
dataServiceId,
|
|
150
156
|
dataFeedId,
|
|
151
|
-
uniqueSignersCount
|
|
152
|
-
this.#historicalTimestampMs
|
|
157
|
+
uniqueSignersCount
|
|
153
158
|
);
|
|
154
|
-
|
|
155
|
-
if (this.#historicalTimestampMs && !!cached) {
|
|
159
|
+
if (cached) {
|
|
156
160
|
fromCache.push({ ...cached, cached: true });
|
|
157
161
|
} else {
|
|
158
162
|
uncached.push(dataFeedId);
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
dataServiceId,
|
|
169
|
-
resp.dataFeedId,
|
|
170
|
-
uniqueSignersCount,
|
|
171
|
-
this.#historicalTimestampMs
|
|
172
|
-
);
|
|
173
|
-
this.#cache.set(key, resp);
|
|
174
|
-
}
|
|
165
|
+
const fromRedstone = await this.#fetchPayloads(
|
|
166
|
+
dataServiceId,
|
|
167
|
+
new Set(uncached),
|
|
168
|
+
uniqueSignersCount
|
|
169
|
+
);
|
|
170
|
+
for (const resp of fromRedstone) {
|
|
171
|
+
this.#cache.set(dataServiceId, resp.dataFeedId, uniqueSignersCount, resp);
|
|
175
172
|
}
|
|
176
173
|
this.#logger?.debug(
|
|
177
174
|
`got ${fromRedstone.length} new redstone updates and ${fromCache.length} from cache`
|
|
178
175
|
);
|
|
179
|
-
if (fromRedstoneResp.status === "rejected") {
|
|
180
|
-
this.#logger?.error(fromRedstoneResp.reason);
|
|
181
|
-
}
|
|
182
176
|
return [...fromCache, ...fromRedstone];
|
|
183
177
|
}
|
|
184
178
|
/**
|
|
@@ -203,7 +197,9 @@ class RedstoneUpdater extends import_base.SDKConstruct {
|
|
|
203
197
|
dataPackagesIds,
|
|
204
198
|
uniqueSignersCount,
|
|
205
199
|
historicalTimestamp: this.#historicalTimestampMs,
|
|
206
|
-
urls: this.#gateways
|
|
200
|
+
urls: this.#gateways,
|
|
201
|
+
ignoreMissingFeed: this.#ignoreMissingFeeds,
|
|
202
|
+
enableEnhancedLogs: this.#enableLogging
|
|
207
203
|
});
|
|
208
204
|
const dataPayload = await (0, import_utils.retry)(
|
|
209
205
|
() => wrapper.prepareRedstonePayload(true),
|
|
@@ -229,9 +225,6 @@ class RedstoneUpdater extends import_base.SDKConstruct {
|
|
|
229
225
|
});
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
|
-
function cacheKey(dataServiceId, dataFeedId, uniqueSignersCount, historicalTimestamp = 0) {
|
|
233
|
-
return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}:${historicalTimestamp}`;
|
|
234
|
-
}
|
|
235
228
|
function groupDataPackages(signedDataPackages) {
|
|
236
229
|
const packagesByDataFeedId = {};
|
|
237
230
|
for (const p of signedDataPackages) {
|
|
@@ -98,11 +98,11 @@ class GearboxBackendApi {
|
|
|
98
98
|
static getStaticTokenUrl = () => STATIC_TOKEN;
|
|
99
99
|
static getRewardsMerkleUrl = (network, root, account) => {
|
|
100
100
|
const path = `${network}_${root.slice(2)}/${account.slice(2, 4)}`;
|
|
101
|
-
const url = `https://am.gearbox.
|
|
101
|
+
const url = `https://am.gearbox.finance/${path.toLowerCase()}.json`;
|
|
102
102
|
return url;
|
|
103
103
|
};
|
|
104
104
|
static getNFTMerkleUrl = (network, root) => {
|
|
105
|
-
const url = `https://dm.gearbox.
|
|
105
|
+
const url = `https://dm.gearbox.finance/${network.toLowerCase()}_${root}.json`;
|
|
106
106
|
return url;
|
|
107
107
|
};
|
|
108
108
|
static getLeaderboardUrl = (url, chainId) => {
|
|
@@ -27,6 +27,7 @@ __reExport(utils_exports, require("./json.js"), module.exports);
|
|
|
27
27
|
__reExport(utils_exports, require("./mappers.js"), module.exports);
|
|
28
28
|
__reExport(utils_exports, require("./retry.js"), module.exports);
|
|
29
29
|
__reExport(utils_exports, require("./toAddress.js"), module.exports);
|
|
30
|
+
__reExport(utils_exports, require("./type-utils.js"), module.exports);
|
|
30
31
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31
32
|
0 && (module.exports = {
|
|
32
33
|
...require("./AddressMap.js"),
|
|
@@ -40,5 +41,6 @@ __reExport(utils_exports, require("./toAddress.js"), module.exports);
|
|
|
40
41
|
...require("./json.js"),
|
|
41
42
|
...require("./mappers.js"),
|
|
42
43
|
...require("./retry.js"),
|
|
43
|
-
...require("./toAddress.js")
|
|
44
|
+
...require("./toAddress.js"),
|
|
45
|
+
...require("./type-utils.js")
|
|
44
46
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var type_utils_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(type_utils_exports);
|
|
@@ -48,16 +48,13 @@ class GearboxSDK {
|
|
|
48
48
|
#attachConfig;
|
|
49
49
|
// Collection of markets
|
|
50
50
|
#marketRegister;
|
|
51
|
+
#priceFeeds;
|
|
51
52
|
logger;
|
|
52
53
|
/**
|
|
53
54
|
* Interest rate models can be reused across chain (and SDK operates on chain level)
|
|
54
55
|
* TODO: use whatever interface is necessary for InterestRateModels
|
|
55
56
|
*/
|
|
56
57
|
interestRateModels = new AddressMap();
|
|
57
|
-
/**
|
|
58
|
-
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
59
|
-
*/
|
|
60
|
-
priceFeeds;
|
|
61
58
|
/**
|
|
62
59
|
* Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
|
|
63
60
|
*/
|
|
@@ -80,9 +77,10 @@ class GearboxSDK {
|
|
|
80
77
|
logger,
|
|
81
78
|
plugins,
|
|
82
79
|
blockNumber,
|
|
83
|
-
|
|
80
|
+
redstone,
|
|
84
81
|
ignoreUpdateablePrices,
|
|
85
|
-
marketConfigurators: mcs
|
|
82
|
+
marketConfigurators: mcs,
|
|
83
|
+
strictContractTypes
|
|
86
84
|
} = options;
|
|
87
85
|
let { networkType, addressProvider, chainId } = options;
|
|
88
86
|
const attachClient = createPublicClient({
|
|
@@ -106,28 +104,33 @@ class GearboxSDK {
|
|
|
106
104
|
return new GearboxSDK({
|
|
107
105
|
provider,
|
|
108
106
|
logger,
|
|
109
|
-
plugins
|
|
107
|
+
plugins,
|
|
108
|
+
strictContractTypes
|
|
110
109
|
}).#attach({
|
|
111
110
|
addressProvider,
|
|
112
111
|
blockNumber,
|
|
113
|
-
redstoneHistoricTimestamp,
|
|
114
112
|
ignoreUpdateablePrices,
|
|
115
|
-
marketConfigurators
|
|
113
|
+
marketConfigurators,
|
|
114
|
+
redstone
|
|
116
115
|
});
|
|
117
116
|
}
|
|
118
117
|
static hydrate(options, state) {
|
|
119
|
-
const { logger, plugins, ...rest } = options;
|
|
118
|
+
const { logger, plugins, strictContractTypes, ...rest } = options;
|
|
120
119
|
const provider = new Provider({
|
|
121
120
|
...rest,
|
|
122
121
|
chainId: state.chainId,
|
|
123
122
|
networkType: state.network
|
|
124
123
|
});
|
|
125
|
-
return new GearboxSDK({
|
|
124
|
+
return new GearboxSDK({
|
|
125
|
+
provider,
|
|
126
|
+
plugins,
|
|
127
|
+
logger,
|
|
128
|
+
strictContractTypes
|
|
129
|
+
}).#hydrate(rest, state);
|
|
126
130
|
}
|
|
127
131
|
constructor(options) {
|
|
128
132
|
this.#provider = options.provider;
|
|
129
133
|
this.logger = options.logger;
|
|
130
|
-
this.priceFeeds = new PriceFeedRegister(this);
|
|
131
134
|
this.strictContractTypes = options.strictContractTypes ?? false;
|
|
132
135
|
const pluginsInstances = {};
|
|
133
136
|
for (const [name, Plugin] of TypedObjectUtils.entries(
|
|
@@ -141,9 +144,9 @@ class GearboxSDK {
|
|
|
141
144
|
const {
|
|
142
145
|
addressProvider,
|
|
143
146
|
blockNumber,
|
|
144
|
-
redstoneHistoricTimestamp,
|
|
145
147
|
ignoreUpdateablePrices,
|
|
146
|
-
marketConfigurators
|
|
148
|
+
marketConfigurators,
|
|
149
|
+
redstone
|
|
147
150
|
} = opts;
|
|
148
151
|
const re = this.#attachConfig ? "re" : "";
|
|
149
152
|
this.logger?.info(
|
|
@@ -155,7 +158,7 @@ class GearboxSDK {
|
|
|
155
158
|
},
|
|
156
159
|
`${re}attaching gearbox sdk`
|
|
157
160
|
);
|
|
158
|
-
if (!!blockNumber && !
|
|
161
|
+
if (!!blockNumber && !opts.redstone?.historicTimestamp) {
|
|
159
162
|
this.logger?.warn(
|
|
160
163
|
`${re}attaching to fixed block number, but redstoneHistoricTimestamp is not set. price updates might fail`
|
|
161
164
|
);
|
|
@@ -169,7 +172,7 @@ class GearboxSDK {
|
|
|
169
172
|
);
|
|
170
173
|
this.#currentBlock = block.number;
|
|
171
174
|
this.#timestamp = block.timestamp;
|
|
172
|
-
this.#
|
|
175
|
+
this.#priceFeeds = new PriceFeedRegister(this, { redstone });
|
|
173
176
|
this.logger?.debug(
|
|
174
177
|
`${re}attach block number ${this.currentBlock} timestamp ${this.timestamp}`
|
|
175
178
|
);
|
|
@@ -205,7 +208,7 @@ class GearboxSDK {
|
|
|
205
208
|
return this;
|
|
206
209
|
}
|
|
207
210
|
#hydrate(options, state) {
|
|
208
|
-
const { logger: _logger, ...opts } = options;
|
|
211
|
+
const { logger: _logger, redstone, ...opts } = options;
|
|
209
212
|
if (state.version !== STATE_VERSION) {
|
|
210
213
|
throw new Error(
|
|
211
214
|
`hydrated state version is ${state.version}, but expected ${STATE_VERSION}`
|
|
@@ -213,7 +216,7 @@ class GearboxSDK {
|
|
|
213
216
|
}
|
|
214
217
|
this.#currentBlock = state.currentBlock;
|
|
215
218
|
this.#timestamp = state.timestamp;
|
|
216
|
-
this.#
|
|
219
|
+
this.#priceFeeds = new PriceFeedRegister(this, { redstone });
|
|
217
220
|
this.#addressProvider = hydrateAddressProvider(this, state.addressProvider);
|
|
218
221
|
this.logger?.debug(
|
|
219
222
|
`address provider version: ${this.#addressProvider.version}`
|
|
@@ -239,15 +242,6 @@ class GearboxSDK {
|
|
|
239
242
|
}
|
|
240
243
|
return this;
|
|
241
244
|
}
|
|
242
|
-
#confugureRedstone(opts) {
|
|
243
|
-
const { redstoneGateways, redstoneHistoricTimestamp } = opts;
|
|
244
|
-
if (redstoneHistoricTimestamp) {
|
|
245
|
-
this.priceFeeds.redstoneUpdater.historicalTimestamp = redstoneHistoricTimestamp === true ? Number(this.timestamp) * 1e3 : redstoneHistoricTimestamp;
|
|
246
|
-
}
|
|
247
|
-
if (redstoneGateways?.length) {
|
|
248
|
-
this.priceFeeds.redstoneUpdater.gateways = redstoneGateways;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
245
|
/**
|
|
252
246
|
* Reattach SDK with the same config as before, without re-creating instance. Will load all state from scratch
|
|
253
247
|
* Be mindful of block number, for example
|
|
@@ -349,7 +343,7 @@ class GearboxSDK {
|
|
|
349
343
|
*/
|
|
350
344
|
async syncState(opts) {
|
|
351
345
|
let { blockNumber, timestamp, skipPriceUpdate } = opts ?? {};
|
|
352
|
-
if (this.#attachConfig?.
|
|
346
|
+
if (this.#attachConfig?.redstone?.historicTimestamp) {
|
|
353
347
|
throw new Error(
|
|
354
348
|
"syncState is not supported with redstoneHistoricTimestamp"
|
|
355
349
|
);
|
|
@@ -374,11 +368,12 @@ class GearboxSDK {
|
|
|
374
368
|
...Array.from(this.marketRegister.watchAddresses),
|
|
375
369
|
this.addressProvider.address
|
|
376
370
|
];
|
|
371
|
+
const fromBlock = this.currentBlock + 1n;
|
|
377
372
|
this.logger?.debug(
|
|
378
|
-
`getting logs from ${watchAddresses.length} addresses in [${
|
|
373
|
+
`getting logs from ${watchAddresses.length} addresses in [${fromBlock}:${blockNumber}]`
|
|
379
374
|
);
|
|
380
375
|
const logs = await getLogsSafe(this.provider.publicClient, {
|
|
381
|
-
fromBlock
|
|
376
|
+
fromBlock,
|
|
382
377
|
toBlock: blockNumber,
|
|
383
378
|
address: watchAddresses
|
|
384
379
|
});
|
|
@@ -432,6 +427,15 @@ class GearboxSDK {
|
|
|
432
427
|
}
|
|
433
428
|
return this.#timestamp;
|
|
434
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
432
|
+
*/
|
|
433
|
+
get priceFeeds() {
|
|
434
|
+
if (this.#priceFeeds === void 0) {
|
|
435
|
+
throw ERR_NOT_ATTACHED;
|
|
436
|
+
}
|
|
437
|
+
return this.#priceFeeds;
|
|
438
|
+
}
|
|
435
439
|
get gear() {
|
|
436
440
|
try {
|
|
437
441
|
const g = this.addressProvider.getAddress(AP_GEAR_TOKEN, NO_VERSION);
|
|
@@ -45,14 +45,17 @@ class MarketRegister extends SDKConstruct {
|
|
|
45
45
|
const nonDirtyOracles = [];
|
|
46
46
|
for (const m of this.markets) {
|
|
47
47
|
if (m.dirty) {
|
|
48
|
-
dirtyPools.push(m.pool
|
|
48
|
+
dirtyPools.push(m.pool);
|
|
49
49
|
} else {
|
|
50
50
|
nonDirtyOracles.push(m.priceOracle.address);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
if (dirtyPools.length) {
|
|
54
54
|
this.#logger?.debug(`need to reload ${dirtyPools.length} markets`);
|
|
55
|
-
await this.#loadMarkets(
|
|
55
|
+
await this.#loadMarkets(
|
|
56
|
+
Array.from(new Set(dirtyPools.map((p) => p.marketConfigurator.address))),
|
|
57
|
+
dirtyPools.map((p) => p.pool.address)
|
|
58
|
+
);
|
|
56
59
|
} else if (!skipPriceUpdate && nonDirtyOracles.length) {
|
|
57
60
|
this.#logger?.debug(
|
|
58
61
|
`syncing prices on ${nonDirtyOracles.length} oracles`
|
|
@@ -36,6 +36,9 @@ class MarketSuite extends SDKConstruct {
|
|
|
36
36
|
this.creditManagers.push(new CreditSuite(sdk, marketData, i));
|
|
37
37
|
}
|
|
38
38
|
this.priceOracle = getOrCreatePriceOracle(sdk, marketData.priceOracle);
|
|
39
|
+
sdk.logger?.debug(
|
|
40
|
+
`oracle ${this.labelAddress(this.priceOracle.address)} has ${this.priceOracle.mainPriceFeeds.size} main and ${this.priceOracle.reservePriceFeeds.size} reserve price feeds`
|
|
41
|
+
);
|
|
39
42
|
}
|
|
40
43
|
get dirty() {
|
|
41
44
|
return this.configurator.dirty || this.pool.dirty || this.priceOracle.dirty || this.creditManagers.some((cm) => cm.dirty);
|
|
@@ -265,9 +265,7 @@ class PriceOracleBaseContract extends BaseContract {
|
|
|
265
265
|
}
|
|
266
266
|
this.#labelPriceFeed(priceFeed, reserve ? "Reserve" : "Main", token);
|
|
267
267
|
}
|
|
268
|
-
this.
|
|
269
|
-
`Got ${this.mainPriceFeeds.size} main and ${this.reservePriceFeeds.size} reserve price feeds`
|
|
270
|
-
);
|
|
268
|
+
this.dirty = false;
|
|
271
269
|
}
|
|
272
270
|
#labelPriceFeed(address, usage, token) {
|
|
273
271
|
this.sdk.provider.addressLabels.set(address, (oldLabel) => {
|
|
@@ -11,6 +11,7 @@ class PoolSuite extends SDKConstruct {
|
|
|
11
11
|
pqk;
|
|
12
12
|
rateKeeper;
|
|
13
13
|
interestRateModel;
|
|
14
|
+
#marketConfigurator;
|
|
14
15
|
constructor(sdk, data) {
|
|
15
16
|
super(sdk);
|
|
16
17
|
this.pool = createPool(sdk, data.pool);
|
|
@@ -20,6 +21,7 @@ class PoolSuite extends SDKConstruct {
|
|
|
20
21
|
sdk,
|
|
21
22
|
data.interestRateModel
|
|
22
23
|
);
|
|
24
|
+
this.#marketConfigurator = data.configurator;
|
|
23
25
|
}
|
|
24
26
|
get gauge() {
|
|
25
27
|
if (this.rateKeeper instanceof GaugeContract) {
|
|
@@ -45,6 +47,11 @@ class PoolSuite extends SDKConstruct {
|
|
|
45
47
|
`Interest rate model is not a linear model, but a ${this.interestRateModel.contractType}`
|
|
46
48
|
);
|
|
47
49
|
}
|
|
50
|
+
get marketConfigurator() {
|
|
51
|
+
return this.sdk.contracts.mustGet(
|
|
52
|
+
this.#marketConfigurator
|
|
53
|
+
);
|
|
54
|
+
}
|
|
48
55
|
get underlying() {
|
|
49
56
|
return this.pool.underlying;
|
|
50
57
|
}
|
|
@@ -33,10 +33,10 @@ class PriceFeedRegister extends SDKConstruct {
|
|
|
33
33
|
#feeds = new AddressMap(void 0, "priceFeeds");
|
|
34
34
|
#latestUpdate;
|
|
35
35
|
redstoneUpdater;
|
|
36
|
-
constructor(sdk) {
|
|
36
|
+
constructor(sdk, opts = {}) {
|
|
37
37
|
super(sdk);
|
|
38
38
|
this.logger = childLogger("PriceFeedRegister", sdk.logger);
|
|
39
|
-
this.redstoneUpdater = new RedstoneUpdater(sdk);
|
|
39
|
+
this.redstoneUpdater = new RedstoneUpdater(sdk, opts?.redstone);
|
|
40
40
|
}
|
|
41
41
|
addHook = this.#hooks.addHook.bind(this.#hooks);
|
|
42
42
|
removeHook = this.#hooks.removeHook.bind(this.#hooks);
|
|
@@ -109,12 +109,11 @@ class PriceFeedRegister extends SDKConstruct {
|
|
|
109
109
|
* Loads PARTIAL information about all updatable price feeds from MarketCompressor
|
|
110
110
|
* This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
|
|
111
111
|
*/
|
|
112
|
-
async getPartialUpdatablePriceFeeds(
|
|
112
|
+
async getPartialUpdatablePriceFeeds(configurators, pools) {
|
|
113
113
|
const [priceFeedCompressorAddress] = this.sdk.addressProvider.mustGetLatest(
|
|
114
114
|
AP_PRICE_FEED_COMPRESSOR,
|
|
115
115
|
VERSION_RANGE_310
|
|
116
116
|
);
|
|
117
|
-
const configurators = marketConfigurators ?? this.sdk.marketRegister.marketConfigurators.map((mc) => mc.address);
|
|
118
117
|
this.logger?.debug(
|
|
119
118
|
{ configurators, pools },
|
|
120
119
|
`calling getUpdatablePriceFeeds in block ${this.sdk.currentBlock}`
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class RedstoneCache {
|
|
2
|
+
#cache = /* @__PURE__ */ new Map();
|
|
3
|
+
#ttlMs;
|
|
4
|
+
#historical;
|
|
5
|
+
constructor(opts) {
|
|
6
|
+
this.#ttlMs = opts.ttl;
|
|
7
|
+
this.#historical = opts.historical;
|
|
8
|
+
}
|
|
9
|
+
get(dataServiceId, dataFeedId, uniqueSignersCount) {
|
|
10
|
+
const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
|
|
11
|
+
const data = this.#cache.get(key);
|
|
12
|
+
if (!data) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
if (this.#expired(data)) {
|
|
16
|
+
this.#cache.delete(key);
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
set(dataServiceId, dataFeedId, uniqueSignersCount, value) {
|
|
22
|
+
const key = this.#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount);
|
|
23
|
+
this.#cache.set(key, value);
|
|
24
|
+
}
|
|
25
|
+
#expired(value) {
|
|
26
|
+
if (this.#historical) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return value.timestamp * 1e3 + this.#ttlMs < Date.now();
|
|
30
|
+
}
|
|
31
|
+
#cacheKey(dataServiceId, dataFeedId, uniqueSignersCount) {
|
|
32
|
+
return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
RedstoneCache
|
|
37
|
+
};
|
|
@@ -3,6 +3,7 @@ import { RedstonePayload } from "@redstone-finance/protocol";
|
|
|
3
3
|
import { encodeAbiParameters, toBytes } from "viem";
|
|
4
4
|
import { SDKConstruct } from "../../base/index.js";
|
|
5
5
|
import { AddressMap, childLogger, retry } from "../../utils/index.js";
|
|
6
|
+
import { RedstoneCache } from "./RedstoneCache.js";
|
|
6
7
|
const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
|
|
7
8
|
const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
|
|
8
9
|
class RedstoneUpdateTx {
|
|
@@ -30,24 +31,29 @@ class RedstoneUpdateTx {
|
|
|
30
31
|
}
|
|
31
32
|
class RedstoneUpdater extends SDKConstruct {
|
|
32
33
|
#logger;
|
|
33
|
-
#cache
|
|
34
|
+
#cache;
|
|
34
35
|
#historicalTimestampMs;
|
|
35
36
|
#gateways;
|
|
36
|
-
|
|
37
|
+
#ignoreMissingFeeds;
|
|
38
|
+
#enableLogging;
|
|
39
|
+
constructor(sdk, opts = {}) {
|
|
37
40
|
super(sdk);
|
|
38
41
|
this.#logger = childLogger("RedstoneUpdater", sdk.logger);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
this.#ignoreMissingFeeds = opts.ignoreMissingFeeds;
|
|
43
|
+
this.#enableLogging = opts.enableLogging;
|
|
44
|
+
this.#gateways = opts.gateways?.length ? opts.gateways : void 0;
|
|
45
|
+
let ts = opts.historicTimestamp;
|
|
46
|
+
if (ts) {
|
|
47
|
+
ts = ts === true ? Number(this.sdk.timestamp) * 1e3 : ts;
|
|
48
|
+
this.#historicalTimestampMs = 6e4 * Math.floor(ts / 6e4);
|
|
49
|
+
}
|
|
50
|
+
this.#cache = new RedstoneCache({
|
|
51
|
+
// currently staleness period is 240 seconds on all networks, add some buffer
|
|
52
|
+
// this period of 4 minutes is selected based on time that is required for user to sign transaction with wallet
|
|
53
|
+
// so it's unlikely to decrease
|
|
54
|
+
ttl: opts.cacheTTL ?? 225 * 1e3,
|
|
55
|
+
historical: !!ts
|
|
56
|
+
});
|
|
51
57
|
}
|
|
52
58
|
async getUpdateTxs(feeds, logContext = {}) {
|
|
53
59
|
this.#logger?.debug(
|
|
@@ -121,40 +127,28 @@ class RedstoneUpdater extends SDKConstruct {
|
|
|
121
127
|
const fromCache = [];
|
|
122
128
|
const uncached = [];
|
|
123
129
|
for (const dataFeedId of dataFeedsIds) {
|
|
124
|
-
const
|
|
130
|
+
const cached = this.#cache.get(
|
|
125
131
|
dataServiceId,
|
|
126
132
|
dataFeedId,
|
|
127
|
-
uniqueSignersCount
|
|
128
|
-
this.#historicalTimestampMs
|
|
133
|
+
uniqueSignersCount
|
|
129
134
|
);
|
|
130
|
-
|
|
131
|
-
if (this.#historicalTimestampMs && !!cached) {
|
|
135
|
+
if (cached) {
|
|
132
136
|
fromCache.push({ ...cached, cached: true });
|
|
133
137
|
} else {
|
|
134
138
|
uncached.push(dataFeedId);
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
dataServiceId,
|
|
145
|
-
resp.dataFeedId,
|
|
146
|
-
uniqueSignersCount,
|
|
147
|
-
this.#historicalTimestampMs
|
|
148
|
-
);
|
|
149
|
-
this.#cache.set(key, resp);
|
|
150
|
-
}
|
|
141
|
+
const fromRedstone = await this.#fetchPayloads(
|
|
142
|
+
dataServiceId,
|
|
143
|
+
new Set(uncached),
|
|
144
|
+
uniqueSignersCount
|
|
145
|
+
);
|
|
146
|
+
for (const resp of fromRedstone) {
|
|
147
|
+
this.#cache.set(dataServiceId, resp.dataFeedId, uniqueSignersCount, resp);
|
|
151
148
|
}
|
|
152
149
|
this.#logger?.debug(
|
|
153
150
|
`got ${fromRedstone.length} new redstone updates and ${fromCache.length} from cache`
|
|
154
151
|
);
|
|
155
|
-
if (fromRedstoneResp.status === "rejected") {
|
|
156
|
-
this.#logger?.error(fromRedstoneResp.reason);
|
|
157
|
-
}
|
|
158
152
|
return [...fromCache, ...fromRedstone];
|
|
159
153
|
}
|
|
160
154
|
/**
|
|
@@ -179,7 +173,9 @@ class RedstoneUpdater extends SDKConstruct {
|
|
|
179
173
|
dataPackagesIds,
|
|
180
174
|
uniqueSignersCount,
|
|
181
175
|
historicalTimestamp: this.#historicalTimestampMs,
|
|
182
|
-
urls: this.#gateways
|
|
176
|
+
urls: this.#gateways,
|
|
177
|
+
ignoreMissingFeed: this.#ignoreMissingFeeds,
|
|
178
|
+
enableEnhancedLogs: this.#enableLogging
|
|
183
179
|
});
|
|
184
180
|
const dataPayload = await retry(
|
|
185
181
|
() => wrapper.prepareRedstonePayload(true),
|
|
@@ -205,9 +201,6 @@ class RedstoneUpdater extends SDKConstruct {
|
|
|
205
201
|
});
|
|
206
202
|
}
|
|
207
203
|
}
|
|
208
|
-
function cacheKey(dataServiceId, dataFeedId, uniqueSignersCount, historicalTimestamp = 0) {
|
|
209
|
-
return `${dataServiceId}:${dataFeedId}:${uniqueSignersCount}:${historicalTimestamp}`;
|
|
210
|
-
}
|
|
211
204
|
function groupDataPackages(signedDataPackages) {
|
|
212
205
|
const packagesByDataFeedId = {};
|
|
213
206
|
for (const p of signedDataPackages) {
|
|
@@ -69,11 +69,11 @@ class GearboxBackendApi {
|
|
|
69
69
|
static getStaticTokenUrl = () => STATIC_TOKEN;
|
|
70
70
|
static getRewardsMerkleUrl = (network, root, account) => {
|
|
71
71
|
const path = `${network}_${root.slice(2)}/${account.slice(2, 4)}`;
|
|
72
|
-
const url = `https://am.gearbox.
|
|
72
|
+
const url = `https://am.gearbox.finance/${path.toLowerCase()}.json`;
|
|
73
73
|
return url;
|
|
74
74
|
};
|
|
75
75
|
static getNFTMerkleUrl = (network, root) => {
|
|
76
|
-
const url = `https://dm.gearbox.
|
|
76
|
+
const url = `https://dm.gearbox.finance/${network.toLowerCase()}_${root}.json`;
|
|
77
77
|
return url;
|
|
78
78
|
};
|
|
79
79
|
static getLeaderboardUrl = (url, chainId) => {
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ import type { IAddressProviderContract } from "./core/index.js";
|
|
|
7
7
|
import { BotListContract, GearStakingContract } from "./core/index.js";
|
|
8
8
|
import { MarketRegister } from "./market/MarketRegister.js";
|
|
9
9
|
import { PriceFeedRegister } from "./market/pricefeeds/index.js";
|
|
10
|
+
import type { RedstoneOptions } from "./market/pricefeeds/RedstoneUpdater.js";
|
|
10
11
|
import { type PluginConstructorMap, type PluginsMap } from "./plugins/index.js";
|
|
11
12
|
import { type IRouterContract } from "./router/index.js";
|
|
12
13
|
import type { GearboxState, GearboxStateHuman, ILogger, MultiCall } from "./types/index.js";
|
|
@@ -28,15 +29,6 @@ export interface SDKOptions<Plugins extends PluginsMap> {
|
|
|
28
29
|
* Attach and load state at this specific block number
|
|
29
30
|
*/
|
|
30
31
|
blockNumber?: bigint | number;
|
|
31
|
-
/**
|
|
32
|
-
* Fixed redstone historic timestamp in ms
|
|
33
|
-
* Set to true to enable redstone historical mode using timestamp from attach block
|
|
34
|
-
*/
|
|
35
|
-
redstoneHistoricTimestamp?: number | true;
|
|
36
|
-
/**
|
|
37
|
-
* Override redstone gateways. Can be used to set caching proxies, to avoid rate limiting
|
|
38
|
-
*/
|
|
39
|
-
redstoneGateways?: string[];
|
|
40
32
|
/**
|
|
41
33
|
* Will skip updateable prices on attach and sync
|
|
42
34
|
* Makes things faster when your service is not intereseted in prices
|
|
@@ -54,6 +46,10 @@ export interface SDKOptions<Plugins extends PluginsMap> {
|
|
|
54
46
|
* Bring your own logger
|
|
55
47
|
*/
|
|
56
48
|
logger?: ILogger;
|
|
49
|
+
/**
|
|
50
|
+
* Options related to redstone price feeds
|
|
51
|
+
*/
|
|
52
|
+
redstone?: RedstoneOptions;
|
|
57
53
|
}
|
|
58
54
|
export type HydrateOptions<Plugins extends PluginsMap> = Omit<SDKOptions<Plugins>, "blockNumber" | "addressProvider" | "marketConfigurators">;
|
|
59
55
|
export interface SyncStateOptions {
|
|
@@ -73,10 +69,6 @@ export declare class GearboxSDK<const Plugins extends PluginsMap = {}> {
|
|
|
73
69
|
* TODO: use whatever interface is necessary for InterestRateModels
|
|
74
70
|
*/
|
|
75
71
|
readonly interestRateModels: AddressMap<BaseContract<readonly unknown[]>>;
|
|
76
|
-
/**
|
|
77
|
-
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
78
|
-
*/
|
|
79
|
-
readonly priceFeeds: PriceFeedRegister;
|
|
80
72
|
/**
|
|
81
73
|
* Will throw an error if contract type is not supported, otherwise will try to use generic contract first, if possible
|
|
82
74
|
*/
|
|
@@ -150,6 +142,10 @@ export declare class GearboxSDK<const Plugins extends PluginsMap = {}> {
|
|
|
150
142
|
get provider(): Provider;
|
|
151
143
|
get currentBlock(): bigint;
|
|
152
144
|
get timestamp(): bigint;
|
|
145
|
+
/**
|
|
146
|
+
* All price feeds known to sdk, without oracle-related data (stalenessPeriod, main/reserve, etc.)
|
|
147
|
+
*/
|
|
148
|
+
get priceFeeds(): PriceFeedRegister;
|
|
153
149
|
get gear(): Address | undefined;
|
|
154
150
|
get addressProvider(): IAddressProviderContract;
|
|
155
151
|
get botListContract(): BotListContract | undefined;
|
|
@@ -3,11 +3,13 @@ import type { MarketData } from "../../base/index.js";
|
|
|
3
3
|
import { SDKConstruct } from "../../base/index.js";
|
|
4
4
|
import type { GearboxSDK } from "../../GearboxSDK.js";
|
|
5
5
|
import type { PoolSuiteStateHuman } from "../../types/index.js";
|
|
6
|
+
import type { MarketConfiguratorContract } from "../MarketConfiguratorContract.js";
|
|
6
7
|
import { GaugeContract } from "./GaugeContract.js";
|
|
7
8
|
import { LinearInterestRateModelContract } from "./LinearInterestRateModelContract.js";
|
|
8
9
|
import { TumblerContract } from "./TumblerContract.js";
|
|
9
10
|
import type { IInterestRateModelContract, IRateKeeperContract, PoolContract, PoolQuotaKeeperContract } from "./types.js";
|
|
10
11
|
export declare class PoolSuite extends SDKConstruct {
|
|
12
|
+
#private;
|
|
11
13
|
readonly pool: PoolContract;
|
|
12
14
|
readonly pqk: PoolQuotaKeeperContract;
|
|
13
15
|
readonly rateKeeper: IRateKeeperContract;
|
|
@@ -16,6 +18,7 @@ export declare class PoolSuite extends SDKConstruct {
|
|
|
16
18
|
get gauge(): GaugeContract;
|
|
17
19
|
get tumbler(): TumblerContract;
|
|
18
20
|
get linearModel(): LinearInterestRateModelContract;
|
|
21
|
+
get marketConfigurator(): MarketConfiguratorContract;
|
|
19
22
|
get underlying(): Address;
|
|
20
23
|
get dirty(): boolean;
|
|
21
24
|
get watchAddresses(): Set<Address>;
|
|
@@ -5,7 +5,7 @@ import type { GearboxSDK } from "../../GearboxSDK.js";
|
|
|
5
5
|
import type { ILogger } from "../../types/index.js";
|
|
6
6
|
import type { IHooks } from "../../utils/internal/index.js";
|
|
7
7
|
import { type PartialPriceFeedTreeNode } from "./AbstractPriceFeed.js";
|
|
8
|
-
import type { RedstoneUpdateTask } from "./RedstoneUpdater.js";
|
|
8
|
+
import type { RedstoneOptions, RedstoneUpdateTask } from "./RedstoneUpdater.js";
|
|
9
9
|
import { RedstoneUpdater } from "./RedstoneUpdater.js";
|
|
10
10
|
import type { IPriceFeedContract, UpdatePriceFeedsResult } from "./types.js";
|
|
11
11
|
export type PriceFeedRegisterHooks = {
|
|
@@ -14,6 +14,9 @@ export type PriceFeedRegisterHooks = {
|
|
|
14
14
|
*/
|
|
15
15
|
updatesGenerated: [UpdatePriceFeedsResult];
|
|
16
16
|
};
|
|
17
|
+
export interface PriceFeedRegisterOptions {
|
|
18
|
+
redstone?: RedstoneOptions;
|
|
19
|
+
}
|
|
17
20
|
export interface LatestUpdate {
|
|
18
21
|
timestamp: number;
|
|
19
22
|
redstone: RedstoneUpdateTask[];
|
|
@@ -27,7 +30,7 @@ export declare class PriceFeedRegister extends SDKConstruct implements IHooks<Pr
|
|
|
27
30
|
#private;
|
|
28
31
|
readonly logger?: ILogger;
|
|
29
32
|
readonly redstoneUpdater: RedstoneUpdater;
|
|
30
|
-
constructor(sdk: GearboxSDK);
|
|
33
|
+
constructor(sdk: GearboxSDK, opts?: PriceFeedRegisterOptions);
|
|
31
34
|
addHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
|
|
32
35
|
removeHook: <K extends "updatesGenerated">(hookName: K, fn: (...args: PriceFeedRegisterHooks[K]) => void | Promise<void>) => void;
|
|
33
36
|
/**
|
|
@@ -44,7 +47,7 @@ export declare class PriceFeedRegister extends SDKConstruct implements IHooks<Pr
|
|
|
44
47
|
* Loads PARTIAL information about all updatable price feeds from MarketCompressor
|
|
45
48
|
* This is not saved anywhere in PriceFeedRegister, and can later be used to load price feed updates
|
|
46
49
|
*/
|
|
47
|
-
getPartialUpdatablePriceFeeds(
|
|
50
|
+
getPartialUpdatablePriceFeeds(configurators: Address[], pools?: Address[]): Promise<IPriceFeedContract[]>;
|
|
48
51
|
create(data: PartialPriceFeedTreeNode): IPriceFeedContract;
|
|
49
52
|
/**
|
|
50
53
|
* Information update latest update of updatable price feeds, for diagnostic purposes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface TimestampedCalldata {
|
|
2
|
+
dataFeedId: string;
|
|
3
|
+
data: `0x${string}`;
|
|
4
|
+
/**
|
|
5
|
+
* This timestamp is in seconds
|
|
6
|
+
*/
|
|
7
|
+
timestamp: number;
|
|
8
|
+
cached: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface RedstoneCacheOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Assume that in historical mode we only need to fetch once and then reuse from cache forever
|
|
13
|
+
*/
|
|
14
|
+
historical: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* TTL in milliseconds
|
|
17
|
+
*/
|
|
18
|
+
ttl: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class RedstoneCache {
|
|
21
|
+
#private;
|
|
22
|
+
constructor(opts: RedstoneCacheOptions);
|
|
23
|
+
get(dataServiceId: string, dataFeedId: string, uniqueSignersCount: number): Omit<TimestampedCalldata, "cached"> | undefined;
|
|
24
|
+
set(dataServiceId: string, dataFeedId: string, uniqueSignersCount: number, value: Omit<TimestampedCalldata, "cached">): void;
|
|
25
|
+
}
|
|
@@ -17,19 +17,37 @@ export declare class RedstoneUpdateTx implements IPriceUpdateTx<RedstoneUpdateTa
|
|
|
17
17
|
get pretty(): string;
|
|
18
18
|
validateTimestamp(blockTimestamp: bigint): "valid" | "too old" | "in future";
|
|
19
19
|
}
|
|
20
|
+
export interface RedstoneOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Fixed redstone historic timestamp in ms
|
|
23
|
+
* Set to true to enable redstone historical mode using timestamp from attach block
|
|
24
|
+
*/
|
|
25
|
+
historicTimestamp?: number | true;
|
|
26
|
+
/**
|
|
27
|
+
* Override redstone gateways. Can be used to set caching proxies, to avoid rate limiting
|
|
28
|
+
*/
|
|
29
|
+
gateways?: string[];
|
|
30
|
+
/**
|
|
31
|
+
* TTL for redstone cache in milliseconds
|
|
32
|
+
* If 0, disables caching
|
|
33
|
+
* If not set, uses some default value
|
|
34
|
+
* Cache is always enabled in historical mode
|
|
35
|
+
*/
|
|
36
|
+
cacheTTL?: number;
|
|
37
|
+
/**
|
|
38
|
+
* When true, no error will be thrown when redstone is unable to fetch data for some feeds
|
|
39
|
+
*/
|
|
40
|
+
ignoreMissingFeeds?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Enable redstone internal logging
|
|
43
|
+
*/
|
|
44
|
+
enableLogging?: boolean;
|
|
45
|
+
}
|
|
20
46
|
/**
|
|
21
47
|
* Class to update multiple redstone price feeds at once
|
|
22
48
|
*/
|
|
23
49
|
export declare class RedstoneUpdater extends SDKConstruct {
|
|
24
50
|
#private;
|
|
25
|
-
constructor(sdk: GearboxSDK);
|
|
26
|
-
/**
|
|
27
|
-
* Set redstone historical timestamp in milliseconds
|
|
28
|
-
*/
|
|
29
|
-
set historicalTimestamp(timestampMs: number);
|
|
30
|
-
/**
|
|
31
|
-
* Set redstone gateways
|
|
32
|
-
*/
|
|
33
|
-
set gateways(gateways: string[]);
|
|
51
|
+
constructor(sdk: GearboxSDK, opts?: RedstoneOptions);
|
|
34
52
|
getUpdateTxs(feeds: RedstonePriceFeedContract[], logContext?: Record<string, any>): Promise<RedstoneUpdateTx[]>;
|
|
35
53
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type PickSomeRequired<T, RequiredKeys extends keyof T, OptionalKeys extends keyof T> = Required<Pick<T, RequiredKeys>> & Partial<Pick<T, OptionalKeys>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gearbox-protocol/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-next.2",
|
|
4
4
|
"description": "Gearbox SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/cjs/sdk/index.js",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"abitype": "^1.0.8",
|
|
71
71
|
"date-fns": "^4.1.0",
|
|
72
72
|
"decimal.js-light": "^2.5.1",
|
|
73
|
+
"keyv": "^5.3.3",
|
|
73
74
|
"viem": ">=2.23.15 <3.0.0",
|
|
74
75
|
"zod": "^3.24.4"
|
|
75
76
|
},
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
"eslint": "^8.57.1",
|
|
85
86
|
"eslint-plugin-require-extensions": "^0.1.3",
|
|
86
87
|
"husky": "^9.1.7",
|
|
87
|
-
"lint-staged": "^
|
|
88
|
+
"lint-staged": "^16.0.0",
|
|
88
89
|
"pino": "^9.6.0",
|
|
89
90
|
"pino-pretty": "^13.0.0",
|
|
90
91
|
"prettier": "^3.5.3",
|