@gearbox-protocol/sdk 14.8.7 → 14.10.0-next.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.
Files changed (45) hide show
  1. package/dist/cjs/abi/IWithdrawalCompressorV311.js +315 -0
  2. package/dist/cjs/{plugins/apy/apy-cache.js → common-utils/axios-cache/AxiosCache.js} +31 -20
  3. package/dist/cjs/common-utils/axios-cache/index.js +22 -0
  4. package/dist/cjs/common-utils/index.js +2 -0
  5. package/dist/cjs/plugins/apy/ApyPlugin.js +4 -4
  6. package/dist/cjs/plugins/apy/index.js +0 -2
  7. package/dist/cjs/plugins/delayed-withdrawal/DelayedWithdrawalPlugin.js +3 -2
  8. package/dist/cjs/plugins/remote-configs/CustomConfigSource.js +41 -0
  9. package/dist/cjs/plugins/remote-configs/RemoteConfigSource.js +56 -0
  10. package/dist/cjs/plugins/remote-configs/RemoteConfigsPlugin.js +186 -0
  11. package/dist/cjs/plugins/remote-configs/index.js +28 -0
  12. package/dist/cjs/plugins/remote-configs/package.json +1 -0
  13. package/dist/cjs/plugins/remote-configs/types.js +16 -0
  14. package/dist/cjs/sdk/OnchainSDK.js +2 -2
  15. package/dist/cjs/sdk/accounts/CreditAccountsServiceV310.js +13 -6
  16. package/dist/cjs/sdk/utils/viem/simulateWithPriceUpdates.js +1 -1
  17. package/dist/esm/abi/IWithdrawalCompressorV311.js +291 -0
  18. package/dist/esm/{plugins/apy/apy-cache.js → common-utils/axios-cache/AxiosCache.js} +27 -16
  19. package/dist/esm/common-utils/axios-cache/index.js +1 -0
  20. package/dist/esm/common-utils/index.js +1 -0
  21. package/dist/esm/plugins/apy/ApyPlugin.js +4 -4
  22. package/dist/esm/plugins/apy/index.js +0 -1
  23. package/dist/esm/plugins/delayed-withdrawal/DelayedWithdrawalPlugin.js +3 -2
  24. package/dist/esm/plugins/remote-configs/CustomConfigSource.js +17 -0
  25. package/dist/esm/plugins/remote-configs/RemoteConfigSource.js +32 -0
  26. package/dist/esm/plugins/remote-configs/RemoteConfigsPlugin.js +165 -0
  27. package/dist/esm/plugins/remote-configs/index.js +4 -0
  28. package/dist/esm/plugins/remote-configs/package.json +1 -0
  29. package/dist/esm/plugins/remote-configs/types.js +0 -0
  30. package/dist/esm/sdk/OnchainSDK.js +2 -2
  31. package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +13 -6
  32. package/dist/esm/sdk/utils/viem/simulateWithPriceUpdates.js +1 -1
  33. package/dist/types/abi/IWithdrawalCompressorV311.d.ts +327 -0
  34. package/dist/types/{plugins/apy/apy-cache.d.ts → common-utils/axios-cache/AxiosCache.d.ts} +6 -7
  35. package/dist/types/common-utils/axios-cache/index.d.ts +1 -0
  36. package/dist/types/common-utils/index.d.ts +1 -0
  37. package/dist/types/plugins/apy/index.d.ts +0 -1
  38. package/dist/types/plugins/delayed-withdrawal/types.d.ts +10 -2
  39. package/dist/types/plugins/remote-configs/CustomConfigSource.d.ts +13 -0
  40. package/dist/types/plugins/remote-configs/RemoteConfigSource.d.ts +31 -0
  41. package/dist/types/plugins/remote-configs/RemoteConfigsPlugin.d.ts +24 -0
  42. package/dist/types/plugins/remote-configs/index.d.ts +4 -0
  43. package/dist/types/plugins/remote-configs/types.d.ts +24 -0
  44. package/dist/types/sdk/accounts/CreditAccountsServiceV310.d.ts +4 -1
  45. package/package.json +1 -1
