@gearbox-protocol/sdk 13.6.0-apy-plugin.4 → 13.6.0-apy-plugin.5

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.
@@ -31,15 +31,40 @@ var import_apy_parser = require("./apy-parser.js");
31
31
  var import_constants = require("./constants.js");
32
32
  var import_pool_apy_utils = require("./pool-apy-utils.js");
33
33
  const MAP_LABEL = "pools7DAgo";
34
+ const PLUGIN_KEY = "ApyPlugin";
34
35
  class ApyPlugin extends import_sdk.BasePlugin {
36
+ #timerInterval;
35
37
  #apyUrl;
36
38
  #cacheTtlMs;
37
39
  #pools7DAgo;
38
40
  #apySnapshot;
39
- constructor(loadOnAttach = false, options) {
41
+ /**
42
+ * Default timer options
43
+ * @see PluginTimerOptions
44
+ */
45
+ #defaultTimerOptions;
46
+ /**
47
+ * When `true`, the timer is started eagerly during the `attach` phase
48
+ * rather than waiting for an explicit `load` call.
49
+ **/
50
+ startTimerOnAttach;
51
+ constructor(loadOnAttach = false, startTimerOnAttach = false, options) {
40
52
  super(loadOnAttach);
53
+ this.startTimerOnAttach = startTimerOnAttach;
41
54
  this.#apyUrl = options?.apyUrl ?? import_constants.APY_STATE_CACHE_URL;
42
55
  this.#cacheTtlMs = options?.cacheTtlMs ?? import_constants.DEFAULT_APY_INTERVAL_MS;
56
+ this.#defaultTimerOptions = options?.timer ?? {
57
+ refreshPools7DAgoOnTick: false,
58
+ intervalMs: import_constants.DEFAULT_APY_INTERVAL_MS,
59
+ onChange: () => {
60
+ }
61
+ };
62
+ }
63
+ async attach() {
64
+ await super.attach();
65
+ if (this.startTimerOnAttach) {
66
+ this.startTimer();
67
+ }
43
68
  }
44
69
  // ---------------------------------------------------------------------------
45
70
  // Load — single entry point for all data (on-chain + state-cache)
@@ -182,10 +207,48 @@ class ApyPlugin extends import_sdk.BasePlugin {
182
207
  return { data, data7DAgo, pointsBase: poolPointsBase, points };
183
208
  }
184
209
  // ---------------------------------------------------------------------------
210
+ // Periodic full refresh
211
+ // ---------------------------------------------------------------------------
212
+ /**
213
+ * Starts a periodic timer that performs a full plugin state refresh.
214
+ * Only one timer can be active; calling again is a no-op.
215
+ * @returns Cleanup function that stops the timer.
216
+ */
217
+ startTimer(opts) {
218
+ if (this.#timerInterval) {
219
+ this.#logger?.debug("plugin timer already running");
220
+ return () => this.stopTimer();
221
+ }
222
+ const intervalMs = opts?.intervalMs ?? this.#defaultTimerOptions.intervalMs;
223
+ this.#logger?.debug(`starting plugin timer (interval: ${intervalMs}ms)`);
224
+ this.#timerInterval = setInterval(async () => {
225
+ try {
226
+ const prevTimestamp = this.#apySnapshot?.timestamp;
227
+ await this.load(true, {
228
+ loadPools7DAgo: opts?.refreshPools7DAgoOnTick ?? this.#defaultTimerOptions.refreshPools7DAgoOnTick
229
+ });
230
+ if (this.#apySnapshot?.timestamp !== prevTimestamp) {
231
+ opts?.onChange?.() ?? this.#defaultTimerOptions.onChange?.();
232
+ await this.sdk.triggerPluginUpdate(PLUGIN_KEY);
233
+ }
234
+ } catch (e) {
235
+ this.#logger?.error(e, "periodic refresh failed");
236
+ }
237
+ }, intervalMs);
238
+ return () => this.stopTimer();
239
+ }
240
+ stopTimer() {
241
+ if (this.#timerInterval) {
242
+ clearInterval(this.#timerInterval);
243
+ this.#timerInterval = void 0;
244
+ this.#logger?.debug("plugin timer stopped");
245
+ }
246
+ }
247
+ // ---------------------------------------------------------------------------
185
248
  // Plugin lifecycle
186
249
  // ---------------------------------------------------------------------------
187
250
  async syncState() {
188
- await this.load();
251
+ await this.load(true);
189
252
  }
190
253
  stateHuman(_) {
191
254
  return this.pools7DAgo.values().flatMap((p) => ({
@@ -110,6 +110,17 @@ class GearboxSDK extends import_base.ChainContractsRegister {
110
110
  * @see {@link SDKHooks} for available event names.
111
111
  **/
112
112
  removeHook = this.#hooks.removeHook.bind(this.#hooks);
113
+ /**
114
+ * Triggers the `pluginUpdate` hook.
115
+ *
116
+ * Intended to be called by plugins when they update their internal state
117
+ * outside of the normal `syncState`/`rehydrate` cycle (e.g. via an
118
+ * internal timer). Frontend listeners registered with
119
+ * `sdk.addHook("pluginUpdate", …)` will be notified.
120
+ **/
121
+ async triggerPluginUpdate(plugin) {
122
+ await this.#hooks.triggerHooks("pluginUpdate", { plugin });
123
+ }
113
124
  /**
114
125
  * Creates and initialises a new SDK instance by reading live on-chain state.
115
126
  *
@@ -21,15 +21,40 @@ import {
21
21
  getPoolExtraAPY
22
22
  } from "./pool-apy-utils.js";
23
23
  const MAP_LABEL = "pools7DAgo";
24
+ const PLUGIN_KEY = "ApyPlugin";
24
25
  class ApyPlugin extends BasePlugin {
26
+ #timerInterval;
25
27
  #apyUrl;
26
28
  #cacheTtlMs;
27
29
  #pools7DAgo;
28
30
  #apySnapshot;
29
- constructor(loadOnAttach = false, options) {
31
+ /**
32
+ * Default timer options
33
+ * @see PluginTimerOptions
34
+ */
35
+ #defaultTimerOptions;
36
+ /**
37
+ * When `true`, the timer is started eagerly during the `attach` phase
38
+ * rather than waiting for an explicit `load` call.
39
+ **/
40
+ startTimerOnAttach;
41
+ constructor(loadOnAttach = false, startTimerOnAttach = false, options) {
30
42
  super(loadOnAttach);
43
+ this.startTimerOnAttach = startTimerOnAttach;
31
44
  this.#apyUrl = options?.apyUrl ?? APY_STATE_CACHE_URL;
32
45
  this.#cacheTtlMs = options?.cacheTtlMs ?? DEFAULT_APY_INTERVAL_MS;
46
+ this.#defaultTimerOptions = options?.timer ?? {
47
+ refreshPools7DAgoOnTick: false,
48
+ intervalMs: DEFAULT_APY_INTERVAL_MS,
49
+ onChange: () => {
50
+ }
51
+ };
52
+ }
53
+ async attach() {
54
+ await super.attach();
55
+ if (this.startTimerOnAttach) {
56
+ this.startTimer();
57
+ }
33
58
  }
34
59
  // ---------------------------------------------------------------------------
35
60
  // Load — single entry point for all data (on-chain + state-cache)
@@ -172,10 +197,48 @@ class ApyPlugin extends BasePlugin {
172
197
  return { data, data7DAgo, pointsBase: poolPointsBase, points };
173
198
  }
174
199
  // ---------------------------------------------------------------------------
200
+ // Periodic full refresh
201
+ // ---------------------------------------------------------------------------
202
+ /**
203
+ * Starts a periodic timer that performs a full plugin state refresh.
204
+ * Only one timer can be active; calling again is a no-op.
205
+ * @returns Cleanup function that stops the timer.
206
+ */
207
+ startTimer(opts) {
208
+ if (this.#timerInterval) {
209
+ this.#logger?.debug("plugin timer already running");
210
+ return () => this.stopTimer();
211
+ }
212
+ const intervalMs = opts?.intervalMs ?? this.#defaultTimerOptions.intervalMs;
213
+ this.#logger?.debug(`starting plugin timer (interval: ${intervalMs}ms)`);
214
+ this.#timerInterval = setInterval(async () => {
215
+ try {
216
+ const prevTimestamp = this.#apySnapshot?.timestamp;
217
+ await this.load(true, {
218
+ loadPools7DAgo: opts?.refreshPools7DAgoOnTick ?? this.#defaultTimerOptions.refreshPools7DAgoOnTick
219
+ });
220
+ if (this.#apySnapshot?.timestamp !== prevTimestamp) {
221
+ opts?.onChange?.() ?? this.#defaultTimerOptions.onChange?.();
222
+ await this.sdk.triggerPluginUpdate(PLUGIN_KEY);
223
+ }
224
+ } catch (e) {
225
+ this.#logger?.error(e, "periodic refresh failed");
226
+ }
227
+ }, intervalMs);
228
+ return () => this.stopTimer();
229
+ }
230
+ stopTimer() {
231
+ if (this.#timerInterval) {
232
+ clearInterval(this.#timerInterval);
233
+ this.#timerInterval = void 0;
234
+ this.#logger?.debug("plugin timer stopped");
235
+ }
236
+ }
237
+ // ---------------------------------------------------------------------------
175
238
  // Plugin lifecycle
176
239
  // ---------------------------------------------------------------------------
177
240
  async syncState() {
178
- await this.load();
241
+ await this.load(true);
179
242
  }
180
243
  stateHuman(_) {
181
244
  return this.pools7DAgo.values().flatMap((p) => ({
@@ -101,6 +101,17 @@ class GearboxSDK extends ChainContractsRegister {
101
101
  * @see {@link SDKHooks} for available event names.
102
102
  **/
103
103
  removeHook = this.#hooks.removeHook.bind(this.#hooks);
104
+ /**
105
+ * Triggers the `pluginUpdate` hook.
106
+ *
107
+ * Intended to be called by plugins when they update their internal state
108
+ * outside of the normal `syncState`/`rehydrate` cycle (e.g. via an
109
+ * internal timer). Frontend listeners registered with
110
+ * `sdk.addHook("pluginUpdate", …)` will be notified.
111
+ **/
112
+ async triggerPluginUpdate(plugin) {
113
+ await this.#hooks.triggerHooks("pluginUpdate", { plugin });
114
+ }
104
115
  /**
105
116
  * Creates and initialises a new SDK instance by reading live on-chain state.
106
117
  *
@@ -9,8 +9,9 @@ export interface ApyPluginState {
9
9
  }
10
10
  export interface ApyPluginConstructorOptions {
11
11
  apyUrl?: string;
12
- /** TTL for the shared HTTP cache in milliseconds (default: 10 minutes, see `DEFAULT_APY_INTERVAL_MS`) */
12
+ /** TTL for the shared HTTP cache in milliseconds (default: same as timer interval) */
13
13
  cacheTtlMs?: number;
14
+ timer?: PluginTimerOptions;
14
15
  }
15
16
  export interface ApyPluginLoadOptions {
16
17
  /**
@@ -20,9 +21,25 @@ export interface ApyPluginLoadOptions {
20
21
  */
21
22
  loadPools7DAgo?: boolean;
22
23
  }
24
+ export interface PluginTimerOptions {
25
+ /** Polling interval in milliseconds (default: 10 minutes) */
26
+ intervalMs?: number;
27
+ /** Callback fired after each successful refresh */
28
+ onChange?: () => void;
29
+ /**
30
+ * When `true`, each tick also refreshes 7d-ago pool state on-chain.
31
+ */
32
+ refreshPools7DAgoOnTick?: boolean;
33
+ }
23
34
  export declare class ApyPlugin extends BasePlugin<ApyPluginState> implements IGearboxSDKPlugin<ApyPluginState> {
24
35
  #private;
25
- constructor(loadOnAttach?: boolean, options?: ApyPluginConstructorOptions);
36
+ /**
37
+ * When `true`, the timer is started eagerly during the `attach` phase
38
+ * rather than waiting for an explicit `load` call.
39
+ **/
40
+ readonly startTimerOnAttach: boolean;
41
+ constructor(loadOnAttach?: boolean, startTimerOnAttach?: boolean, options?: ApyPluginConstructorOptions);
42
+ attach(): Promise<void>;
26
43
  load(force?: boolean, loadOptions?: ApyPluginLoadOptions): Promise<ApyPluginState>;
27
44
  get loaded(): boolean;
28
45
  /**
@@ -39,6 +56,13 @@ export declare class ApyPlugin extends BasePlugin<ApyPluginState> implements IGe
39
56
  * @throws if plugin is not loaded
40
57
  */
41
58
  getPoolsAPY(): GetPoolsAPYResult;
59
+ /**
60
+ * Starts a periodic timer that performs a full plugin state refresh.
61
+ * Only one timer can be active; calling again is a no-op.
62
+ * @returns Cleanup function that stops the timer.
63
+ */
64
+ startTimer(opts?: PluginTimerOptions): () => void;
65
+ stopTimer(): void;
42
66
  syncState(): Promise<void>;
43
67
  stateHuman(_?: boolean): Pools7DAgoStateHuman[];
44
68
  get state(): ApyPluginState;
@@ -115,6 +115,13 @@ export interface SyncStateOptions {
115
115
  **/
116
116
  ignoreUpdateablePrices?: boolean;
117
117
  }
118
+ /**
119
+ * Payload carried by the `pluginUpdate` hook.
120
+ **/
121
+ export interface PluginUpdateInfo {
122
+ /** Identifier of the plugin that triggered the update (e.g. `"pools7DAgo"`). */
123
+ plugin: string;
124
+ }
118
125
  /**
119
126
  * Hook event map for the SDK lifecycle.
120
127
  *
@@ -123,10 +130,13 @@ export interface SyncStateOptions {
123
130
  *
124
131
  * - `syncState` — fired after {@link GearboxSDK.syncState} completes.
125
132
  * - `rehydrate` — fired after {@link GearboxSDK.rehydrate} completes.
133
+ * - `pluginUpdate` — fired by a plugin when its internal state changes
134
+ * outside of the normal `syncState`/`rehydrate` cycle (e.g. timer tick).
126
135
  **/
127
136
  export type SDKHooks = {
128
137
  syncState: [SyncStateOptions];
129
138
  rehydrate: [SyncStateOptions];
139
+ pluginUpdate: [PluginUpdateInfo];
130
140
  };
131
141
  /**
132
142
  * Main entry point for the Gearbox SDK.
@@ -175,6 +185,15 @@ export declare class GearboxSDK<const Plugins extends PluginsMap = {}> extends C
175
185
  * @see {@link SDKHooks} for available event names.
176
186
  **/
177
187
  removeHook: <K extends keyof SDKHooks>(hookName: K, fn: (...args: SDKHooks[K]) => void | Promise<void>) => void;
188
+ /**
189
+ * Triggers the `pluginUpdate` hook.
190
+ *
191
+ * Intended to be called by plugins when they update their internal state
192
+ * outside of the normal `syncState`/`rehydrate` cycle (e.g. via an
193
+ * internal timer). Frontend listeners registered with
194
+ * `sdk.addHook("pluginUpdate", …)` will be notified.
195
+ **/
196
+ triggerPluginUpdate(plugin: string): Promise<void>;
178
197
  /**
179
198
  * Creates and initialises a new SDK instance by reading live on-chain state.
180
199
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "13.6.0-apy-plugin.4",
3
+ "version": "13.6.0-apy-plugin.5",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "main": "./dist/cjs/sdk/index.js",