@gearbox-protocol/sdk 16.0.0-next.54 → 16.0.0-next.56

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 (106) hide show
  1. package/dist/cjs/dev/mode-parity/compareOpportunities.js +3 -4
  2. package/dist/cjs/dev/mode-parity/comparePositions.js +5 -6
  3. package/dist/cjs/dev/mode-parity/fieldDiff.js +4 -6
  4. package/dist/cjs/dev/mode-parity/scriptUtils.js +145 -5
  5. package/dist/cjs/model/analytics.schema.js +44 -1
  6. package/dist/cjs/model/charts.js +15 -1
  7. package/dist/cjs/model/charts.schema.js +9 -3
  8. package/dist/cjs/model/index.js +9 -4
  9. package/dist/cjs/model/liquidations.schema.js +1 -1
  10. package/dist/cjs/model/opportunities.schema.js +2 -2
  11. package/dist/cjs/model/positions.schema.js +2 -2
  12. package/dist/cjs/offchain/analytics/OffchainAnalytics.js +25 -2
  13. package/dist/cjs/rewards/errors.js +27 -0
  14. package/dist/cjs/rewards/index.js +6 -7
  15. package/dist/cjs/rewards/merkl-api.js +45 -0
  16. package/dist/cjs/rewards/multichain.js +40 -0
  17. package/dist/cjs/rewards/{rewards/api.js → toMerklRewards.js} +26 -28
  18. package/dist/cjs/sdk/analytics/AnalyticsNamespace.js +6 -0
  19. package/dist/esm/dev/AccountOpener.js +1 -1
  20. package/dist/esm/dev/mode-parity/compareOpportunities.js +3 -4
  21. package/dist/esm/dev/mode-parity/comparePositions.js +5 -6
  22. package/dist/esm/dev/mode-parity/fieldDiff.js +4 -6
  23. package/dist/esm/dev/mode-parity/scriptUtils.js +142 -6
  24. package/dist/esm/dev/withdrawalUtils.js +1 -1
  25. package/dist/esm/model/analytics.schema.js +42 -2
  26. package/dist/esm/model/charts.js +15 -2
  27. package/dist/esm/model/charts.schema.js +9 -4
  28. package/dist/esm/model/index.js +6 -6
  29. package/dist/esm/model/liquidations.schema.js +1 -1
  30. package/dist/esm/model/opportunities.schema.js +2 -2
  31. package/dist/esm/model/positions.schema.js +2 -2
  32. package/dist/esm/offchain/analytics/OffchainAnalytics.js +25 -2
  33. package/dist/esm/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  34. package/dist/esm/onchain/accounts/liquidations/LiquidationsService.js +1 -1
  35. package/dist/esm/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
  36. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
  37. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
  38. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  39. package/dist/esm/onchain/base/TokensMeta.js +3 -3
  40. package/dist/esm/onchain/core/createAddressProvider.js +1 -1
  41. package/dist/esm/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  42. package/dist/esm/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  43. package/dist/esm/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  44. package/dist/esm/onchain/market/pool/PoolV310Contract.js +1 -1
  45. package/dist/esm/onchain/market/zapper/IETHZapperContract.js +1 -1
  46. package/dist/esm/onchain/market/zapper/ZapperContract.js +1 -1
  47. package/dist/esm/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  48. package/dist/esm/preview/simulate/simulatePoolOperation.js +1 -1
  49. package/dist/esm/preview/trace/extractTransfers.js +1 -1
  50. package/dist/esm/rewards/errors.js +26 -0
  51. package/dist/esm/rewards/index.js +4 -5
  52. package/dist/esm/rewards/merkl-api.js +42 -0
  53. package/dist/esm/rewards/multichain.js +39 -0
  54. package/dist/esm/rewards/{rewards/api.js → toMerklRewards.js} +26 -28
  55. package/dist/esm/sdk/analytics/AnalyticsNamespace.js +6 -0
  56. package/dist/types/dev/mode-parity/compareOpportunities.d.ts +3 -7
  57. package/dist/types/dev/mode-parity/comparePositions.d.ts +4 -8
  58. package/dist/types/dev/mode-parity/fieldDiff.d.ts +9 -8
  59. package/dist/types/dev/mode-parity/scriptUtils.d.ts +26 -1
  60. package/dist/types/model/analytics.d.ts +22 -1
  61. package/dist/types/model/analytics.schema.d.ts +65 -1
  62. package/dist/types/model/charts.d.ts +20 -2
  63. package/dist/types/model/charts.schema.d.ts +14 -2
  64. package/dist/types/model/index.d.ts +5 -5
  65. package/dist/types/offchain/AbstractOffchainNamespace.d.ts +1 -1
  66. package/dist/types/offchain/analytics/OffchainAnalytics.d.ts +14 -2
  67. package/dist/types/offchain/analytics/types.d.ts +11 -0
  68. package/dist/types/offchain/opportunities/OffchainOpportunities.d.ts +1 -1
  69. package/dist/types/offchain/opportunities/types.d.ts +1 -1
  70. package/dist/types/offchain/positions/OffchainPositions.d.ts +1 -1
  71. package/dist/types/offchain/positions/types.d.ts +1 -1
  72. package/dist/types/rewards/errors.d.ts +18 -0
  73. package/dist/types/rewards/index.d.ts +4 -7
  74. package/dist/types/rewards/merkl-api.d.ts +64 -0
  75. package/dist/types/rewards/multichain.d.ts +33 -0
  76. package/dist/types/rewards/toMerklRewards.d.ts +42 -0
  77. package/dist/types/sdk/analytics/AnalyticsNamespace.d.ts +6 -0
  78. package/dist/types/sdk/analytics/types.d.ts +16 -0
  79. package/dist/types/sdk/opportunities/OpportunitiesNamespace.d.ts +1 -1
  80. package/dist/types/sdk/opportunities/types.d.ts +1 -1
  81. package/dist/types/sdk/positions/PositionsNamespace.d.ts +1 -1
  82. package/dist/types/sdk/positions/types.d.ts +1 -1
  83. package/package.json +1 -3
  84. package/dist/cjs/_virtual/_rolldown/runtime.js +0 -23
  85. package/dist/cjs/rewards/apy/index.js +0 -3
  86. package/dist/cjs/rewards/apy/output-details.js +0 -1
  87. package/dist/cjs/rewards/apy/output.js +0 -1
  88. package/dist/cjs/rewards/rewards/common.js +0 -1
  89. package/dist/cjs/rewards/rewards/extra-apy.js +0 -71
  90. package/dist/cjs/rewards/rewards/index.js +0 -6
  91. package/dist/cjs/rewards/rewards/merkl-api.js +0 -22
  92. package/dist/esm/rewards/apy/index.js +0 -3
  93. package/dist/esm/rewards/apy/output-details.js +0 -1
  94. package/dist/esm/rewards/apy/output.js +0 -1
  95. package/dist/esm/rewards/rewards/common.js +0 -1
  96. package/dist/esm/rewards/rewards/extra-apy.js +0 -67
  97. package/dist/esm/rewards/rewards/index.js +0 -3
  98. package/dist/esm/rewards/rewards/merkl-api.js +0 -19
  99. package/dist/types/rewards/apy/index.d.ts +0 -3
  100. package/dist/types/rewards/apy/output-details.d.ts +0 -99
  101. package/dist/types/rewards/apy/output.d.ts +0 -25
  102. package/dist/types/rewards/rewards/api.d.ts +0 -60
  103. package/dist/types/rewards/rewards/common.d.ts +0 -9
  104. package/dist/types/rewards/rewards/extra-apy.d.ts +0 -37
  105. package/dist/types/rewards/rewards/index.d.ts +0 -3
  106. package/dist/types/rewards/rewards/merkl-api.d.ts +0 -54