@@ -1,42 +1,49 @@
1
1
  import axios from "axios";
2
- class ApyOutputCache {
2
+ class AxiosCache {
3
3
  static #instances = /* @__PURE__ */ new Map();
4
4
  #url;
5
5
  #ttlMs;
6
6
  #cache;
7
7
  #pending;
8
8
  #logger;
9
- constructor(url, ttlMs, logger) {
9
+ #getLogMeta;
10
+ constructor(url, ttlMs, logger, getLogMeta) {
10
11
  this.#url = url;
11
12
  this.#ttlMs = ttlMs;
12
13
  this.#logger = logger;
14
+ this.#getLogMeta = getLogMeta;
13
15
  }
14
16
  /**
15
17
  * Returns a shared cache instance for the given URL.
16
18
  * The same instance is reused across all callers with identical URL.
17
19
  */
18
- static get(url, ttlMs, logger) {
19
- let instance = ApyOutputCache.#instances.get(url);
20
+ static get(url, ttlMs, logger, getLogMeta) {
21
+ let instance = AxiosCache.#instances.get(url);
20
22
  if (!instance) {
21
- instance = new ApyOutputCache(url, ttlMs, logger);
22
- ApyOutputCache.#instances.set(url, instance);
23
+ instance = new AxiosCache(url, ttlMs, logger, getLogMeta);
24
+ AxiosCache.#instances.set(url, instance);
23
25
  }
24
26
  if (logger) {
25
27
  instance.#logger = logger;
26
28
  }
29
+ if (getLogMeta) {
30
+ instance.#getLogMeta = getLogMeta;
31
+ }
27
32
  return instance;
28
33
  }
29
34
  /**
30
- * Returns cached Output if fresh, otherwise fetches from the network.
35
+ * Returns cached data if fresh, otherwise fetches from the network.
31
36
  * Concurrent calls are de-duplicated.
32
37
  */
33
38
  async fetch() {
34
39
  if (this.#cache && Date.now() - this.#cache.fetchedAt < this.#ttlMs) {
35
- this.#logger?.debug("apy cache: TTL still valid, returning cached data");
40
+ this.#logger?.debug(
41
+ "axios cache: TTL still valid, returning cached data"
42
+ );
36
43
  return this.#cache.data;
37
44
  }
38
45
  if (this.#pending) {
39
- this.#logger?.debug("apy cache: request in flight, waiting");
46
+ this.#logger?.debug("axios cache: request in flight, waiting");
40
47
  return this.#pending;
41
48
  }
42
49
  this.#pending = this.#doFetch();
@@ -58,29 +65,33 @@ class ApyOutputCache {
58
65
  });
59
66
  if (response.status === 304 && this.#cache) {
60
67
  this.#cache.fetchedAt = Date.now();
61
- this.#logger?.debug("apy cache: 304 Not Modified, extended TTL");
68
+ this.#logger?.debug("axios cache: 304 Not Modified, extended TTL");
62
69
  return this.#cache.data;
63
70
  }
64
- const etag = response.headers["etag"];
71
+ const etag = response.headers.etag;
65
72
  this.#cache = {
66
73
  data: response.data,
67
74
  etag,
68
75
  fetchedAt: Date.now()
69
76
  };
77
+ const meta = this.#getLogMeta?.(response.data);
70
78
  this.#logger?.debug(
71
- `apy cache: fetched fresh data (timestamp: ${response.data.timestamp})`
79
+ `axios cache: fetched fresh data${meta ? ` (${meta})` : ""}`
72
80
  );
73
81
  return response.data;
74
82
  } catch (e) {
75
- this.#logger?.error(e, "apy cache: fetch failed");
76
- return this.#cache?.data;
83
+ this.#logger?.error(e, "axios cache: fetch failed");
84
+ if (this.#cache) {
85
+ return this.#cache.data;
86
+ }
87
+ throw e;
77
88
  }
78
89
  }
79
90
  /** Evicts all cached entries. Mainly useful for tests. */
80
91
  static clearAll() {
81
- ApyOutputCache.#instances.clear();
92
+ AxiosCache.#instances.clear();
82
93
  }
