@gearbox-protocol/sdk 13.1.1 → 13.2.0

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.
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
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
+ };
6
10
  var __copyProps = (to, from, except, desc) => {
7
11
  if (from && typeof from === "object" || typeof from === "function") {
8
12
  for (let key of __getOwnPropNames(from))
@@ -14,6 +18,11 @@ var __copyProps = (to, from, except, desc) => {
14
18
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
15
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
20
  var dev_exports = {};
21
+ __export(dev_exports, {
22
+ GetLogsTransportOptions: () => import_logSplitterTransport.LogSplitterTransportOptions,
23
+ isRangeError: () => import_logSplitterTransport.isRangeError,
24
+ logSplitterTransport: () => import_logSplitterTransport.logSplitterTransport
25
+ });
17
26
  module.exports = __toCommonJS(dev_exports);
18
27
  __reExport(dev_exports, require("./AccountOpener.js"), module.exports);
19
28
  __reExport(dev_exports, require("./CachedStateSubscriber.js"), module.exports);
@@ -26,6 +35,7 @@ __reExport(dev_exports, require("./EthCallSpy.js"), module.exports);
26
35
  __reExport(dev_exports, require("./isOutOfSyncError.js"), module.exports);
27
36
  __reExport(dev_exports, require("./isRateLimitError.js"), module.exports);
28
37
  __reExport(dev_exports, require("./isTransientError.js"), module.exports);
38
+ var import_logSplitterTransport = require("./logSplitterTransport.js");
29
39
  __reExport(dev_exports, require("./ltUtils.js"), module.exports);
30
40
  __reExport(dev_exports, require("./migrateFaucet.js"), module.exports);
31
41
  __reExport(dev_exports, require("./mint/index.js"), module.exports);
@@ -37,6 +47,9 @@ __reExport(dev_exports, require("./transports.js"), module.exports);
37
47
  __reExport(dev_exports, require("./types.js"), module.exports);
38
48
  // Annotate the CommonJS export names for ESM import in node:
39
49
  0 && (module.exports = {
50
+ GetLogsTransportOptions,
51
+ isRangeError,
52
+ logSplitterTransport,
40
53
  ...require("./AccountOpener.js"),
41
54
  ...require("./CachedStateSubscriber.js"),
42
55
  ...require("./calcLiquidatableLTs.js"),
@@ -57,7 +57,11 @@ const TRANSIENT_PATTERNS = [
57
57
  // EIP-1474 ResourceUnavailableRpcError (code -32002): node is syncing or the
58
58
  // requested resource is temporarily unavailable.
59
59
  // Matches both the RPC message and viem's shortMessage "Requested resource not available."
60
- /resource unavailable|requested resource not available/i
60
+ /resource unavailable|requested resource not available/i,
61
+ // sometimes happens on DRPC: "GRPC Context cancellation"
62
+ /context cancel/i,
63
+ // DRPC error: Can't route your request to suitable provider, if you specified certain providers revise the list
64
+ /suitable provider/i
61
65
  ];
62
66
  function isTransientError(e) {
63
67
  if (e instanceof import_viem.BaseError) {
@@ -0,0 +1,154 @@
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 logSplitterTransport_exports = {};
20
+ __export(logSplitterTransport_exports, {
21
+ fetchLogsWithPagination: () => fetchLogsWithPagination,
22
+ isFixedBlockRange: () => isFixedBlockRange,
23
+ isRangeError: () => isRangeError,
24
+ logSplitterTransport: () => logSplitterTransport,
25
+ parsePageSizeHint: () => parsePageSizeHint
26
+ });
27
+ module.exports = __toCommonJS(logSplitterTransport_exports);
28
+ var import_viem = require("viem");
29
+ function isFixedBlockRange(filter) {
30
+ if (filter.blockHash != null) return false;
31
+ return isHexBlockNumber(filter.fromBlock) && isHexBlockNumber(filter.toBlock);
32
+ }
33
+ function isHexBlockNumber(value) {
34
+ return typeof value === "string" && value.startsWith("0x");
35
+ }
36
+ const RANGE_ERROR_PATTERNS = [
37
+ /block range/i,
38
+ /too many results/i,
39
+ /response size/i,
40
+ /result window/i,
41
+ /exceeds allowed/i,
42
+ /cannot request logs over more than/i,
43
+ /eth_getLogs is limited to/i,
44
+ /eth_getLogs requests with up to/i,
45
+ /range is too large/i,
46
+ /exceeded max allowed range/i
47
+ ];
48
+ function isRangeError(error) {
49
+ const msg = errorMessage(error);
50
+ return RANGE_ERROR_PATTERNS.some((re) => re.test(msg));
51
+ }
52
+ const GENERIC_BLOCKS_RE = /(\d+)\s*block/i;
53
+ const ALCHEMY_RANGE_RE = /this block range should work: \[(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+)\]/;
54
+ function parsePageSizeHint(error) {
55
+ const alchemy = tryAlchemyHint(error);
56
+ if (alchemy != null) return alchemy;
57
+ const msg = errorMessage(error);
58
+ const m = msg.match(GENERIC_BLOCKS_RE);
59
+ return m ? Number(m[1]) : null;
60
+ }
61
+ function tryAlchemyHint(error) {
62
+ if (!(error instanceof import_viem.HttpRequestError)) return void 0;
63
+ try {
64
+ const parsed = JSON.parse(error.details);
65
+ if (typeof parsed.message !== "string") return void 0;
66
+ const match = parsed.message.match(ALCHEMY_RANGE_RE);
67
+ if (!match) return void 0;
68
+ const from = (0, import_viem.hexToNumber)(match[1]);
69
+ const to = (0, import_viem.hexToNumber)(match[2]);
70
+ return to - from + 1;
71
+ } catch {
72
+ return void 0;
73
+ }
74
+ }
75
+ function errorMessage(error) {
76
+ if (error instanceof Error) {
77
+ const details = error.details;
78
+ return details ? `${error.message} ${details}` : error.message;
79
+ }
80
+ return String(error);
81
+ }
82
+ async function fetchLogsWithPagination(callRpc, from, to, options) {
83
+ if (from > to) {
84
+ throw new import_viem.InvalidParamsRpcError(
85
+ new Error(`Invalid parameters: from (${from}) > to (${to})`)
86
+ );
87
+ }
88
+ const { softLimit, hardLimit } = options ?? {};
89
+ let pageSize = hardLimit ?? to - from + 1;
90
+ const results = [];
91
+ let cursor = from;
92
+ while (cursor <= to) {
93
+ const chunkEnd = Math.min(to, cursor + pageSize - 1);
94
+ try {
95
+ const logs = await callRpc(cursor, chunkEnd);
96
+ results.push(...logs);
97
+ cursor = chunkEnd + 1;
98
+ } catch (error) {
99
+ if (!isRangeError(error)) {
100
+ throw error;
101
+ }
102
+ if (cursor === chunkEnd) {
103
+ throw error;
104
+ }
105
+ const hint = parsePageSizeHint(error);
106
+ if (hint) {
107
+ pageSize = hint;
108
+ } else {
109
+ const half = Math.max(1, Math.floor((chunkEnd - cursor + 1) / 2));
110
+ pageSize = softLimit ? Math.min(softLimit, half) : half;
111
+ }
112
+ }
113
+ }
114
+ return results;
115
+ }
116
+ function logSplitterTransport(transport, logOptions) {
117
+ return (opts) => {
118
+ const base = transport(opts);
119
+ const rpcRequest = base.request;
120
+ const request = (async (args) => {
121
+ const { method, params } = args;
122
+ if (method !== "eth_getLogs") {
123
+ return rpcRequest(args);
124
+ }
125
+ const filter = params?.[0] ?? {};
126
+ if (!isFixedBlockRange(filter)) {
127
+ return rpcRequest(args);
128
+ }
129
+ const from = (0, import_viem.hexToNumber)(filter.fromBlock);
130
+ const to = (0, import_viem.hexToNumber)(filter.toBlock);
131
+ const callRpc = async (f, t) => {
132
+ const paginatedFilter = {
133
+ ...filter,
134
+ fromBlock: (0, import_viem.numberToHex)(f),
135
+ toBlock: (0, import_viem.numberToHex)(t)
136
+ };
137
+ return rpcRequest({
138
+ method: "eth_getLogs",
139
+ params: [paginatedFilter]
140
+ });
141
+ };
142
+ return fetchLogsWithPagination(callRpc, from, to, logOptions);
143
+ });
144
+ return { ...base, request };
145
+ };
146
+ }
147
+ // Annotate the CommonJS export names for ESM import in node:
148
+ 0 && (module.exports = {
149
+ fetchLogsWithPagination,
150
+ isFixedBlockRange,
151
+ isRangeError,
152
+ logSplitterTransport,
153
+ parsePageSizeHint
154
+ });
@@ -9,6 +9,11 @@ export * from "./EthCallSpy.js";
9
9
  export * from "./isOutOfSyncError.js";
10
10
  export * from "./isRateLimitError.js";
11
11
  export * from "./isTransientError.js";
12
+ import {
13
+ isRangeError,
14
+ LogSplitterTransportOptions,
15
+ logSplitterTransport
16
+ } from "./logSplitterTransport.js";
12
17
  export * from "./ltUtils.js";
13
18
  export * from "./migrateFaucet.js";
14
19
  export * from "./mint/index.js";
@@ -18,3 +23,8 @@ export * from "./replaceStorage.js";
18
23
  export * from "./resilientTransport.js";
19
24
  export * from "./transports.js";
20
25
  export * from "./types.js";
26
+ export {
27
+ LogSplitterTransportOptions as GetLogsTransportOptions,
28
+ isRangeError,
29
+ logSplitterTransport
30
+ };
@@ -34,7 +34,11 @@ const TRANSIENT_PATTERNS = [
34
34
  // EIP-1474 ResourceUnavailableRpcError (code -32002): node is syncing or the
35
35
  // requested resource is temporarily unavailable.
36
36
  // Matches both the RPC message and viem's shortMessage "Requested resource not available."
37
- /resource unavailable|requested resource not available/i
37
+ /resource unavailable|requested resource not available/i,
38
+ // sometimes happens on DRPC: "GRPC Context cancellation"
39
+ /context cancel/i,
40
+ // DRPC error: Can't route your request to suitable provider, if you specified certain providers revise the list
41
+ /suitable provider/i
38
42
  ];
39
43
  function isTransientError(e) {
40
44
  if (e instanceof BaseError) {
@@ -0,0 +1,131 @@
1
+ import {
2
+ HttpRequestError,
3
+ hexToNumber,
4
+ InvalidParamsRpcError,
5
+ numberToHex
6
+ } from "viem";
7
+ function isFixedBlockRange(filter) {
8
+ if (filter.blockHash != null) return false;
9
+ return isHexBlockNumber(filter.fromBlock) && isHexBlockNumber(filter.toBlock);
10
+ }
11
+ function isHexBlockNumber(value) {
12
+ return typeof value === "string" && value.startsWith("0x");
13
+ }
14
+ const RANGE_ERROR_PATTERNS = [
15
+ /block range/i,
16
+ /too many results/i,
17
+ /response size/i,
18
+ /result window/i,
19
+ /exceeds allowed/i,
20
+ /cannot request logs over more than/i,
21
+ /eth_getLogs is limited to/i,
22
+ /eth_getLogs requests with up to/i,
23
+ /range is too large/i,
24
+ /exceeded max allowed range/i
25
+ ];
26
+ function isRangeError(error) {
27
+ const msg = errorMessage(error);
28
+ return RANGE_ERROR_PATTERNS.some((re) => re.test(msg));
29
+ }
30
+ const GENERIC_BLOCKS_RE = /(\d+)\s*block/i;
31
+ const ALCHEMY_RANGE_RE = /this block range should work: \[(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+)\]/;
32
+ function parsePageSizeHint(error) {
33
+ const alchemy = tryAlchemyHint(error);
34
+ if (alchemy != null) return alchemy;
35
+ const msg = errorMessage(error);
36
+ const m = msg.match(GENERIC_BLOCKS_RE);
37
+ return m ? Number(m[1]) : null;
38
+ }
39
+ function tryAlchemyHint(error) {
40
+ if (!(error instanceof HttpRequestError)) return void 0;
41
+ try {
42
+ const parsed = JSON.parse(error.details);
43
+ if (typeof parsed.message !== "string") return void 0;
44
+ const match = parsed.message.match(ALCHEMY_RANGE_RE);
45
+ if (!match) return void 0;
46
+ const from = hexToNumber(match[1]);
47
+ const to = hexToNumber(match[2]);
48
+ return to - from + 1;
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ }
53
+ function errorMessage(error) {
54
+ if (error instanceof Error) {
55
+ const details = error.details;
56
+ return details ? `${error.message} ${details}` : error.message;
57
+ }
58
+ return String(error);
59
+ }
60
+ async function fetchLogsWithPagination(callRpc, from, to, options) {
61
+ if (from > to) {
62
+ throw new InvalidParamsRpcError(
63
+ new Error(`Invalid parameters: from (${from}) > to (${to})`)
64
+ );
65
+ }
66
+ const { softLimit, hardLimit } = options ?? {};
67
+ let pageSize = hardLimit ?? to - from + 1;
68
+ const results = [];
69
+ let cursor = from;
70
+ while (cursor <= to) {
71
+ const chunkEnd = Math.min(to, cursor + pageSize - 1);
72
+ try {
73
+ const logs = await callRpc(cursor, chunkEnd);
74
+ results.push(...logs);
75
+ cursor = chunkEnd + 1;
76
+ } catch (error) {
77
+ if (!isRangeError(error)) {
78
+ throw error;
79
+ }
80
+ if (cursor === chunkEnd) {
81
+ throw error;
82
+ }
83
+ const hint = parsePageSizeHint(error);
84
+ if (hint) {
85
+ pageSize = hint;
86
+ } else {
87
+ const half = Math.max(1, Math.floor((chunkEnd - cursor + 1) / 2));
88
+ pageSize = softLimit ? Math.min(softLimit, half) : half;
89
+ }
90
+ }
91
+ }
92
+ return results;
93
+ }
94
+ function logSplitterTransport(transport, logOptions) {
95
+ return (opts) => {
96
+ const base = transport(opts);
97
+ const rpcRequest = base.request;
98
+ const request = (async (args) => {
99
+ const { method, params } = args;
100
+ if (method !== "eth_getLogs") {
101
+ return rpcRequest(args);
102
+ }
103
+ const filter = params?.[0] ?? {};
104
+ if (!isFixedBlockRange(filter)) {
105
+ return rpcRequest(args);
106
+ }
107
+ const from = hexToNumber(filter.fromBlock);
108
+ const to = hexToNumber(filter.toBlock);
109
+ const callRpc = async (f, t) => {
110
+ const paginatedFilter = {
111
+ ...filter,
112
+ fromBlock: numberToHex(f),
113
+ toBlock: numberToHex(t)
114
+ };
115
+ return rpcRequest({
116
+ method: "eth_getLogs",
117
+ params: [paginatedFilter]
118
+ });
119
+ };
120
+ return fetchLogsWithPagination(callRpc, from, to, logOptions);
121
+ });
122
+ return { ...base, request };
123
+ };
124
+ }
125
+ export {
126
+ fetchLogsWithPagination,
127
+ isFixedBlockRange,
128
+ isRangeError,
129
+ logSplitterTransport,
130
+ parsePageSizeHint
131
+ };
@@ -9,6 +9,7 @@ export * from "./EthCallSpy.js";
9
9
  export * from "./isOutOfSyncError.js";
10
10
  export * from "./isRateLimitError.js";
11
11
  export * from "./isTransientError.js";
12
+ export { isRangeError, LogSplitterTransportOptions as GetLogsTransportOptions, logSplitterTransport, } from "./logSplitterTransport.js";
12
13
  export * from "./ltUtils.js";
13
14
  export * from "./migrateFaucet.js";
14
15
  export * from "./mint/index.js";
@@ -0,0 +1,118 @@
1
+ import { type PublicRpcSchema, type Transport } from "viem";
2
+ /** Schema entry for `eth_getLogs` extracted from viem's {@link PublicRpcSchema}. */
3
+ type EthGetLogsSchema = Extract<PublicRpcSchema[number], {
4
+ Method: "eth_getLogs";
5
+ }>;
6
+ /**
7
+ * Filter parameter for `eth_getLogs` as defined by viem's {@link PublicRpcSchema}.
8
+ * A discriminated union of block-range filters and block-hash filters.
9
+ */
10
+ export type EthGetLogsFilter = EthGetLogsSchema["Parameters"][0];
11
+ /** Raw RPC return type for `eth_getLogs` (logs with hex-encoded quantities). */
12
+ export type EthGetLogsResult = EthGetLogsSchema["ReturnType"];
13
+ /**
14
+ * Options controlling pagination behaviour of {@link logSplitterTransport}.
15
+ */
16
+ export interface LogSplitterTransportOptions {
17
+ /**
18
+ * Acts as a min page size hint when there's no explit hint in rpc error message.
19
+ * Has no effect once the page size is already below `softLimit`.
20
+ */
21
+ softLimit?: number;
22
+ /**
23
+ * Initial page size. The block range is walked in `hardLimit`-sized pages
24
+ * from the start without attempting the full range first.
25
+ */
26
+ hardLimit?: number;
27
+ }
28
+ /**
29
+ * @internal
30
+ * Returns `true` when both `fromBlock` and `toBlock` are concrete hex-encoded
31
+ * block numbers and `blockHash` is absent. Only fixed numeric ranges can be
32
+ * split into sub-ranges for pagination.
33
+ *
34
+ * At the RPC transport level, block numbers are always `0x`-prefixed hex
35
+ * strings, while named tags (`"latest"`, `"safe"`, etc.) are plain words.
36
+ * The `0x` prefix check is sufficient to distinguish the two.
37
+ *
38
+ * @param filter - The `eth_getLogs` filter object.
39
+ * @returns Whether the filter describes a fixed, splittable block range.
40
+ */
41
+ export declare function isFixedBlockRange(filter: EthGetLogsFilter): boolean;
42
+ /**
43
+ * Returns `true` when the error message matches known RPC range/size limit
44
+ * patterns. Transient errors (timeouts, rate limits, 5xx) are explicitly
45
+ * excluded — they should be retried by the underlying transport.
46
+ *
47
+ * @param error - Any thrown value.
48
+ * @returns Whether the error indicates a block-range or result-size limit.
49
+ */
50
+ export declare function isRangeError(error: unknown): boolean;
51
+ /**
52
+ * @internal
53
+ * Attempts to extract a concrete page-size hint from an error thrown by an
54
+ * RPC provider. Two strategies are tried:
55
+ *
56
+ * 1. **Alchemy JSON details** — parses the `details` field of a
57
+ * {@link HttpRequestError} for a suggested `[fromHex, toHex]` range and
58
+ * computes the span as `toHex - fromHex + 1`.
59
+ * 2. **Generic N-blocks pattern** — matches `/<number> block(s)/i` in the
60
+ * error message.
61
+ *
62
+ * @param error - Any thrown value.
63
+ * @returns The suggested page size, or `null` if no hint could be extracted.
64
+ */
65
+ export declare function parsePageSizeHint(error: unknown): number | null;
66
+ /**
67
+ * @internal
68
+ * Callback that performs a single `eth_getLogs` RPC call for the given
69
+ * inclusive block range `[from, to]`.
70
+ */
71
+ export type CallRpcFn = (from: number, to: number) => Promise<EthGetLogsResult>;
72
+ /**
73
+ * @internal
74
+ * Fetches logs over `[from, to]` using a sequential linear walk with
75
+ * dynamically adjustable page size. On range errors the page is bisected
76
+ * (optionally capped by {@link LogSplitterTransportOptions.softLimit | softLimit}).
77
+ * Reduced page sizes persist for all subsequent pages in the same call.
78
+ *
79
+ * @param callRpc - Callback that performs a single `eth_getLogs` RPC call.
80
+ * @param from - Inclusive start block number.
81
+ * @param to - Inclusive end block number.
82
+ * @param options - Optional pagination limits.
83
+ * @returns All logs in the requested range, concatenated in order.
84
+ *
85
+ * @throws {@link InvalidParamsRpcError} when `from > to`.
86
+ * @throws The original error when a single-block request fails or the error
87
+ * is not a recognised range error.
88
+ */
89
+ export declare function fetchLogsWithPagination(callRpc: CallRpcFn, from: number, to: number, options?: LogSplitterTransportOptions): Promise<EthGetLogsResult>;
90
+ /**
91
+ * Wraps a viem {@link Transport} to transparently split `eth_getLogs` requests
92
+ * that exceed RPC provider block-range or result-size limits.
93
+ *
94
+ * Only requests with concrete numeric `fromBlock`/`toBlock` values (hex or
95
+ * bigint) are intercepted. Calls using block tags (`"latest"`, `"earliest"`,
96
+ * etc.) or `blockHash` are passed through unmodified.
97
+ *
98
+ * @param transport - The underlying viem transport to wrap.
99
+ * @param options - Optional pagination configuration.
100
+ * @returns A new transport that performs automatic log pagination.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * import { createPublicClient, http } from "viem";
105
+ * import { mainnet } from "viem/chains";
106
+ * import { logSplitterTransport } from "./logSplitterTransport.js";
107
+ *
108
+ * const client = createPublicClient({
109
+ * chain: mainnet,
110
+ * transport: logSplitterTransport(http("https://rpc.example.com"), {
111
+ * hardLimit: 10_000,
112
+ * softLimit: 2_000,
113
+ * }),
114
+ * });
115
+ * ```
116
+ */
117
+ export declare function logSplitterTransport(transport: Transport, logOptions?: LogSplitterTransportOptions): Transport;
118
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "13.1.1",
3
+ "version": "13.2.0",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "main": "./dist/cjs/sdk/index.js",
@@ -83,10 +83,10 @@
83
83
  "zod": "^4.3.6"
84
84
  },
85
85
  "devDependencies": {
86
- "@biomejs/biome": "^2.4.9",
86
+ "@biomejs/biome": "^2.4.10",
87
87
  "@commitlint/cli": "^20.5.0",
88
88
  "@commitlint/config-conventional": "^20.5.0",
89
- "@gearbox-protocol/biome-config": "^1.0.24",
89
+ "@gearbox-protocol/biome-config": "^1.0.25",
90
90
  "@types/cross-spawn": "^6.0.6",
91
91
  "axios": "^1.14.0",
92
92
  "cross-spawn": "^7.0.6",