@@ -15,8 +15,8 @@ const tagDiff = require_dev_mode_parity_compareRules.makeTagDiff({
15
15
  *
16
16
  * Nothing is filtered out. A field only the backend can fill, or a USD value
17
17
  * that drifted within snapshot-lag noise, is still reported — tagged
18
- * {@link FieldDiff.expected} so that {@link CompareCounts.clean} can ignore it
19
- * while {@link CompareCounts.identical} stays strict.
18
+ * {@link FieldDiff.expected} so that {@link CompareCounts.similar} can ignore
19
+ * it.
20
20
  **/
21
21
  function compareOpportunities(input) {
22
22
  const onchainRows = indexById(input.onchain.data);
@@ -37,8 +37,7 @@ function compareOpportunities(input) {
37
37
  chainId: row.chainId,
38
38
  onchainName: row.name,
39
39
  offchainName: counterpart.name,
40
- identical: diffs.length === 0,
41
- clean: diffs.every((diff) => diff.expected),
40
+ similar: diffs.every((diff) => diff.expected),
42
41
  diffs
43
42
  });
44
43
  }
@@ -18,8 +18,8 @@ const tagDiff = require_dev_mode_parity_compareRules.makeTagDiff({
18
18
  * Nothing is filtered out. A field only one mode can fill, a strategy field
19
19
  * both-mode merge overlays from the backend, or a USD value that drifted
20
20
  * within snapshot-lag noise, is still reported — tagged
21
- * {@link FieldDiff.expected} so that {@link CompareCounts.clean} can ignore it
22
- * while {@link CompareCounts.identical} stays strict.
21
+ * {@link FieldDiff.expected} so that {@link CompareCounts.similar} can ignore
22
+ * it.
23
23
  **/
24
24
  function comparePositions(input) {
25
25
  const failures = input.failures ?? [];
@@ -66,8 +66,7 @@ function compareWallet(input) {
66
66
  chainId: row.chainId,
67
67
  onchainName: row.name,
68
68
  offchainName: counterpart.name,
69
- identical: diffs.length === 0,
70
- clean: diffs.every((diff) => diff.expected),
69
+ similar: diffs.every((diff) => diff.expected),
71
70
  diffs
72
71
  });
73
72
  }
@@ -130,11 +129,11 @@ function summarize(inputs, wallets, failed) {
130
129
  ...wallet.error ? { error: wallet.error } : {},
131
130
  ...require_dev_mode_parity_fieldDiff.toCompareCounts(wallet.matched.length + wallet.onlyOnchain.length, wallet.matched.length + wallet.onlyOffchain.length, wallet.onlyOnchain.length, wallet.onlyOffchain.length, wallet.matched)
132
131
  }));