83
94
  }
84
95
  export {
85
- ApyOutputCache
96
+ AxiosCache
86
97
  };
@@ -0,0 +1 @@
1
+ export * from "./AxiosCache.js";
@@ -1,3 +1,4 @@
1
+ export * from "./axios-cache/index.js";
1
2
  export * from "./charts/index.js";
2
3
  export * from "./static/index.js";
3
4
  export * from "./utils/index.js";
@@ -1,4 +1,5 @@
1
1
  import { marketCompressorAbi } from "../../abi/compressors/marketCompressor.js";
2
+ import { AxiosCache } from "../../common-utils/axios-cache/index.js";
2
3
  import {
3
4
  getAvailableAndDisabledStrategies,
4
5
  getReleasedStrategiesListCore,
@@ -16,7 +17,6 @@ import {
16
17
  } from "../../sdk/index.js";
17
18
  import { rayToNumber } from "../../sdk/utils/formatter.js";
18
19
  import { hexEq } from "../../sdk/utils/hex.js";
19
- import { ApyOutputCache } from "./apy-cache.js";
20
20
  import { parseGearStats, parseNetworkApy } from "./apy-parser.js";
21
21
  import { APY_STATE_CACHE_URL, DEFAULT_APY_INTERVAL_MS } from "./constants.js";
22
22
  import {
@@ -272,13 +272,13 @@ class ApyPlugin extends BasePlugin {
272
272
  // ---------------------------------------------------------------------------
273
273
  async #fetchApy() {
274
274
  try {
275
- const cache = ApyOutputCache.get(
275
+ const cache = AxiosCache.get(
276
276
  this.#apyUrl,
277
277
  this.#cacheTtlMs,
278
- this.#logger
278
+ this.#logger,
279
+ (data) => `timestamp: ${data.timestamp}`
279
280
  );
280
281
  const output = await cache.fetch();
281
- if (!output) return void 0;
282
282
  const chainData = output.chains[this.sdk.chainId];
283
283
  if (!chainData) {
284
284
  this.#logger?.debug(
@@ -1,5 +1,4 @@
1
1
  export * from "./ApyPlugin.js";
2
- export * from "./apy-cache.js";
3
2
  export * from "./apy-parser.js";
4
3
  export * from "./constants.js";
5
4
  export * from "./pool-apy-types.js";
@@ -1,4 +1,5 @@
1
1
  import { iWithdrawalCompressorV310Abi } from "../../abi/IWithdrawalCompressorV310.js";
2
+ import { iWithdrawalCompressorV311Abi } from "../../abi/IWithdrawalCompressorV311.js";
2
3
  import {
3
4
  AddressMap,
4
5
  BasePlugin,
@@ -19,8 +20,8 @@ class DelayedWithdrawalPlugin extends BasePlugin {
19
20
  const resp = await this.client.multicall({
20
21
  contracts: compressor ? creditManagers.map(
21
22
  (cm) => ({
22
- abi: iWithdrawalCompressorV310Abi,
23
- address: compressor,
23
+ address: compressor.address,
24
+ abi: compressor.version === 310 ? iWithdrawalCompressorV310Abi : iWithdrawalCompressorV311Abi,
24
25
  functionName: "getWithdrawableAssets",
25
26
  args: [cm.creditManager.address]
26
27
  })
@@ -0,0 +1,17 @@
1
+ class CustomConfigSource {
2
+ #getPools;
3
+ #getStrategies;
4
+ constructor(options) {
5
+ this.#getPools = options.getPools;
6
+ this.#getStrategies = options.getStrategies;
7
+ }
8
+ async getPools() {
9
+ return this.#getPools();
10
+ }
11
+ async getStrategies() {
12
+ return this.#getStrategies();
13
+ }
14
+ }
15
+ export {
16
+ CustomConfigSource
17
+ };
@@ -0,0 +1,32 @@
1
+ import { AxiosCache } from "../../common-utils/axios-cache/index.js";
2
+ const DEFAULT_POOLS_URL = "https://static.gearbox.finance/client-v3/configs/pools/pools.json";
3
+ const DEFAULT_STRATEGIES_URL = "https://static.gearbox.finance/client-v3/configs/strategies/strategies.json";
4
+ const DEFAULT_CACHE_TTL_MS = 30 * 60 * 1e3;
5
+ class RemoteConfigSource {
6
+ #poolsCache;
7
+ #strategiesCache;
8
+ constructor(options) {
9
+ const poolsUrl = options?.poolsUrl ?? DEFAULT_POOLS_URL;
10
+ const strategiesUrl = options?.strategiesUrl ?? DEFAULT_STRATEGIES_URL;
11
+ const ttlMs = options?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
12
+ this.#poolsCache = AxiosCache.get(
13
+ poolsUrl,
14
+ ttlMs,
15
+ options?.logger
16
+ );
17
+ this.#strategiesCache = AxiosCache.get(
18
+ strategiesUrl,
19
+ ttlMs,
20
+ options?.logger
21
+ );
22
+ }
23
+ async getPools() {
24
+ return this.#poolsCache.fetch();
25
+ }
26
+ async getStrategies() {
27
+ return this.#strategiesCache.fetch();
28
+ }
29
+ }
30
+ export {
31
+ RemoteConfigSource
32
+ };
@@ -0,0 +1,165 @@
1
+ import { isAddress } from "viem";
2
+ import {
3
+ BasePlugin,
4
+ TypedObjectUtils
5
+ } from "../../sdk/index.js";
6
+ import { RemoteConfigSource } from "./RemoteConfigSource.js";
7
+ class RemoteConfigsPlugin extends BasePlugin {
8
+ #sources;
9
+ #pools;
10
+ #strategies;
11
+ constructor(loadOnAttach = false, options) {
12
+ super(loadOnAttach);
13
+ this.#sources = options?.sources ?? [new RemoteConfigSource()];
14
+ }
15
+ get loaded() {
16
+ return !!this.#pools && !!this.#strategies;
17
+ }
18
+ async load(force) {
19
+ if (!force && this.loaded) {
20
+ return this.state;
21
+ }
22
+ const [pools, strategies] = await Promise.all([
23
+ this.#loadPools(),
24
+ this.#loadStrategies()
25
+ ]);
26
+ this.#pools = pools;
27
+ this.#strategies = strategies;
28
+ return this.state;
29
+ }
30
+ async syncState() {
31
+ await this.load(false);
32
+ }
33
+ get state() {
34
+ return {
35
+ pools: this.pools,
36
+ strategies: this.strategies
37
+ };
38
+ }
39
+ hydrate(state) {
40
+ this.#pools = state.pools;
41
+ this.#strategies = state.strategies;
42
+ }
43
+ stateHuman(_) {
44
+ return this.state;
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // Accessors
48
+ // ---------------------------------------------------------------------------
49
+ /**
50
+ * Pool configs for the current SDK network.
51
+ * @throws if plugin is not loaded
52
+ */
53
+ get pools() {
54
+ if (!this.#pools) {
55
+ throw new Error("remote-configs plugin not loaded");
56
+ }
57
+ return this.#pools;
58
+ }
59
+ /**
60
+ * Strategy configs for the current SDK network.
61
+ * @throws if plugin is not loaded
62
+ */
63
+ get strategies() {
64
+ if (!this.#strategies) {
65
+ throw new Error("remote-configs plugin not loaded");
66
+ }
67
+ return this.#strategies;
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Internal
71
+ // ---------------------------------------------------------------------------
72
+ async #loadPools() {
73
+ for (const source of this.#sources) {
74
+ try {
75
+ const data = await source.getPools();
76
+ const mapped = this.#mapPoolPayload(
77
+ data.filter(
78
+ (p) => p.chainId === this.sdk.chainId && p.network.toLowerCase() === this.sdk.networkType.toLowerCase()
79
+ )
80
+ );
81
+ return mapped;
82
+ } catch (e) {
83
+ this.logger?.warn(e, `${source.constructor.name} failed to load pools`);
84
+ }
85
+ }
86
+ throw new Error("all config sources failed to load pools");
87
+ }
88
+ async #loadStrategies() {
89
+ for (const source of this.#sources) {
90
+ try {
91
+ const data = await source.getStrategies();
92
+ const mapped = this.#mapStrategyPayload(
93
+ data.filter(
94
+ (s) => s.chainId === this.sdk.chainId && s.network.toLowerCase() === this.sdk.networkType.toLowerCase()
95
+ )
96
+ );
97
+ return mapped;
98
+ } catch (e) {
99
+ this.logger?.warn(
100
+ e,
101
+ `${source.constructor.name} failed to load strategies`
102
+ );
103
+ }
104
+ }
105
+ throw new Error("all config sources failed to load strategies");
106
+ }
107
+ #mapPoolPayload(payload) {
108
+ return payload.reduce((acc, p) => {
109
+ const addressLc = p.address.toLowerCase();
110
+ if (isAddress(addressLc)) {
111
+ acc.push({
112
+ ...p,
113
+ address: addressLc
114
+ });
115
+ }
116
+ return acc;
117
+ }, []);
118
+ }
119
+ #mapStrategyPayload(payload) {
120
+ return payload.reduce((acc, p) => {
121
+ const tokenOutAddressLc = p.tokenOutAddress.toLowerCase();
122
+ const creditManagers = p.creditManagers.filter((cm) => isAddress(cm)).map((cm) => cm.toLowerCase());
123
+ const zeroSlippage = p.zeroSlippage ? TypedObjectUtils.entries(p.zeroSlippage).reduce((zsAcc, [address, value]) => {
124
+ const addressLc = address.toLowerCase();
125
+ if (isAddress(addressLc)) zsAcc[addressLc] = value;
126
+ return zsAcc;
127
+ }, {}) : void 0;
128
+ const additionalRewardQuotas = p.additionalRewardQuotas ? TypedObjectUtils.entries(p.additionalRewardQuotas).reduce((rqAcc, [address, value]) => {
129
+ const addressLc = address.toLowerCase();
130
+ if (isAddress(addressLc)) {
131
+ const rewards = value?.filter((r) => isAddress(r)).map((r) => r.toLowerCase());
132
+ rqAcc[addressLc] = rewards;
133
+ }
134
+ return rqAcc;
135
+ }, {}) : void 0;
136
+ const additionalCollaterals = p.additionalCollaterals?.reduce((colAcc, cfg) => {
137
+ const token = typeof cfg === "string" ? cfg : cfg.token;
138
+ const tokenLc = token.toLowerCase();
139
+ const valid = isAddress(tokenLc);
140
+ if (valid && !colAcc[tokenLc]) colAcc[tokenLc] = [];
141
+ const nextCfg = typeof cfg === "string" ? tokenLc : {
142
+ token: tokenLc,
143
+ cm: cfg.cm.toLowerCase()
144
+ };
145
+ if (valid) colAcc[tokenLc].push(nextCfg);
146
+ return colAcc;
147
+ }, {});
148
+ if (isAddress(tokenOutAddressLc)) {
149
+ const { additionalCollaterals: _, ...rest } = p;
150
+ acc.push({
151
+ ...rest,
152
+ tokenOutAddress: tokenOutAddressLc,
153
+ creditManagers,
154
+ ...zeroSlippage ? { zeroSlippage } : {},
155
+ ...additionalRewardQuotas ? { additionalRewardQuotas } : {},
156
+ ...additionalCollaterals ? { additionalCollaterals } : {}
157
+ });
158
+ }
159
+ return acc;
160
+ }, []);
161
+ }
162
+ }
163
+ export {
164
+ RemoteConfigsPlugin
165
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./CustomConfigSource.js";
2
+ export * from "./RemoteConfigSource.js";
3
+ export * from "./RemoteConfigsPlugin.js";
4
+ export * from "./types.js";
@@ -0,0 +1 @@
1
+ {"type": "module","sideEffects":false}
File without changes
@@ -149,12 +149,12 @@ class OnchainSDK extends ChainContractsRegister {
149
149
  );
150
150
  this.#currentBlock = block.number;
151
151
  this.#timestamp = block.timestamp;
152
- if (!!blockNumber && !redstone?.historicTimestamp && time - Number(block.timestamp) * 1e3 > 60 * 1e3) {
152
+ if (blockNumber && !redstone?.historicTimestamp && time - Number(block.timestamp) * 1e3 > 60 * 1e3) {
153
153
  this.logger?.warn(
154
154
  "attaching to fixed block number, but redstone historicTimestamp is not set. price updates might fail"
155
155
  );
156
156
  }
157
- if (!!blockNumber && !pyth?.historicTimestamp && time - Number(block.timestamp) * 1e3 > 60 * 1e3) {
157
+ if (blockNumber && !pyth?.historicTimestamp && time - Number(block.timestamp) * 1e3 > 60 * 1e3) {
158
158
  this.logger?.warn(
159
159
  "attaching to fixed block number, but pyth historicTimestamp is not set. price updates might fail"
160
160
  );
@@ -8,6 +8,7 @@ import { creditAccountCompressorAbi } from "../../abi/compressors/creditAccountC
8
8
  import { peripheryCompressorAbi } from "../../abi/compressors/peripheryCompressor.js";
9
9
  import { rewardsCompressorAbi } from "../../abi/compressors/rewardsCompressor.js";
10
10
  import { iWithdrawalCompressorV310Abi } from "../../abi/IWithdrawalCompressorV310.js";
11
+ import { iWithdrawalCompressorV311Abi } from "../../abi/IWithdrawalCompressorV311.js";
11
12
  import { iBaseRewardPoolAbi } from "../../abi/iBaseRewardPool.js";
12
13
  import { iRWAFactoryAbi } from "../../abi/rwa/iRWAFactory.js";
13
14
  import { SDKConstruct } from "../base/index.js";
@@ -35,8 +36,14 @@ import {
35
36
  mergePriceUpdates
36
37
  } from "./multicall-utils.js";
37
38
  const COMPRESSORS = {
38
- [chains.Mainnet.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023",
39
- [chains.Monad.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023"
39
+ [chains.Mainnet.id]: {
40
+ address: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023",
41
+ version: 310
42
+ },
43
+ [chains.Monad.id]: {
44
+ address: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023",
45
+ version: 310
46
+ }
40
47
  };
41
48
  function getWithdrawalCompressorAddress(chainId) {
42
49
  return COMPRESSORS[chainId];
@@ -773,8 +780,8 @@ class CreditAccountsServiceV310 extends SDKConstruct {
773
780
  `No compressor for current chain ${this.sdk.networkType}`
774
781
  );
775
782
  const contract = getContract({
776
- address: compressor,
777
- abi: iWithdrawalCompressorV310Abi,
783
+ address: compressor.address,
784
+ abi: compressor.version === 310 ? iWithdrawalCompressorV310Abi : iWithdrawalCompressorV311Abi,
778
785
  client: this.client
779
786
  });
780
787
  const resp = await contract.read.getWithdrawalRequestResult([
@@ -796,8 +803,8 @@ class CreditAccountsServiceV310 extends SDKConstruct {
796
803
  `No compressor for current chain ${this.sdk.networkType}`
797
804
  );
798
805
  const contract = getContract({
799
- address: compressor,
800
- abi: iWithdrawalCompressorV310Abi,
806
+ address: compressor.address,
807
+ abi: compressor.version === 310 ? iWithdrawalCompressorV310Abi : iWithdrawalCompressorV311Abi,
801
808
  client: this.client
802
809
  });
803
810
  const resp = await contract.read.getCurrentWithdrawals([creditAccount]);
@@ -114,7 +114,7 @@ async function simulateWithPriceUpdates(client, parameters) {
114
114
  const r = multicallResults[1];
115
115
  if (r.status === "success") {
116
116
  const fromMc = BigInt(r.result);
117
- if (!!r.result && fromMc !== parameters.blockNumber) {
117
+ if (r.result && fromMc !== parameters.blockNumber) {
118
118
  throw getSimulateWithPriceUpdatesError(
119
119
  new BaseError(
120
120
  `block number returned from multicall (${fromMc}) is different from the one provided (${parameters.blockNumber})`