@gearbox-protocol/cli-utils 6.11.0 → 6.13.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.
@@ -0,0 +1,30 @@
1
+ import type { ILogger, NetworkType } from "@gearbox-protocol/sdk";
2
+ import { WhitelistItemSchema } from "./whitelist-schema.js";
3
+ export interface WhitelistOptions {
4
+ url: string;
5
+ network: NetworkType;
6
+ logger?: ILogger;
7
+ }
8
+ /**
9
+ * Fetches a network-scoped whitelist from the deploy-tools backend and keeps
10
+ * it in memory with a 10-minute refresh interval.
11
+ *
12
+ * On fetch failure the previous in-memory copy is retained; if no successful
13
+ * fetch has occurred yet the whitelist is considered empty.
14
+ */
15
+ export declare class Whitelist {
16
+ #private;
17
+ constructor({ url, network, logger }: WhitelistOptions);
18
+ /**
19
+ * Performs the initial fetch and starts the periodic refresh timer.
20
+ * Failure of the initial fetch is non-fatal: the whitelist stays empty.
21
+ */
22
+ start(): Promise<void>;
23
+ /** Stops the refresh timer. */
24
+ stop(): void;
25
+ /**
26
+ * Returns the matching whitelist entry for the given address, or
27
+ * `undefined` if absent or expired.
28
+ */
29
+ has(address: string): WhitelistItemSchema | undefined;
30
+ }
@@ -0,0 +1,84 @@
1
+ import { setInterval } from "node:timers";
2
+ import { AddressMap } from "@gearbox-protocol/sdk";
3
+ import { z } from "zod/v4";
4
+ import { fetchRetry } from "./fetchRetry.js";
5
+ import { WhitelistItemSchema } from "./whitelist-schema.js";
6
+ const REFRESH_INTERVAL_MS = 10 * 60 * 1000;
7
+ const WhitelistResponse = z.array(WhitelistItemSchema);
8
+ /**
9
+ * Fetches a network-scoped whitelist from the deploy-tools backend and keeps
10
+ * it in memory with a 10-minute refresh interval.
11
+ *
12
+ * On fetch failure the previous in-memory copy is retained; if no successful
13
+ * fetch has occurred yet the whitelist is considered empty.
14
+ */
15
+ export class Whitelist {
16
+ #url;
17
+ #network;
18
+ #logger;
19
+ #items = new AddressMap();
20
+ #refreshInterval;
21
+ constructor({ url, network, logger }) {
22
+ this.#url = url;
23
+ this.#network = network;
24
+ this.#logger = logger?.child?.({ name: "whitelist" }) ?? logger;
25
+ }
26
+ /**
27
+ * Performs the initial fetch and starts the periodic refresh timer.
28
+ * Failure of the initial fetch is non-fatal: the whitelist stays empty.
29
+ */
30
+ async start() {
31
+ this.#logger?.info(`loading ${this.#network} whitelist from ${this.#url}, refresh every ${REFRESH_INTERVAL_MS / 1000}s`);
32
+ await this.#refresh();
33
+ this.#refreshInterval = setInterval(() => {
34
+ this.#refresh().catch(e => {
35
+ this.#logger?.warn(`unexpected whitelist refresh error: ${e}`);
36
+ });
37
+ }, REFRESH_INTERVAL_MS);
38
+ }
39
+ /** Stops the refresh timer. */
40
+ stop() {
41
+ if (this.#refreshInterval) {
42
+ clearInterval(this.#refreshInterval);
43
+ this.#refreshInterval = undefined;
44
+ }
45
+ }
46
+ /**
47
+ * Fetches the whitelist from the backend, parses it and atomically
48
+ * replaces the in-memory map. On any error logs a warning and retains
49
+ * the previous map.
50
+ */
51
+ async #refresh() {
52
+ try {
53
+ const resp = await fetchRetry(this.#url);
54
+ if (!resp.ok) {
55
+ throw new Error(`whitelist fetch failed: ${resp.status} ${resp.statusText}`);
56
+ }
57
+ const data = await resp.json();
58
+ const items = WhitelistResponse.parse(data).filter(i => i.network === this.#network);
59
+ const next = new AddressMap();
60
+ for (const item of items) {
61
+ next.upsert(item.address, item);
62
+ }
63
+ this.#items = next;
64
+ this.#logger?.debug(`loaded ${items.length} whitelist entries for ${this.#network}`);
65
+ }
66
+ catch (e) {
67
+ this.#logger?.warn(`failed to refresh whitelist from ${this.#url}, keeping previous version: ${e}`);
68
+ }
69
+ }
70
+ /**
71
+ * Returns the matching whitelist entry for the given address, or
72
+ * `undefined` if absent or expired.
73
+ */
74
+ has(address) {
75
+ const item = this.#items.get(address);
76
+ if (!item) {
77
+ return undefined;
78
+ }
79
+ if (item.expiresAt !== undefined && Date.now() / 1000 >= item.expiresAt) {
80
+ return undefined;
81
+ }
82
+ return item;
83
+ }
84
+ }
@@ -0,0 +1,7 @@
1
+ interface RetrySettings {
2
+ retries?: number;
3
+ retryInterval?: number;
4
+ validateResponse?: (response: Response) => void;
5
+ }
6
+ export declare function fetchRetry(input: string | URL, init?: RequestInit & RetrySettings): Promise<Response>;
7
+ export {};
@@ -0,0 +1,26 @@
1
+ // Mirror of packages/shared/src/fetchRetry.ts; keep in sync.
2
+ import { sleep } from "./sleep.js";
3
+ const DEFAULT_RETRIES = 5;
4
+ const DEFAULT_RETRY_INTERVAL = 1000;
5
+ export async function fetchRetry(input, init = {}) {
6
+ const { retries = DEFAULT_RETRIES, retryInterval = DEFAULT_RETRY_INTERVAL, validateResponse, ...options } = init;
7
+ try {
8
+ const response = await fetch(input, options);
9
+ validateResponse?.(response);
10
+ return response;
11
+ }
12
+ catch (e) {
13
+ if (retries > 0) {
14
+ await sleep(retryInterval);
15
+ return fetchRetry(input, {
16
+ ...options,
17
+ retries: retries - 1,
18
+ retryInterval,
19
+ validateResponse,
20
+ });
21
+ }
22
+ else {
23
+ throw e;
24
+ }
25
+ }
26
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./CensoredString.js";
2
2
  export * from "./CensoredURL.js";
3
3
  export * from "./compressedConfig.js";
4
+ export * from "./fetchRetry.js";
4
5
  export * from "./getFirstHttpTransportOptions.js";
5
6
  export * from "./getProviders.js";
6
7
  export * from "./notifications/index.js";
@@ -10,5 +11,8 @@ export * from "./resolveYamlFiles.js";
10
11
  export * from "./SecretsManager.js";
11
12
  export * from "./SSMManager.js";
12
13
  export * from "./schema-primitives.js";
14
+ export * from "./sleep.js";
13
15
  export * from "./uncensorMap.js";
16
+ export * from "./Whitelist.js";
17
+ export * from "./whitelist-schema.js";
14
18
  export * from "./Zommand.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./CensoredString.js";
2
2
  export * from "./CensoredURL.js";
3
3
  export * from "./compressedConfig.js";
4
+ export * from "./fetchRetry.js";
4
5
  export * from "./getFirstHttpTransportOptions.js";
5
6
  export * from "./getProviders.js";
6
7
  export * from "./notifications/index.js";
@@ -10,5 +11,8 @@ export * from "./resolveYamlFiles.js";
10
11
  export * from "./SecretsManager.js";
11
12
  export * from "./SSMManager.js";
12
13
  export * from "./schema-primitives.js";
14
+ export * from "./sleep.js";
13
15
  export * from "./uncensorMap.js";
16
+ export * from "./Whitelist.js";
17
+ export * from "./whitelist-schema.js";
14
18
  export * from "./Zommand.js";
@@ -0,0 +1 @@
1
+ export declare function sleep(ms: number): Promise<unknown>;
package/dist/sleep.js ADDED
@@ -0,0 +1,6 @@
1
+ // Mirror of packages/shared/src/sleep.ts; keep in sync.
2
+ export async function sleep(ms) {
3
+ return new Promise(resolve => {
4
+ setTimeout(resolve, ms);
5
+ });
6
+ }
@@ -0,0 +1,54 @@
1
+ import { z } from "zod/v4";
2
+ export declare const WhitelistItemsSchema: z.ZodObject<{
3
+ network: z.ZodEnum<{
4
+ Mainnet: "Mainnet";
5
+ Arbitrum: "Arbitrum";
6
+ Optimism: "Optimism";
7
+ Base: "Base";
8
+ Sonic: "Sonic";
9
+ MegaETH: "MegaETH";
10
+ Monad: "Monad";
11
+ Berachain: "Berachain";
12
+ Avalanche: "Avalanche";
13
+ BNB: "BNB";
14
+ WorldChain: "WorldChain";
15
+ Etherlink: "Etherlink";
16
+ Hemi: "Hemi";
17
+ Lisk: "Lisk";
18
+ Plasma: "Plasma";
19
+ Somnia: "Somnia";
20
+ }>;
21
+ addresses: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
22
+ reason: z.ZodDefault<z.ZodString>;
23
+ issue: z.ZodOptional<z.ZodString>;
24
+ expiresAt: z.ZodOptional<z.ZodNumber>;
25
+ }, z.core.$strip>;
26
+ export type WhitelistItemsSchema = z.infer<typeof WhitelistItemsSchema>;
27
+ export declare const WhitelistItemSchema: z.ZodObject<{
28
+ network: z.ZodEnum<{
29
+ Mainnet: "Mainnet";
30
+ Arbitrum: "Arbitrum";
31
+ Optimism: "Optimism";
32
+ Base: "Base";
33
+ Sonic: "Sonic";
34
+ MegaETH: "MegaETH";
35
+ Monad: "Monad";
36
+ Berachain: "Berachain";
37
+ Avalanche: "Avalanche";
38
+ BNB: "BNB";
39
+ WorldChain: "WorldChain";
40
+ Etherlink: "Etherlink";
41
+ Hemi: "Hemi";
42
+ Lisk: "Lisk";
43
+ Plasma: "Plasma";
44
+ Somnia: "Somnia";
45
+ }>;
46
+ category: z.ZodString;
47
+ address: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
48
+ reason: z.ZodDefault<z.ZodString>;
49
+ issue: z.ZodOptional<z.ZodString>;
50
+ contractType: z.ZodOptional<z.ZodString>;
51
+ createdAt: z.ZodNumber;
52
+ expiresAt: z.ZodOptional<z.ZodNumber>;
53
+ }, z.core.$strip>;
54
+ export type WhitelistItemSchema = z.infer<typeof WhitelistItemSchema>;
@@ -0,0 +1,21 @@
1
+ // Mirror of packages/shared/src/whitelist-schema.ts; keep in sync.
2
+ import { NetworkType } from "@gearbox-protocol/sdk";
3
+ import { z } from "zod/v4";
4
+ import { addressLike } from "./schema-primitives.js";
5
+ export const WhitelistItemsSchema = z.object({
6
+ network: NetworkType,
7
+ addresses: z.array(addressLike()).min(1),
8
+ reason: z.string().default(""),
9
+ issue: z.string().optional(),
10
+ expiresAt: z.number().optional(),
11
+ });
12
+ export const WhitelistItemSchema = z.object({
13
+ network: NetworkType,
14
+ category: z.string(),
15
+ address: addressLike(),
16
+ reason: z.string().default(""),
17
+ issue: z.string().optional(),
18
+ contractType: z.string().optional(),
19
+ createdAt: z.number(),
20
+ expiresAt: z.number().optional(),
21
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/cli-utils",
3
3
  "description": "Utils for creating cli apps",
4
- "version": "6.11.0",
4
+ "version": "6.13.1",
5
5
  "homepage": "https://gearbox.fi",
6
6
  "keywords": [
7
7
  "gearbox"