133
- const walletsClean = compared.filter((wallet) => wallet.onlyOnchain.length === 0 && wallet.onlyOffchain.length === 0 && wallet.matched.every((match) => match.clean)).length;
132
+ const walletsSimilar = compared.filter((wallet) => wallet.onlyOnchain.length === 0 && wallet.onlyOffchain.length === 0 && wallet.matched.every((match) => match.similar)).length;
134
133
  return {
135
134
  ...require_dev_mode_parity_fieldDiff.toCompareCounts(allOnchain.length, allOffchain.length, onlyOnchain.length, onlyOffchain.length, matched),
136
135
  wallets: wallets.length,
137
- walletsClean,
136
+ walletsSimilar,
138
137
  walletsFailed: failed,
139
138
  byChain,
140
139
  byWallet,
@@ -54,7 +54,7 @@ function diffObjects(onchain, offchain, options) {
54
54
  return diffs;
55
55
  }
56
56
  /**
57
- * Mark a diff expected, so a later `clean` count can ignore it.
57
+ * Mark a diff expected, so a later `similar` count can ignore it.
58
58
  **/
59
59
  function withExpected(diff, reason) {
60
60
  return {
@@ -118,15 +118,13 @@ function collapseArrayKeys(path) {
118
118
  * Membership and match totals of one comparison, from already-built lists.
119
119
  **/
120
120
  function toCompareCounts(onchainRows, offchainRows, onlyOnchain, onlyOffchain, matched) {
121
- const identical = matched.filter((match) => match.identical).length;
122
- const clean = matched.filter((match) => match.clean).length;
121
+ const similar = matched.filter((match) => match.similar).length;
123
122
  return {
124
123
  onchainRows,
125
124
  offchainRows,
126
125
  matched: matched.length,
127
- identical,
128
- clean,
129
- differing: matched.length - identical,
126
+ similar,
127
+ different: matched.length - similar,
130
128
  onlyOnchain,
131
129
  onlyOffchain
132
130
  };
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_dev_providers = require("../providers.js");
3
+ let node_fs = require("node:fs");
3
4
  let pino = require("pino");
4
5
  //#region src/dev/mode-parity/scriptUtils.ts
5
6
  const NETWORKS = [
@@ -77,6 +78,40 @@ function worstColumns(worst) {
77
78
  };
78
79
  }
79
80
  /**
81
+ * Reasons a compare run should fail CI: unexpected membership or field diffs,
82
+ * both sources empty, a chain that could not be read, or wallets that failed
83
+ * to list. Expected diffs (mode-scoped, backend-preferred, tolerance) do not
84
+ * fail the run.
85
+ **/
86
+ function evaluateCompareFailure(report) {
87
+ const { summary } = report;
88
+ const reasons = [];
89
+ if (summary.onchainRows === 0 && summary.offchainRows === 0) reasons.push("both sources produced no data");
90
+ if (summary.different > 0) reasons.push(`${summary.different} matched rows are different`);
91
+ if (summary.onlyOnchain > 0) reasons.push(`${summary.onlyOnchain} rows only onchain`);
92
+ if (summary.onlyOffchain > 0) reasons.push(`${summary.onlyOffchain} rows only offchain`);
93
+ if ((summary.walletsFailed ?? 0) > 0) reasons.push(`${summary.walletsFailed} wallets failed to list`);
94
+ for (const chain of [...report.onchainChains, ...report.offchainChains]) if (chain.status === "error") reasons.push(`chain ${chain.chainId} failed on ${chain.source ?? "unknown"}: ${errorMessage(chain.error)}`);
95
+ return reasons;
96
+ }
97
+ function splitDiffs(summary) {
98
+ return {
99
+ unexpected: summary.diffsByPath.filter((entry) => entry.unexpected > 0),
100
+ expected: summary.diffsByPath.filter((entry) => entry.unexpected === 0 && entry.expected > 0)
101
+ };
102
+ }
103
+ function markdownCell(value) {
104
+ return String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
105
+ }
106
+ function markdownTable(headers, rows) {
107
+ const line = (cells) => `| ${cells.map(markdownCell).join(" | ")} |`;
108
+ return [
109
+ line(headers),
110
+ line(headers.map(() => "---")),
111
+ ...rows.map(line)
112
+ ].join("\n");
113
+ }
114
+ /**
80
115
  * Prints the shared membership table, the fields that differed most often,
81
116
  * and any chain that failed to answer.
82
117
  **/
@@ -88,15 +123,13 @@ function printCompareSummary(noun, report, extraLines = []) {
88
123
  onchain: chain.onchainRows,
89
124
  offchain: chain.offchainRows,
90
125
  matched: chain.matched,
91
- identical: chain.identical,
92
- clean: chain.clean,
93
- differing: chain.differing,
126
+ similar: chain.similar,
127
+ different: chain.different,
94
128
  "only onchain": chain.onlyOnchain,
95
129
  "only offchain": chain.onlyOffchain
96
130
  })));
97
131
  for (const line of extraLines) console.log(line);
98
- const unexpected = summary.diffsByPath.filter((entry) => entry.unexpected > 0);
99
- const expected = summary.diffsByPath.filter((entry) => entry.unexpected === 0 && entry.expected > 0);
132
+ const { unexpected, expected } = splitDiffs(summary);
100
133
  if (unexpected.length) {
101
134
  console.log("\nunexpected fields that differed most often:");
102
135
  console.table(unexpected.slice(0, 25).map((entry) => ({
@@ -118,14 +151,121 @@ function printCompareSummary(noun, report, extraLines = []) {
118
151
  }
119
152
  for (const chain of [...report.onchainChains, ...report.offchainChains]) if (chain.status === "error") console.log(`chain ${chain.chainId} failed on ${chain.source}:`, chain.error);
120
153
  }
154
+ /**
155
+ * Markdown of the same summary printed to the console, for GitHub job summaries.
156
+ **/
157
+ function formatCompareMarkdown(noun, report, extraLines = [], reasons = []) {
158
+ const { summary } = report;
159
+ const { unexpected, expected } = splitDiffs(summary);
160
+ const parts = [
161
+ `# ${noun} compare`,
162
+ "",
163
+ reasons.length ? "**Failed**" : "**Passed**",
164
+ "",
165
+ `${noun} from \`${report.backendUrl}\` vs the chain`,
166
+ ""
167
+ ];
168
+ if (reasons.length) {
169
+ parts.push("## Failures", "");
170
+ for (const reason of reasons) parts.push(`- ${reason}`);
171
+ parts.push("");
172
+ }
173
+ if (extraLines.length) {
174
+ for (const line of extraLines) parts.push(line);
175
+ parts.push("");
176
+ }
177
+ if (summary.byChain.length) parts.push(markdownTable([
178
+ "chain",
179
+ "onchain",
180
+ "offchain",
181
+ "matched",
182
+ "similar",
183
+ "different",
184
+ "only onchain",
185
+ "only offchain"
186
+ ], summary.byChain.map((chain) => [
187
+ chain.chainId,
188
+ chain.onchainRows,
189
+ chain.offchainRows,
190
+ chain.matched,
191
+ chain.similar,
192
+ chain.different,
193
+ chain.onlyOnchain,
194
+ chain.onlyOffchain
195
+ ])), "");
196
+ if (unexpected.length) parts.push("## Unexpected fields that differed most often", "", markdownTable([
197
+ "field",
198
+ "unexpected",
199
+ "expected",
200
+ "kinds",
201
+ "max diff",
202
+ "worst entity"
203
+ ], unexpected.slice(0, 25).map((entry) => {
204
+ const worst = worstColumns(entry.worstUnexpected);
205
+ return [
206
+ entry.path,
207
+ entry.unexpected,
208
+ entry.expected,
209
+ entry.kinds.join(", "),
210
+ worst["max diff"],
211
+ worst["worst entity"]
212
+ ];
213
+ })), "");
214
+ if (expected.length) parts.push("## Expected fields (mode-scoped, backend-preferred, or within tolerance)", "", markdownTable([
215
+ "field",
216
+ "rows",
217
+ "kinds",
218
+ "max diff",
219
+ "worst entity"
220
+ ], expected.slice(0, 25).map((entry) => {
221
+ const worst = worstColumns(entry.worstExpected);
222
+ return [
223
+ entry.path,
224
+ entry.expected,
225
+ entry.kinds.join(", "),
226
+ worst["max diff"],
227
+ worst["worst entity"]
228
+ ];
229
+ })), "");
230
+ const failedChains = [...report.onchainChains, ...report.offchainChains].filter((chain) => chain.status === "error");
231
+ if (failedChains.length) {
232
+ parts.push("## Chain errors", "");
233
+ for (const chain of failedChains) parts.push(`- chain ${chain.chainId} failed on ${chain.source ?? "unknown"}: ${errorMessage(chain.error)}`);
234
+ parts.push("");
235
+ }
236
+ return parts.join("\n");
237
+ }
238
+ /**
239
+ * Appends {@link formatCompareMarkdown} to `GITHUB_STEP_SUMMARY` when running
240
+ * in GitHub Actions. No-op locally.
241
+ **/
242
+ function writeGithubJobSummary(noun, report, extraLines = [], reasons = []) {
243
+ const path = process.env.GITHUB_STEP_SUMMARY;
244
+ if (!path) return;
245
+ (0, node_fs.appendFileSync)(path, `${formatCompareMarkdown(noun, report, extraLines, reasons)}\n`);
246
+ }
247
+ /**
248
+ * Console + job-summary output for a compare run. Returns the failure reasons
249
+ * so the caller can exit 1 when the report is not similar.
250
+ **/
251
+ function reportCompare(noun, report, extraLines = []) {
252
+ printCompareSummary(noun, report, extraLines);
253
+ const reasons = evaluateCompareFailure(report);
254
+ writeGithubJobSummary(noun, report, extraLines, reasons);
255
+ return reasons;
256
+ }
121
257
  //#endregion
122
258
  exports.BACKEND_URL = BACKEND_URL;
123
259
  exports.NETWORKS = NETWORKS;
124
260
  exports.TIMEOUT = TIMEOUT;
125
261
  exports.createLogger = createLogger;
126
262
  exports.errorMessage = errorMessage;
263
+ exports.evaluateCompareFailure = evaluateCompareFailure;
127
264
  exports.formatBpsAsPercent = formatBpsAsPercent;
265
+ exports.formatCompareMarkdown = formatCompareMarkdown;
128
266
  exports.mapPool = mapPool;
129
267
  exports.printCompareSummary = printCompareSummary;
268
+ exports.reportCompare = reportCompare;
130
269
  exports.requireEnv = requireEnv;
131
270
  exports.rpcUrls = rpcUrls;
271
+ exports.writeGithubJobSummary = writeGithubJobSummary;
@@ -1,8 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_zod = require("../onchain/utils/zod.js");
3
+ const require_model_primitives_schema = require("./primitives.schema.js");
4
+ const require_model_charts_schema = require("./charts.schema.js");
3
5
  const require_model_filters = require("./filters.js");
4
6
  const require_model_filters_schema = require("./filters.schema.js");
5
- const require_model_primitives_schema = require("./primitives.schema.js");
6
7
  const require_model_liquidations_schema = require("./liquidations.schema.js");
7
8
  const require_model_positions_schema = require("./positions.schema.js");
8
9
  let viem = require("viem");
@@ -105,9 +106,51 @@ const analyticsPositionPageSchema = zod_v4.z.object({
105
106
  offset: zod_v4.z.number().int().nonnegative(),
106
107
  limit: zod_v4.z.number().int().positive().max(100)
107
108
  });
109
+ /**
110
+ * {@link AnalyticsChartQuery}
111
+ *
112
+ * The metric list is narrowed to {@link ProtocolChartMetric} here rather than
113
+ * on the route, so asking the protocol chart for an opportunity metric is
114
+ * rejected by the one codec both sides share instead of by a check only the
115
+ * backend runs.
116
+ **/
117
+ const analyticsChartQueryOptionsSchema = zod_v4.z.object({
118
+ metrics: zod_v4.z.array(require_model_charts_schema.protocolChartMetricSchema).readonly().refine((metrics) => metrics.length > 0, { error: "a chart read needs at least one metric" }).refine((metrics) => new Set(metrics).size === metrics.length, { error: "a chart read needs distinct metrics" }),
119
+ range: require_model_charts_schema.chartRangeSchema,
120
+ chainIds: zod_v4.z.array(require_model_primitives_schema.chainIdSchema).optional()
121
+ });
122
+ /**
123
+ * {@link AnalyticsChartQuery} as URL query parameters: the metrics comma-joined
124
+ * like every other chart read, and the chains comma-joined like every other
125
+ * analytics read.
126
+ **/
127
+ const analyticsChartQueryParamsSchema = zod_v4.z.object({
128
+ metrics: zod_v4.z.string().regex(/^\w+(,\w+)*$/),
129
+ range: require_model_charts_schema.chartRangeSchema,
130
+ chainIds: zod_v4.z.string().regex(/^$|^\d+(,\d+)*$/).optional()
131
+ });
132
+ /**
133
+ * Codec for a protocol-wide chart query. It is shared by the SDK client and
134
+ * backend controller so both sides interpret every parameter identically.
135
+ **/
136
+ const analyticsChartQuerySchema = zod_v4.z.codec(analyticsChartQueryParamsSchema, analyticsChartQueryOptionsSchema, {
137
+ decode: (params) => ({
138
+ metrics: params.metrics.split(","),
139
+ range: params.range,
140
+ ...params.chainIds === void 0 ? {} : { chainIds: params.chainIds === "" ? [] : params.chainIds.split(",").map(Number) }
141
+ }),
142
+ encode: (query) => ({
143
+ metrics: query.metrics.join(","),
144
+ range: query.range,
145
+ chainIds: query.chainIds?.join(",")
146
+ })
147
+ });
108
148
  //#endregion
109
149
  exports.ANALYTICS_POSITIONS_DEFAULT_LIMIT = ANALYTICS_POSITIONS_DEFAULT_LIMIT;
110
150
  exports.ANALYTICS_POSITIONS_MAX_LIMIT = ANALYTICS_POSITIONS_MAX_LIMIT;
151
+ exports.analyticsChartQueryOptionsSchema = analyticsChartQueryOptionsSchema;
152
+ exports.analyticsChartQueryParamsSchema = analyticsChartQueryParamsSchema;
153
+ exports.analyticsChartQuerySchema = analyticsChartQuerySchema;
111
154
  exports.analyticsPositionListOptionsSchema = analyticsPositionListOptionsSchema;
112
155
  exports.analyticsPositionListQueryParamsSchema = analyticsPositionListQueryParamsSchema;
113
156
  exports.analyticsPositionListQuerySchema = analyticsPositionListQuerySchema;
@@ -85,6 +85,18 @@ const STRATEGY_POSITION_CHART_METRICS = [
85
85
  "netApy7d"
86
86
  ];
87
87
  /**
88
+ * Every metric the protocol as a whole can chart.
89
+ *
90
+ * Protocol-wide rather than per subject: the series describes Gearbox itself
91
+ * over every chain the read covers, so it has no address to hang off and is
92
+ * read from the analytics namespace rather than from an opportunity.
93
+ *
94
+ * `tvlUsd` is deliberately not spelled `tvl`: that key is already the credit
95
+ * manager's TVL in whole underlying tokens, and one metric has exactly one
96
+ * unit in {@link CHART_METRIC_UNITS}.
97
+ **/
98
+ const PROTOCOL_CHART_METRICS = ["tvlUsd"];
99
+ /**
88
100
  * Unit of every metric, the one place either side decides it.
89
101
  *
90
102
  * A metric added to a union above fails to compile here until its unit is
@@ -114,7 +126,8 @@ const CHART_METRIC_UNITS = {
114
126
  totalValueUnderlying: "token",
115
127
  debt: "token",
116
128
  healthFactor: "bps",
117
- netApy7d: "bps"
129
+ netApy7d: "bps",
130
+ tvlUsd: "usd"
118
131
  };
119
132
  /**
120
133
  * Reason a series could not be produced at all, which is not the same as a
@@ -133,5 +146,6 @@ exports.CHART_RANGES = CHART_RANGES;
133
146
  exports.CHART_UNAVAILABLE_CODES = CHART_UNAVAILABLE_CODES;
134
147
  exports.POOL_OPPORTUNITY_CHART_METRICS = POOL_OPPORTUNITY_CHART_METRICS;
135
148
  exports.POOL_POSITION_CHART_METRICS = POOL_POSITION_CHART_METRICS;
149
+ exports.PROTOCOL_CHART_METRICS = PROTOCOL_CHART_METRICS;
136
150
  exports.STRATEGY_OPPORTUNITY_CHART_METRICS = STRATEGY_OPPORTUNITY_CHART_METRICS;
137
151
  exports.STRATEGY_POSITION_CHART_METRICS = STRATEGY_POSITION_CHART_METRICS;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_model_primitives_schema = require("./primitives.schema.js");
3
2
  const require_model_charts = require("./charts.js");
3
+ const require_model_primitives_schema = require("./primitives.schema.js");
4
4
  let zod_v4 = require("zod/v4");
5
5
  //#region src/model/charts.schema.ts
6
6
  /**
@@ -33,13 +33,18 @@ const poolPositionChartMetricSchema = zod_v4.z.enum(require_model_charts.POOL_PO
33
33
  **/
34
34
  const strategyPositionChartMetricSchema = zod_v4.z.enum(require_model_charts.STRATEGY_POSITION_CHART_METRICS);
35
35
  /**
36
- * {@link ChartMetric}, every metric either kind of subject can chart.
36
+ * {@link ProtocolChartMetric}
37
+ **/
38
+ const protocolChartMetricSchema = zod_v4.z.enum(require_model_charts.PROTOCOL_CHART_METRICS);
39
+ /**
40
+ * {@link ChartMetric}, every metric any subject can chart.
37
41
  **/
38
42
  const chartMetricSchema = zod_v4.z.union([
39
43
  poolOpportunityChartMetricSchema,
40
44
  strategyOpportunityChartMetricSchema,
41
45
  poolPositionChartMetricSchema,
42
- strategyPositionChartMetricSchema
46
+ strategyPositionChartMetricSchema,
47
+ protocolChartMetricSchema
43
48
  ]);
44
49
  /**
45
50
  * {@link ChartQuery}
@@ -236,5 +241,6 @@ exports.chartValueSchema = chartValueSchema;
236
241
  exports.chartWindowSchema = chartWindowSchema;
237
242
  exports.poolOpportunityChartMetricSchema = poolOpportunityChartMetricSchema;
238
243
  exports.poolPositionChartMetricSchema = poolPositionChartMetricSchema;
244
+ exports.protocolChartMetricSchema = protocolChartMetricSchema;
239
245
  exports.strategyOpportunityChartMetricSchema = strategyOpportunityChartMetricSchema;
240
246
  exports.strategyPositionChartMetricSchema = strategyPositionChartMetricSchema;
@@ -1,16 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./analytics.js");
3
+ const require_model_charts = require("./charts.js");
4
+ const require_model_compare_schema = require("./compare.schema.js");
5
+ const require_model_primitives_schema = require("./primitives.schema.js");
6
+ const require_model_charts_schema = require("./charts.schema.js");
3
7
  const require_model_filters = require("./filters.js");
4
8
  const require_model_filters_schema = require("./filters.schema.js");
5
- const require_model_compare_schema = require("./compare.schema.js");
6
9
  const require_model_curators_schema = require("./curators.schema.js");
7
- const require_model_primitives_schema = require("./primitives.schema.js");
8
10
  const require_model_liquidations_schema = require("./liquidations.schema.js");
9
11
  const require_model_opportunities_schema = require("./opportunities.schema.js");
10
12
  const require_model_positions_schema = require("./positions.schema.js");
11
13
  const require_model_analytics_schema = require("./analytics.schema.js");
12
- const require_model_charts = require("./charts.js");
13
- const require_model_charts_schema = require("./charts.schema.js");
14
14
  require("./curators.js");
15
15
  require("./delayed-intents.js");
16
16
  require("./errors.js");
@@ -34,10 +34,14 @@ exports.CHART_UNAVAILABLE_CODES = require_model_charts.CHART_UNAVAILABLE_CODES;
34
34
  exports.FILTER_ALL = require_model_filters.FILTER_ALL;
35
35
  exports.POOL_OPPORTUNITY_CHART_METRICS = require_model_charts.POOL_OPPORTUNITY_CHART_METRICS;
36
36
  exports.POOL_POSITION_CHART_METRICS = require_model_charts.POOL_POSITION_CHART_METRICS;
37
+ exports.PROTOCOL_CHART_METRICS = require_model_charts.PROTOCOL_CHART_METRICS;
37
38
  exports.STRATEGY_OPPORTUNITY_CHART_METRICS = require_model_charts.STRATEGY_OPPORTUNITY_CHART_METRICS;
38
39
  exports.STRATEGY_POSITION_CHART_METRICS = require_model_charts.STRATEGY_POSITION_CHART_METRICS;
39
40
  exports.STRATEGY_POSITION_COLLATERAL_ERROR = require_model_positions.STRATEGY_POSITION_COLLATERAL_ERROR;
40
41
  exports.amountSchema = require_model_primitives_schema.amountSchema;
42
+ exports.analyticsChartQueryOptionsSchema = require_model_analytics_schema.analyticsChartQueryOptionsSchema;
43
+ exports.analyticsChartQueryParamsSchema = require_model_analytics_schema.analyticsChartQueryParamsSchema;
44
+ exports.analyticsChartQuerySchema = require_model_analytics_schema.analyticsChartQuerySchema;
41
45
  exports.analyticsPositionListOptionsSchema = require_model_analytics_schema.analyticsPositionListOptionsSchema;
42
46
  exports.analyticsPositionListQueryParamsSchema = require_model_analytics_schema.analyticsPositionListQueryParamsSchema;
43
47
  exports.analyticsPositionListQuerySchema = require_model_analytics_schema.analyticsPositionListQuerySchema;
@@ -131,6 +135,7 @@ exports.positionWithdrawalsSchema = require_model_withdrawals_schema.positionWit
131
135
  exports.positionsTotalsSchema = require_model_positions_schema.positionsTotalsSchema;
132
136
  exports.priceFeedDataSchema = require_model_opportunities_schema.priceFeedDataSchema;
133
137
  exports.priceFeedSummarySchema = require_model_opportunities_schema.priceFeedSummarySchema;
138
+ exports.protocolChartMetricSchema = require_model_charts_schema.protocolChartMetricSchema;
134
139
  exports.quotaAssetSchema = require_model_opportunities_schema.quotaAssetSchema;
135
140
  exports.rateCurvePointSchema = require_model_opportunities_schema.rateCurvePointSchema;
136
141
  exports.rateCurveSchema = require_model_opportunities_schema.rateCurveSchema;
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_zod = require("../onchain/utils/zod.js");
3
+ const require_model_primitives_schema = require("./primitives.schema.js");
3
4
  const require_model_filters_schema = require("./filters.schema.js");
4
5
  const require_model_curators_schema = require("./curators.schema.js");
5
- const require_model_primitives_schema = require("./primitives.schema.js");
6
6
  let zod_v4 = require("zod/v4");
7
7
  //#region src/model/liquidations.schema.ts
8
8
  /**
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_zod = require("../onchain/utils/zod.js");
3
+ const require_model_compare_schema = require("./compare.schema.js");
4
+ const require_model_primitives_schema = require("./primitives.schema.js");
3
5
  const require_model_filters = require("./filters.js");
4
6
  const require_model_filters_schema = require("./filters.schema.js");
5
- const require_model_compare_schema = require("./compare.schema.js");
6
7
  const require_model_curators_schema = require("./curators.schema.js");
7
- const require_model_primitives_schema = require("./primitives.schema.js");
8
8
  let zod_v4 = require("zod/v4");
9
9
  //#region src/model/opportunities.schema.ts
10
10
  /**
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_zod = require("../onchain/utils/zod.js");
3
- const require_model_filters = require("./filters.js");
4
- const require_model_filters_schema = require("./filters.schema.js");
5
3
  const require_model_compare_schema = require("./compare.schema.js");
6
4
  const require_model_primitives_schema = require("./primitives.schema.js");
5
+ const require_model_filters = require("./filters.js");
6
+ const require_model_filters_schema = require("./filters.schema.js");
7
7
  const require_model_liquidations_schema = require("./liquidations.schema.js");
8
8
  const require_model_opportunities_schema = require("./opportunities.schema.js");
9
9
  let zod_v4 = require("zod/v4");
@@ -1,12 +1,35 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_model_charts_schema = require("../../model/charts.schema.js");
3
+ const require_model_analytics_schema = require("../../model/analytics.schema.js");
4
+ const require_offchain_AbstractOffchainNamespace = require("../AbstractOffchainNamespace.js");
2
5
  const require_offchain_analytics_OffchainAnalyticsPositions = require("./OffchainAnalyticsPositions.js");
6
+ let zod_v4 = require("zod/v4");
3
7
  //#region src/offchain/analytics/OffchainAnalytics.ts
4
- /** Backend-only protocol analytics, grouped by subject. */
5
- var OffchainAnalytics = class {
8
+ /**
9
+ * Backend-only protocol analytics, grouped by subject.
10
+ *
11
+ * It owns the protocol-wide charts itself rather than through a sub-namespace:
12
+ * their subject is Gearbox as a whole, so there is nothing narrower for them to
13
+ * hang off.
14
+ **/
15
+ var OffchainAnalytics = class extends require_offchain_AbstractOffchainNamespace.AbstractOffchainNamespace {
6
16
  positions;
7
17
  constructor(options) {
18
+ super("OffchainAnalytics", options);
8
19
  this.positions = new require_offchain_analytics_OffchainAnalyticsPositions.OffchainAnalyticsPositions(options);
9
20
  }
21
+ /** {@inheritDoc IOffchainAnalytics.getCharts} */
22
+ async getCharts(metrics, range, filter) {
23
+ return this.get({
24
+ path: "/v2/analytics/charts",
25
+ query: zod_v4.z.encode(require_model_analytics_schema.analyticsChartQuerySchema, {
26
+ metrics,
27
+ range,
28
+ chainIds: this.scopedChainIds(filter)
29
+ }),
30
+ schema: require_model_charts_schema.chartBundleSchemaFor(metrics, range)
31
+ });
32
+ }
10
33
  };
11
34
  //#endregion
12
35
  exports.OffchainAnalytics = OffchainAnalytics;
@@ -0,0 +1,27 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let viem = require("viem");
3
+ //#region src/rewards/errors.ts
4
+ /**
5
+ * Thrown when none of Merkl's domains answered for a chain.
6
+ *
7
+ * Everything a reader needs is in the message: this error travels to consumers
8
+ * inside a chain's {@link ChainFailed} metadata, where it is typed `unknown`
9
+ * and may be serialised by something that turns an arbitrary object into `{}`.
10
+ */
11
+ var MerklRequestFailedError = class extends viem.BaseError {
12
+ name = "MerklRequestFailedError";
13
+ chainId;
14
+ constructor(chainId, path, attempts) {
15
+ super(`Merkl could not be reached for chain ${chainId}.`, {
16
+ cause: attempts.find(([, c]) => c instanceof Error)?.[1],
17
+ metaMessages: attempts.map(([domain, cause]) => `${domain}${path} — ${describe(cause)}`)
18
+ });
19
+ this.chainId = chainId;
20
+ }
21
+ };
22
+ function describe(cause) {
23
+ if (cause instanceof Error) return cause.name === "TimeoutError" ? "timed out" : cause.message;
24
+ return String(cause);
25
+ }
26
+ //#endregion
27
+ exports.MerklRequestFailedError = MerklRequestFailedError;
@@ -1,8 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./apy/index.js");
3
- const require_rewards_rewards_api = require("./rewards/api.js");
4
- const require_rewards_rewards_extra_apy = require("./rewards/extra-apy.js");
5
- require("./rewards/index.js");
6
- exports.PoolPointsAPI = require_rewards_rewards_extra_apy.PoolPointsAPI;
7
- exports.getKeyForPoolPointsInfo = require_rewards_rewards_extra_apy.getKeyForPoolPointsInfo;
8
- exports.getMerklRewards = require_rewards_rewards_api.getMerklRewards;
2
+ const require_rewards_errors = require("./errors.js");
3
+ const require_rewards_toMerklRewards = require("./toMerklRewards.js");
4
+ const require_rewards_multichain = require("./multichain.js");
5
+ exports.MerklRequestFailedError = require_rewards_errors.MerklRequestFailedError;
6
+ exports.getMerklRewardsMultichain = require_rewards_multichain.getMerklRewardsMultichain;
7
+ exports.toMerklRewards = require_rewards_toMerklRewards.toMerklRewards;
@@ -0,0 +1,45 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rewards_errors = require("./errors.js");
3
+ //#region src/rewards/merkl-api.ts
4
+ /**
5
+ * Merkl's own host and the Angle mirror, tried in this order.
6
+ */
7
+ const MERKL_DOMAINS = ["https://api.merkl.xyz", "https://api-merkl.angle.money"];
8
+ const MERKL_API_KEY_HEADER = "X-API-Key";
9
+ /**
10
+ * Per-attempt budget. Merkl has no timeout of its own, and a hung connection
11
+ * would otherwise stall its leg of a fan-out for as long as the socket lives.
12
+ */
13
+ const ATTEMPT_TIMEOUT = 1e4;
14
+ /**
15
+ * The wallet's raw Merkl rewards on one chain.
16
+ *
17
+ * Rejects with {@link MerklRequestFailedError} when neither domain answers, so
18
+ * a caller can tell an unreachable Merkl from a wallet with nothing to claim.
19
+ * A non-2xx counts as no answer and moves to the next domain: it carries no
20
+ * rewards either way, and treating it as success would report emptiness that
21
+ * was never established.
22
+ */
23
+ async function fetchMerklUserRewards({ chainId, user, apiKey }) {
24
+ const path = `/v4/users/${user}/rewards?chainId=${chainId}`;
25
+ const headers = apiKey ? { [MERKL_API_KEY_HEADER]: apiKey } : void 0;
26
+ const attempts = [];
27
+ for (const domain of MERKL_DOMAINS) try {
28
+ const response = await fetch(`${domain}${path}`, {
29
+ headers,
30
+ signal: AbortSignal.timeout(ATTEMPT_TIMEOUT)
31
+ });
32
+ if (!response.ok) {
33
+ attempts.push([domain, /* @__PURE__ */ new Error(`answered ${response.status}`)]);
34
+ continue;
35
+ }
36
+ return await response.json();
37
+ } catch (error) {
38
+ attempts.push([domain, error]);
39
+ }
40
+ throw new require_rewards_errors.MerklRequestFailedError(chainId, path, attempts);
41
+ }
42
+ //#endregion
43
+ exports.MERKL_API_KEY_HEADER = MERKL_API_KEY_HEADER;
44
+ exports.MERKL_DOMAINS = MERKL_DOMAINS;
45
+ exports.fetchMerklUserRewards = fetchMerklUserRewards;
@@ -0,0 +1,40 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_onchain_base_MultichainConstruct = require("../onchain/base/MultichainConstruct.js");
3
+ require("../onchain/index.js");
4
+ const require_rewards_merkl_api = require("./merkl-api.js");
5
+ const require_rewards_toMerklRewards = require("./toMerklRewards.js");
6
+ let viem = require("viem");
7
+ //#region src/rewards/multichain.ts
8
+ /**
9
+ * The fan-out itself. Private because rewards are not an SDK namespace yet:
10
+ * the read is a free function, and this is only how it reaches `queryChains`.
11
+ **/
12
+ var MerklRewardsFanOut = class extends require_onchain_base_MultichainConstruct.MultichainConstruct {
13
+ async list(wallet, chainIds, apiKey) {
14
+ const user = (0, viem.getAddress)(wallet);
15
+ return this.queryChains({
16
+ chainIds,
17
+ label: "list rewards",
18
+ block: "state",
19
+ run: async (sdk) => require_rewards_toMerklRewards.toMerklRewards(sdk, await require_rewards_merkl_api.fetchMerklUserRewards({
20
+ chainId: sdk.chainId,
21
+ user,
22
+ apiKey
23
+ }))
24
+ });
25
+ }
26
+ };
27
+ /**
28
+ * Every claimable Merkl reward a wallet holds, across the chains the handle
29
+ * carries.
30
+ *
31
+ * Answers the read model's own envelope, so a chain that could not be reached
32
+ * is `status: "error"` in `meta.chains` while a chain with nothing to claim is
33
+ * a `"success"` that contributed no rows. That distinction is the point: the
34
+ * single-chain read this replaces resolved empty either way.
35
+ **/
36
+ async function getMerklRewardsMultichain({ sdk, wallet, chainIds, apiKey }) {
37
+ return new MerklRewardsFanOut(sdk).list(wallet, chainIds, apiKey);
38
+ }
39
+ //#endregion
40
+ exports.getMerklRewardsMultichain = getMerklRewardsMultichain;