@gearbox-protocol/sdk 15.1.0-next.11 → 15.1.0-next.13

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 (53) hide show
  1. package/dist/cjs/dev/mode-parity/compareOpportunities.js +105 -0
  2. package/dist/cjs/dev/{comparePositions.js → mode-parity/comparePositions.js} +23 -46
  3. package/dist/cjs/dev/mode-parity/compareRules.js +93 -0
  4. package/dist/cjs/dev/{fieldDiff.js → mode-parity/fieldDiff.js} +49 -2
  5. package/dist/cjs/dev/mode-parity/scriptUtils.js +131 -0
  6. package/dist/cjs/model/compare.schema.js +33 -0
  7. package/dist/cjs/model/curators.schema.js +2 -1
  8. package/dist/cjs/model/index.js +5 -0
  9. package/dist/cjs/model/opportunities.schema.js +13 -12
  10. package/dist/cjs/model/positions.schema.js +15 -14
  11. package/dist/cjs/model/primitives.schema.js +2 -1
  12. package/dist/cjs/new-sdk/index.js +3 -3
  13. package/dist/cjs/new-sdk/opportunities/OpportunitiesNamespace.js +8 -8
  14. package/dist/cjs/new-sdk/{simulate/SimulateApi.js → prepare/PrepareApi.js} +21 -49
  15. package/dist/cjs/new-sdk/prepare/index.js +4 -0
  16. package/dist/esm/dev/{compareOpportunities.js → mode-parity/compareOpportunities.js} +14 -46
  17. package/dist/esm/dev/{comparePositions.js → mode-parity/comparePositions.js} +16 -39
  18. package/dist/esm/dev/mode-parity/compareRules.js +91 -0
  19. package/dist/esm/dev/{fieldDiff.js → mode-parity/fieldDiff.js} +49 -3
  20. package/dist/esm/dev/mode-parity/scriptUtils.js +121 -0
  21. package/dist/esm/model/compare.schema.js +29 -0
  22. package/dist/esm/model/curators.schema.js +2 -1
  23. package/dist/esm/model/index.js +2 -1
  24. package/dist/esm/model/opportunities.schema.js +13 -12
  25. package/dist/esm/model/positions.schema.js +15 -14
  26. package/dist/esm/model/primitives.schema.js +2 -1
  27. package/dist/esm/new-sdk/index.js +3 -3
  28. package/dist/esm/new-sdk/opportunities/OpportunitiesNamespace.js +8 -8
  29. package/dist/esm/new-sdk/{simulate/SimulateApi.js → prepare/PrepareApi.js} +21 -49
  30. package/dist/esm/new-sdk/prepare/index.js +3 -0
  31. package/dist/types/dev/{compareOpportunities.d.ts → mode-parity/compareOpportunities.d.ts} +5 -5
  32. package/dist/types/dev/{comparePositions.d.ts → mode-parity/comparePositions.d.ts} +5 -5
  33. package/dist/types/dev/mode-parity/compareRules.d.ts +33 -0
  34. package/dist/types/dev/{fieldDiff.d.ts → mode-parity/fieldDiff.d.ts} +47 -5
  35. package/dist/types/dev/mode-parity/scriptUtils.d.ts +47 -0
  36. package/dist/types/model/compare.schema.d.ts +47 -0
  37. package/dist/types/model/index.d.ts +2 -1
  38. package/dist/types/new-sdk/execute/ExecuteApi.d.ts +9 -9
  39. package/dist/types/new-sdk/index.d.ts +4 -4
  40. package/dist/types/new-sdk/opportunities/OpportunitiesNamespace.d.ts +4 -4
  41. package/dist/types/new-sdk/opportunities/types.d.ts +5 -5
  42. package/dist/types/new-sdk/{simulate/SimulateApi.d.ts → prepare/PrepareApi.d.ts} +19 -19
  43. package/dist/types/new-sdk/prepare/index.d.ts +3 -0
  44. package/dist/types/new-sdk/{simulate → prepare}/types.d.ts +35 -61
  45. package/dist/types/sdk/accounts/intents/testing/sdk-mock.d.ts +1 -1
  46. package/dist/types/sdk/accounts/intents/types.d.ts +12 -12
  47. package/package.json +1 -1
  48. package/dist/cjs/dev/compareOpportunities.js +0 -137
  49. package/dist/cjs/new-sdk/simulate/index.js +0 -4
  50. package/dist/esm/new-sdk/simulate/index.js +0 -3
  51. package/dist/types/new-sdk/simulate/index.d.ts +0 -3
  52. /package/dist/cjs/new-sdk/{simulate → prepare}/types.js +0 -0
  53. /package/dist/esm/new-sdk/{simulate → prepare}/types.js +0 -0
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_sdk_utils_zod = require("../sdk/utils/zod.js");
3
+ const require_model_compare_schema = require("./compare.schema.js");
3
4
  const require_model_primitives_schema = require("./primitives.schema.js");
4
5
  const require_model_filters = require("./filters.js");
5
6
  const require_model_filters_schema = require("./filters.schema.js");
@@ -56,8 +57,8 @@ const pnlBreakdownSchema = zod_v4.z.object({
56
57
  * {@link PositionCollateral}
57
58
  **/
58
59
  const positionCollateralSchema = zod_v4.z.object({
59
- collateral: require_model_primitives_schema.tokenAmountSchema,
60
- quota: require_model_primitives_schema.tokenAmountSchema,
60
+ collateral: require_model_compare_schema.tolerance(require_model_primitives_schema.tokenAmountSchema, "amount"),
61
+ quota: require_model_compare_schema.tolerance(require_model_primitives_schema.tokenAmountSchema, "amount"),
61
62
  withdrawals: zod_v4.z.array(require_model_liquidations_schema.delayedReceivedAssetSchema)
62
63
  });
63
64
  /**
@@ -68,9 +69,9 @@ const poolPositionSchema = zod_v4.z.object({
68
69
  name: zod_v4.z.string(),
69
70
  chainId: require_model_primitives_schema.chainIdSchema,
70
71
  pool: require_sdk_utils_zod.ZodAddress(),
71
- netValue: require_model_primitives_schema.tokenAmountSchema,
72
+ netValue: require_model_compare_schema.tolerance(require_model_primitives_schema.tokenAmountSchema, "amount"),
72
73
  apy: require_model_opportunities_schema.apyBreakdownSchema,
73
- pnl: pnlBreakdownSchema.optional()
74
+ pnl: require_model_compare_schema.offchainOnly(pnlBreakdownSchema).optional()
74
75
  });
75
76
  /**
76
77
  * {@link BorrowRateBreakdown}
@@ -91,16 +92,16 @@ const strategyPositionSchema = zod_v4.z.object({
91
92
  creditManager: require_sdk_utils_zod.ZodAddress(),
92
93
  creditAccount: require_sdk_utils_zod.ZodAddress(),
93
94
  targetCollateral: require_model_primitives_schema.tokenSchema.nullable(),
94
- leverage: require_model_primitives_schema.leverageSchema,
95
- borrowApy: require_model_primitives_schema.bpsSchema,
96
- netApy: require_model_opportunities_schema.apyBreakdownSchema.optional(),
97
- totalDebt: require_model_primitives_schema.tokenAmountSchema,
98
- totalValue: require_model_primitives_schema.tokenAmountSchema,
99
- healthFactor: require_model_primitives_schema.bpsSchema,
100
- borrowRate: borrowRateBreakdownSchema.optional(),
101
- timeToLiquidation: require_sdk_utils_zod.ZodBigInt().nullable().optional(),
102
- liquidationPrice: require_sdk_utils_zod.ZodBigInt().nullable().optional(),
103
- pnl: pnlBreakdownSchema.optional(),
95
+ leverage: require_model_compare_schema.tolerance(require_model_primitives_schema.leverageSchema, "float"),
96
+ borrowApy: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps"),
97
+ netApy: require_model_compare_schema.offchainOnly(require_model_opportunities_schema.apyBreakdownSchema).optional(),
98
+ totalDebt: require_model_compare_schema.tolerance(require_model_primitives_schema.tokenAmountSchema, "amount"),
99
+ totalValue: require_model_compare_schema.tolerance(require_model_primitives_schema.tokenAmountSchema, "amount"),
100
+ healthFactor: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps"),
101
+ borrowRate: require_model_compare_schema.onchainOnly(borrowRateBreakdownSchema).optional(),
102
+ timeToLiquidation: require_model_compare_schema.onchainOnly(require_sdk_utils_zod.ZodBigInt().nullable()).optional(),
103
+ liquidationPrice: require_model_compare_schema.onchainOnly(require_sdk_utils_zod.ZodBigInt().nullable()).optional(),
104
+ pnl: require_model_compare_schema.offchainOnly(pnlBreakdownSchema).optional(),
104
105
  collaterals: zod_v4.z.array(positionCollateralSchema)
105
106
  });
106
107
  /**
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_sdk_utils_zod = require("../sdk/utils/zod.js");
3
+ const require_model_compare_schema = require("./compare.schema.js");
3
4
  let zod_v4 = require("zod/v4");
4
5
  //#region src/model/primitives.schema.ts
5
6
  /**
@@ -40,7 +41,7 @@ const leverageSchema = zod_v4.z.number().nonnegative();
40
41
  **/
41
42
  const amountSchema = zod_v4.z.object({
42
43
  value: require_sdk_utils_zod.ZodBigInt(),
43
- valueUsd: zod_v4.z.number().nullable()
44
+ valueUsd: require_model_compare_schema.tolerance(zod_v4.z.number().nullable(), "usd")
44
45
  });
45
46
  /**
46
47
  * {@link Token}
@@ -10,8 +10,8 @@ require("./errors/index.js");
10
10
  const require_new_sdk_AbstractNamespace = require("./AbstractNamespace.js");
11
11
  const require_new_sdk_execute_ExecuteApi = require("./execute/ExecuteApi.js");
12
12
  require("./execute/index.js");
13
- const require_new_sdk_simulate_SimulateApi = require("./simulate/SimulateApi.js");
14
- require("./simulate/index.js");
13
+ const require_new_sdk_prepare_PrepareApi = require("./prepare/PrepareApi.js");
14
+ require("./prepare/index.js");
15
15
  const require_new_sdk_utils_filterResponse = require("./utils/filterResponse.js");
16
16
  const require_new_sdk_utils_mergeChains = require("./utils/mergeChains.js");
17
17
  require("./utils/index.js");
@@ -31,7 +31,7 @@ exports.MissingSourceError = require_new_sdk_errors_MissingSourceError.MissingSo
31
31
  exports.NoSourceServedError = require_new_sdk_errors_NoSourceServedError.NoSourceServedError;
32
32
  exports.OpportunitiesNamespace = require_new_sdk_opportunities_OpportunitiesNamespace.OpportunitiesNamespace;
33
33
  exports.PositionsNamespace = require_new_sdk_positions_PositionsNamespace.PositionsNamespace;
34
- exports.SimulateApi = require_new_sdk_simulate_SimulateApi.SimulateApi;
34
+ exports.PrepareApi = require_new_sdk_prepare_PrepareApi.PrepareApi;
35
35
  exports.SourceChainMismatchError = require_new_sdk_errors_SourceChainMismatchError.SourceChainMismatchError;
36
36
  exports.SourceUnavailableError = require_new_sdk_errors_SourceUnavailableError.SourceUnavailableError;
37
37
  exports.assertSameChains = require_new_sdk_errors_assertSameChains.assertSameChains;
@@ -6,8 +6,8 @@ require("../errors/index.js");
6
6
  const require_new_sdk_AbstractNamespace = require("../AbstractNamespace.js");
7
7
  const require_new_sdk_execute_ExecuteApi = require("../execute/ExecuteApi.js");
8
8
  require("../execute/index.js");
9
- const require_new_sdk_simulate_SimulateApi = require("../simulate/SimulateApi.js");
10
- require("../simulate/index.js");
9
+ const require_new_sdk_prepare_PrepareApi = require("../prepare/PrepareApi.js");
10
+ require("../prepare/index.js");
11
11
  const require_new_sdk_utils_filterResponse = require("../utils/filterResponse.js");
12
12
  const require_new_sdk_utils_mergeChains = require("../utils/mergeChains.js");
13
13
  require("../utils/index.js");
@@ -25,19 +25,19 @@ var OpportunitiesNamespace = class extends require_new_sdk_AbstractNamespace.Abs
25
25
  pool: (onchain, offchain) => require_new_sdk_utils_mergeChains.mergeChainOne(onchain, offchain, this.maxOffchainLagSeconds),
26
26
  strategy: (onchain, offchain) => require_new_sdk_utils_mergeChains.mergeChainOne(onchain, offchain, this.maxOffchainLagSeconds)
27
27
  };
28
- #simulate;
28
+ #prepare;
29
29
  #execute;
30
30
  constructor(onchain, offchain, options) {
31
31
  super("Opportunities", onchain?.opportunities, offchain?.opportunities, options);
32
- this.#simulate = onchain && new require_new_sdk_simulate_SimulateApi.SimulateApi(onchain, options.ensureFresh);
32
+ this.#prepare = onchain && new require_new_sdk_prepare_PrepareApi.PrepareApi(onchain, options.ensureFresh);
33
33
  this.#execute = onchain && new require_new_sdk_execute_ExecuteApi.ExecuteApi((chainId) => onchain.chain(chainId));
34
34
  }
35
35
  /**
36
- * {@inheritDoc OpportunitiesOnchainOnly.simulate}
36
+ * {@inheritDoc OpportunitiesOnchainOnly.prepare}
37
37
  **/
38
- get simulate() {
39
- if (!this.#simulate) throw new require_new_sdk_errors_SourceUnavailableError.SourceUnavailableError("Opportunities", "onchain");
40
- return this.#simulate;
38
+ get prepare() {
39
+ if (!this.#prepare) throw new require_new_sdk_errors_SourceUnavailableError.SourceUnavailableError("Opportunities", "onchain");
40
+ return this.#prepare;
41
41
  }
42
42
  /**
43
43
  * {@inheritDoc OpportunitiesOnchainOnly.execute}
@@ -4,9 +4,9 @@ const require_sdk_base_MultichainConstruct = require("../../sdk/base/MultichainC
4
4
  const require_sdk_accounts_intents_utils_credit_account_slice = require("../../sdk/accounts/intents/utils/credit-account-slice.js");
5
5
  const require_sdk_accounts_intents_index = require("../../sdk/accounts/intents/index.js");
6
6
  require("../../sdk/index.js");
7
- //#region src/new-sdk/simulate/SimulateApi.ts
7
+ //#region src/new-sdk/prepare/PrepareApi.ts
8
8
  /**
9
- * {@inheritDoc OpportunitiesSimulate}
9
+ * {@inheritDoc OpportunitiesPrepare}
10
10
  *
11
11
  * Holds no state of its own: it owns the mapping from the public,
12
12
  * read-model-shaped request to the engine's intent, and nothing else. All
@@ -18,7 +18,7 @@ require("../../sdk/index.js");
18
18
  * to, hence a chain the SDK does not cover, or one that fails the read, throws
19
19
  * rather than answering with empty metadata.
20
20
  **/
21
- var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainConstruct {
21
+ var PrepareApi = class extends require_sdk_base_MultichainConstruct.MultichainConstruct {
22
22
  #ensureFresh;
23
23
  constructor(sdk, ensureFresh) {
24
24
  super(sdk);
@@ -29,22 +29,10 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
29
29
  return super.queryChain(props);
30
30
  }
31
31
  /**
32
- * {@inheritDoc OpportunitiesSimulate.delayed}
32
+ * {@inheritDoc OpportunitiesPrepare.finalize}
33
33
  **/
34
- delayed = {
35
- withdrawStrategy: (position, params) => this.#startDelayedIntent(position, params, {
36
- type: "WITHDRAW",
37
- amount: params.amount,
38
- to: params.to,
39
- tokenOut: params.tokenOut,
40
- sourceToken: params.sourceToken
41
- }),
42
- adjustLeverage: (position, params) => this.#startDelayedIntent(position, params, {
43
- type: "ADJUST_LEVERAGE",
44
- targetLeverage: params.targetLeverage,
45
- token: params.token
46
- }),
47
- finish: (position, params) => this.queryChain({
34
+ async finalize(position, params) {
35
+ return this.queryChain({
48
36
  network: position.chainId,
49
37
  run: async (sdk) => {
50
38
  const intent = resumable(params.intent ?? params.claimable.intent);
@@ -61,10 +49,10 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
61
49
  quotaReserve: params.quotaReserve
62
50
  });
63
51
  }
64
- })
65
- };
52
+ });
53
+ }
66
54
  /**
67
- * {@inheritDoc OpportunitiesSimulate.deposit}
55
+ * {@inheritDoc OpportunitiesPrepare.deposit}
68
56
  **/
69
57
  deposit(pool, params) {
70
58
  const { marketRegister, pools } = this.sdk.chain(pool.chainId);
@@ -98,7 +86,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
98
86
  };
99
87
  }
100
88
  /**
101
- * {@inheritDoc OpportunitiesSimulate.withdraw}
89
+ * {@inheritDoc OpportunitiesPrepare.withdraw}
102
90
  **/
103
91
  withdraw(pool, params) {
104
92
  const { pools } = this.sdk.chain(pool.chainId);
@@ -130,7 +118,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
130
118
  };
131
119
  }
132
120
  /**
133
- * {@inheritDoc OpportunitiesSimulate.redeem}
121
+ * {@inheritDoc OpportunitiesPrepare.redeem}
134
122
  **/
135
123
  redeem(pool, params) {
136
124
  const { pools } = this.sdk.chain(pool.chainId);
@@ -162,7 +150,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
162
150
  };
163
151
  }
164
152
  /**
165
- * {@inheritDoc OpportunitiesSimulate.openNewStrategy}
153
+ * {@inheritDoc OpportunitiesPrepare.openNewStrategy}
166
154
  **/
167
155
  async openNewStrategy(strategy, params) {
168
156
  return this.queryChain({
@@ -180,7 +168,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
180
168
  });
181
169
  }
182
170
  /**
183
- * {@inheritDoc OpportunitiesSimulate.depositStrategy}
171
+ * {@inheritDoc OpportunitiesPrepare.depositStrategy}
184
172
  **/
185
173
  async depositStrategy(position, params) {
186
174
  return this.#startIntent(position, params, {
@@ -193,7 +181,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
193
181
  });
194
182
  }
195
183
  /**
196
- * {@inheritDoc OpportunitiesSimulate.withdrawStrategy}
184
+ * {@inheritDoc OpportunitiesPrepare.withdrawStrategy}
197
185
  **/
198
186
  async withdrawStrategy(position, params) {
199
187
  return this.#startRoutes(position, params, {
@@ -205,7 +193,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
205
193
  });
206
194
  }
207
195
  /**
208
- * {@inheritDoc OpportunitiesSimulate.maxWithdraw}
196
+ * {@inheritDoc OpportunitiesPrepare.maxWithdraw}
209
197
  **/
210
198
  async maxWithdraw(position) {
211
199
  return this.queryChain({
@@ -217,7 +205,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
217
205
  });
218
206
  }
219
207
  /**
220
- * {@inheritDoc OpportunitiesSimulate.repayStrategy}
208
+ * {@inheritDoc OpportunitiesPrepare.repayStrategy}
221
209
  **/
222
210
  async repayStrategy(position, params) {
223
211
  return this.#startIntent(position, params, {
@@ -228,7 +216,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
228
216
  });
229
217
  }
230
218
  /**
231
- * {@inheritDoc OpportunitiesSimulate.maxRepay}
219
+ * {@inheritDoc OpportunitiesPrepare.maxRepay}
232
220
  **/
233
221
  async maxRepay(position) {
234
222
  return this.queryChain({
@@ -240,7 +228,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
240
228
  });
241
229
  }
242
230
  /**
243
- * {@inheritDoc OpportunitiesSimulate.adjustLeverage}
231
+ * {@inheritDoc OpportunitiesPrepare.adjustLeverage}
244
232
  **/
245
233
  async adjustLeverage(position, params) {
246
234
  return this.#startRoutes(position, params, {
@@ -250,7 +238,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
250
238
  });
251
239
  }
252
240
  /**
253
- * {@inheritDoc OpportunitiesSimulate.addCollateral}
241
+ * {@inheritDoc OpportunitiesPrepare.addCollateral}
254
242
  **/
255
243
  async addCollateral(position, params) {
256
244
  return this.#startIntent(position, params, {
@@ -261,7 +249,7 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
261
249
  });
262
250
  }
263
251
  /**
264
- * {@inheritDoc OpportunitiesSimulate.withdrawCollateral}
252
+ * {@inheritDoc OpportunitiesPrepare.withdrawCollateral}
265
253
  **/
266
254
  async withdrawCollateral(position, params) {
267
255
  return this.#startIntent(position, params, {
@@ -288,22 +276,6 @@ var SimulateApi = class extends require_sdk_base_MultichainConstruct.MultichainC
288
276
  });
289
277
  }
290
278
  /**
291
- * Shared path of the same two flows when only the delayed route is asked for:
292
- * a planner that requests a redemption instead of swapping.
293
- **/
294
- async #startDelayedIntent(position, options, intent) {
295
- return this.queryChain({
296
- network: position.chainId,
297
- run: async (sdk) => service(sdk).startDelayedIntent({
298
- intent,
299
- creditAccount: await slice(sdk, position.creditAccount),
300
- sdk,
301
- slippage: options.slippage,
302
- quotaReserve: options.quotaReserve
303
- })
304
- });
305
- }
306
- /**
307
279
  * Shared path of the five flows that act on an existing account: read the
308
280
  * account, then run the intent through the engine.
309
281
  **/
@@ -358,4 +330,4 @@ function lpRoute(requested, routes) {
358
330
  return options.length === 1 ? options[0] : void 0;
359
331
  }
360
332
  //#endregion
361
- exports.SimulateApi = SimulateApi;
333
+ exports.PrepareApi = PrepareApi;
@@ -0,0 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_new_sdk_prepare_PrepareApi = require("./PrepareApi.js");
3
+ require("./types.js");
4
+ exports.PrepareApi = require_new_sdk_prepare_PrepareApi.PrepareApi;
@@ -1,7 +1,13 @@
1
- import { opportunityId } from "../model/opportunities.js";
2
- import "../model/index.js";
3
- import { countPaths, diffObjects, isAmountWithinTolerance, isBpsWithinTolerance, isUsdWithinTolerance, toCompareCounts, union, withExpected } from "./fieldDiff.js";
4
- //#region src/dev/compareOpportunities.ts
1
+ import { opportunityId } from "../../model/opportunities.js";
2
+ import { poolOpportunitySchema, strategyOpportunitySchema } from "../../model/opportunities.schema.js";
3
+ import "../../model/index.js";
4
+ import { countPaths, diffObjects, toCompareCounts, union } from "./fieldDiff.js";
5
+ import { compileCompareRules, makeTagDiff } from "./compareRules.js";
6
+ //#region src/dev/mode-parity/compareOpportunities.ts
7
+ const tagDiff = makeTagDiff({
8
+ pool: compileCompareRules(poolOpportunitySchema),
9
+ strategy: compileCompareRules(strategyOpportunitySchema)
10
+ });
5
11
  /**
6
12
  * Matches two opportunity listings by {@link opportunityId} and reports every
7
13
  * field the two sources disagree on.
@@ -79,47 +85,6 @@ function toRef(row) {
79
85
  function diffOpportunity(onchain, offchain) {
80
86
  return diffObjects(onchain, offchain).map((diff) => tagDiff(diff, onchain.kind));
81
87
  }
82
- /**
83
- * Paths whose values are basis-point rates that routinely differ by ±1 from
84
- * truncation vs rounding, plus pool `utilization` for the same reason.
85
- **/
86
- const BPS_RATE_PATHS = /* @__PURE__ */ new Set([
87
- "borrowApy",
88
- "supplyApy.organicApy",
89
- "additionalBorrowApy",
90
- "utilization"
91
- ]);
92
- /**
93
- * Amount fields whose bigint `value` moves with expected-liquidity accrual
94
- * between the backend's last sync and the current block. Strategy
95
- * `totalBorrow.value` and `maxBorrowAmount.value` are not in this set: those
96
- * disagreements are formula bugs, not lag.
97
- **/
98
- const LAG_AMOUNT_PATHS = /* @__PURE__ */ new Set(["totalSupply.value", "availableLiquidity.value"]);
99
- /**
100
- * Fields documented `@mode offchain` in the model: the chain has nothing to
101
- * put there, so a presence (or nested) mismatch is expected. Strategy
102
- * `utilization` is in this set; pool `utilization` is not.
103
- **/
104
- function isModeScoped(path, kind) {
105
- if (path === "curator.url") return true;
106
- if (path === "totalApy" || path.endsWith(".totalApy")) return true;
107
- if (path === "rewards" || path.startsWith("rewards[") || path.includes(".rewards[") || path.endsWith(".rewards")) return true;
108
- if (kind !== "strategy") return false;
109
- return path === "utilization" || path === "collateralApy" || path.startsWith("collateralApy.") || path === "maxLeverageApy" || path.startsWith("maxLeverageApy.") || path === "totalValue" || path.startsWith("totalValue.");
110
- }
111
- function tagDiff(diff, kind) {
112
- if (isModeScoped(diff.path, kind)) return withExpected(diff, "mode-scoped");
113
- if (withinTolerance(diff)) return withExpected(diff, "tolerance");
114
- return diff;
115
- }
116
- function withinTolerance(diff) {
117
- if (diff.kind === "usd") return isUsdWithinTolerance(diff.onchain, diff.offchain);
118
- if (diff.kind !== "numeric") return false;
119
- if (BPS_RATE_PATHS.has(diff.path)) return isBpsWithinTolerance(diff.onchain, diff.offchain);
120
- if (LAG_AMOUNT_PATHS.has(diff.path)) return isAmountWithinTolerance(diff.onchain, diff.offchain);
121
- return false;
122
- }
123
88
  function summarize(onchain, offchain, onlyOnchain, onlyOffchain, matched) {
124
89
  const byChain = union(onchain.map((row) => String(row.chainId)), offchain.map((row) => String(row.chainId))).map((chainId) => ({
125
90
  chainId: Number(chainId),
@@ -128,7 +93,10 @@ function summarize(onchain, offchain, onlyOnchain, onlyOffchain, matched) {
128
93
  return {
129
94
  ...toCompareCounts(onchain.length, offchain.length, onlyOnchain.length, onlyOffchain.length, matched),
130
95
  byChain,
131
- diffsByPath: countPaths(matched.flatMap((match) => match.diffs))
96
+ diffsByPath: countPaths(matched.flatMap((match) => match.diffs.map((diff) => ({
97
+ id: match.id,
98
+ diff
99
+ }))))
132
100
  };
133
101
  }
134
102
  //#endregion
@@ -1,7 +1,15 @@
1
- import { positionId } from "../model/positions.js";
2
- import "../model/index.js";
3
- import { USD_RELATIVE_EPSILON, asFiniteNumber, countPaths, diffObjects, isAmountWithinTolerance, isBpsWithinTolerance, isRecord, isUsdWithinTolerance, toCompareCounts, union, withExpected, withinRelative } from "./fieldDiff.js";
4
- //#region src/dev/comparePositions.ts
1
+ import { liquidationPositionSchema } from "../../model/liquidations.schema.js";
2
+ import { positionId } from "../../model/positions.js";
3
+ import { poolPositionSchema, strategyPositionSchema } from "../../model/positions.schema.js";
4
+ import "../../model/index.js";
5
+ import { countPaths, diffObjects, isRecord, toCompareCounts, union } from "./fieldDiff.js";
6
+ import { compileCompareRules, makeTagDiff } from "./compareRules.js";
7
+ //#region src/dev/mode-parity/comparePositions.ts
8
+ const tagDiff = makeTagDiff({
9
+ pool: compileCompareRules(poolPositionSchema),
10
+ strategy: compileCompareRules(strategyPositionSchema),
11
+ liquidation: compileCompareRules(liquidationPositionSchema)
12
+ });
5
13
  /**
6
14
  * Matches two position listings per wallet by {@link positionId} and reports
7
15
  * every field the two sources disagree on.
@@ -104,40 +112,6 @@ function keyOfPositionArray(path, value) {
104
112
  const token = value.collateral.token;
105
113
  return isRecord(token) && typeof token.address === "string" ? token.address : void 0;
106
114
  }
107
- const BPS_RATE_PATHS = /* @__PURE__ */ new Set([
108
- "borrowApy",
109
- "apy.organicApy",
110
- "healthFactor"
111
- ]);
112
- /**
113
- * Amount fields whose bigint `value` moves with interest accrual between the
114
- * backend's last sync and the current block.
115
- **/
116
- function isLagAmountPath(path) {
117
- return path === "totalDebt.value" || path === "totalValue.value" || path === "netValue.value" || path.endsWith(".collateral.value") || path.endsWith(".quota.value");
118
- }
119
- /**
120
- * Fields documented `@mode offchain` or `@mode onchain` in the model.
121
- **/
122
- function isModeScoped(path, kind) {
123
- if (path === "pnl" || path.startsWith("pnl.")) return true;
124
- if (kind === "pool") return path === "apy.totalApy" || path.startsWith("apy.rewards");
125
- if (kind === "strategy") return path === "netApy" || path.startsWith("netApy.") || path === "borrowRate" || path.startsWith("borrowRate.") || path === "timeToLiquidation" || path === "liquidationPrice";
126
- return false;
127
- }
128
- function tagDiff(diff, kind) {
129
- if (isModeScoped(diff.path, kind)) return withExpected(diff, "mode-scoped");
130
- if (withinTolerance(diff)) return withExpected(diff, "tolerance");
131
- return diff;
132
- }
133
- function withinTolerance(diff) {
134
- if (diff.kind === "usd") return isUsdWithinTolerance(diff.onchain, diff.offchain);
135
- if (diff.kind !== "numeric") return false;
136
- if (diff.path === "leverage") return withinRelative(asFiniteNumber(diff.onchain), asFiniteNumber(diff.offchain), USD_RELATIVE_EPSILON);
137
- if (BPS_RATE_PATHS.has(diff.path)) return isBpsWithinTolerance(diff.onchain, diff.offchain);
138
- if (isLagAmountPath(diff.path)) return isAmountWithinTolerance(diff.onchain, diff.offchain);
139
- return false;
140
- }
141
115
  function summarize(inputs, wallets, failed) {
142
116
  const compared = wallets.filter((wallet) => !wallet.error);
143
117
  const allOnchain = inputs.flatMap((input) => input.onchain.data);
@@ -162,7 +136,10 @@ function summarize(inputs, wallets, failed) {
162
136
  walletsFailed: failed,
163
137
  byChain,
164
138
  byWallet,
165
- diffsByPath: countPaths(matched.flatMap((match) => match.diffs))
139
+ diffsByPath: countPaths(matched.flatMap((match) => match.diffs.map((diff) => ({
140
+ id: match.id,
141
+ diff
142
+ }))))
166
143
  };
167
144
  }
168
145
  /**
@@ -0,0 +1,91 @@
1
+ import { compareTagOf } from "../../model/compare.schema.js";
2
+ import { USD_RELATIVE_EPSILON, asFiniteNumber, collapseArrayKeys, isAmountWithinTolerance, isBpsWithinTolerance, isUsdWithinTolerance, withExpected, withinRelative } from "./fieldDiff.js";
3
+ //#region src/dev/mode-parity/compareRules.ts
4
+ /**
5
+ * Walks a schema and records every field that carries compare metadata.
6
+ *
7
+ * `"amount"` on an object (an Amount / TokenAmount) is stored
8
+ * at `<path>.value`; every other tag is stored at the field's own path.
9
+ **/
10
+ function compileCompareRules(schema) {
11
+ const rules = /* @__PURE__ */ new Map();
12
+ walk(schema, "", /* @__PURE__ */ new Set(), rules);
13
+ return rules;
14
+ }
15
+ /**
16
+ * Tags diffs using the rules compiled for each row kind.
17
+ *
18
+ * Mode tags match the path or anything nested under it. Tolerance tags match
19
+ * the path exactly and dispatch on {@link CompareTolerance}.
20
+ **/
21
+ function makeTagDiff(rulesByKind) {
22
+ return (diff, kind) => {
23
+ const rules = rulesByKind[kind];
24
+ if (!rules) return diff;
25
+ const path = collapseArrayKeys(diff.path);
26
+ if (isModeScoped(path, rules)) return withExpected(diff, "mode-scoped");
27
+ const tag = rules.get(path);
28
+ if (tag && typeof tag === "object" && withinTolerance(tag.tolerance, diff)) return withExpected(diff, "tolerance");
29
+ return diff;
30
+ };
31
+ }
32
+ function isModeScoped(path, rules) {
33
+ for (const [rulePath, tag] of rules) {
34
+ if (tag !== "offchainOnly" && tag !== "onchainOnly") continue;
35
+ if (path === rulePath || path.startsWith(`${rulePath}.`) || path.startsWith(`${rulePath}[`)) return true;
36
+ }
37
+ return false;
38
+ }
39
+ function withinTolerance(kind, diff) {
40
+ switch (kind) {
41
+ case "usd": return isUsdWithinTolerance(diff.onchain, diff.offchain);
42
+ case "bps": return isBpsWithinTolerance(diff.onchain, diff.offchain);
43
+ case "amount": return isAmountWithinTolerance(diff.onchain, diff.offchain);
44
+ case "float": return withinRelative(asFiniteNumber(diff.onchain), asFiniteNumber(diff.offchain), USD_RELATIVE_EPSILON);
45
+ }
46
+ }
47
+ function walk(schema, path, seen, rules) {
48
+ if (seen.has(schema)) return;
49
+ const nextSeen = new Set(seen);
50
+ nextSeen.add(schema);
51
+ const tag = compareTagOf(schema);
52
+ if (tag) record(path, tag, schema, rules);
53
+ const def = schema.def;
54
+ switch (def.type) {
55
+ case "optional":
56
+ case "nullable":
57
+ case "default":
58
+ case "prefault":
59
+ case "readonly":
60
+ if (def.innerType) walk(def.innerType, path, nextSeen, rules);
61
+ return;
62
+ case "array":
63
+ if (def.element) walk(def.element, `${path}[]`, nextSeen, rules);
64
+ return;
65
+ case "object":
66
+ if (def.shape) for (const [key, field] of Object.entries(def.shape)) walk(field, join(path, key), nextSeen, rules);
67
+ return;
68
+ case "union":
69
+ for (const option of def.options ?? []) walk(option, path, nextSeen, rules);
70
+ return;
71
+ case "pipe":
72
+ if (def.out) walk(def.out, path, nextSeen, rules);
73
+ return;
74
+ case "lazy":
75
+ if (def.getter) walk(def.getter(), path, nextSeen, rules);
76
+ return;
77
+ default: return;
78
+ }
79
+ }
80
+ function record(path, tag, schema, rules) {
81
+ if (typeof tag === "object" && tag.tolerance === "amount" && schema.def.type === "object") {
82
+ rules.set(join(path, "value"), tag);
83
+ return;
84
+ }
85
+ rules.set(path, tag);
86
+ }
87
+ function join(path, key) {
88
+ return path ? `${path}.${key}` : key;
89
+ }
90
+ //#endregion
91
+ export { compileCompareRules, makeTagDiff };
@@ -1,4 +1,4 @@
1
- //#region src/dev/fieldDiff.ts
1
+ //#region src/dev/mode-parity/fieldDiff.ts
2
2
  /**
3
3
  * Relative drift allowed on {@link Amount.valueUsd} before a USD float is a
4
4
  * real disagreement: 0.1%.
@@ -69,7 +69,7 @@ function withExpected(diff, reason) {
69
69
  **/
70
70
  function countPaths(diffs) {
71
71
  const counts = /* @__PURE__ */ new Map();
72
- for (const diff of diffs) {
72
+ for (const { id, diff } of diffs) {
73
73
  const path = collapseArrayKeys(diff.path);
74
74
  const entry = counts.get(path) ?? {
75
75
  path,
@@ -82,10 +82,31 @@ function countPaths(diffs) {
82
82
  if (diff.expected) entry.expected += 1;
83
83
  else entry.unexpected += 1;
84
84
  if (!entry.kinds.includes(diff.kind)) entry.kinds.push(diff.kind);
85
+ recordWorst(entry, id, diff);
85
86
  counts.set(path, entry);
86
87
  }
87
88
  return [...counts.values()].sort((a, b) => b.unexpected - a.unexpected || b.count - a.count || a.path.localeCompare(b.path));
88
89
  }
90
+ function recordWorst(entry, id, diff) {
91
+ const bps = relativeDiffBps(diff.onchain, diff.offchain);
92
+ if (bps === void 0) return;
93
+ const worst = {
94
+ id,
95
+ path: diff.path,
96
+ bps,
97
+ onchain: diff.onchain,
98
+ offchain: diff.offchain
99
+ };
100
+ if (diff.expected) {
101
+ if (isWorse(worst, entry.worstExpected)) entry.worstExpected = worst;
102
+ return;
103
+ }
104
+ if (isWorse(worst, entry.worstUnexpected)) entry.worstUnexpected = worst;
105
+ }
106
+ function isWorse(candidate, current) {
107
+ if (!current) return true;
108
+ return candidate.bps > current.bps || candidate.bps === current.bps && candidate.id.localeCompare(current.id) < 0;
109
+ }
89
110
  /**
90
111
  * Collapse `collateralTokens[0xa0b8...].symbol` to `collateralTokens[].symbol`.
91
112
  **/
@@ -151,6 +172,31 @@ function withinRelativeBigint(onchain, offchain, epsilon) {
151
172
  function asFiniteNumber(value) {
152
173
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
153
174
  }
175
+ /**
176
+ * Relative difference of two numbers or bigints, in bps:
177
+ * `|a − b| / max(|a|, |b|) * 10_000`. `undefined` when the values are not
178
+ * a comparable pair of finite numbers or of bigints.
179
+ **/
180
+ function relativeDiffBps(onchain, offchain) {
181
+ if (typeof onchain === "bigint" && typeof offchain === "bigint") return relativeDiffBpsBigint(onchain, offchain);
182
+ const left = asFiniteNumber(onchain);
183
+ const right = asFiniteNumber(offchain);
184
+ if (left === void 0 || right === void 0) return;
185
+ const scale = Math.max(Math.abs(left), Math.abs(right));
186
+ return scale === 0 ? 0 : Math.abs(left - right) / scale * 1e4;
187
+ }
188
+ /**
189
+ * Same formula as {@link relativeDiffBps} for bigints, computed in integer
190
+ * arithmetic to milli-bps so a 1e18-scale amount does not round through
191
+ * `Number`.
192
+ **/
193
+ function relativeDiffBpsBigint(onchain, offchain) {
194
+ if (onchain === offchain) return 0;
195
+ const diff = onchain > offchain ? onchain - offchain : offchain - onchain;
196
+ const scale = abs(onchain) > abs(offchain) ? abs(onchain) : abs(offchain);
197
+ if (scale === 0n) return 0;
198
+ return Number(diff * 10000n * 1000n / scale) / 1e3;
199
+ }
154
200
  function union(left, right) {
155
201
  return [.../* @__PURE__ */ new Set([...left, ...right])];
156
202
  }
@@ -221,4 +267,4 @@ function abs(value) {
221
267
  return value < 0n ? -value : value;
222
268
  }
223
269
  //#endregion
224
- export { AMOUNT_RELATIVE_EPSILON, BPS_ABSOLUTE_EPSILON, USD_RELATIVE_EPSILON, asFiniteNumber, collapseArrayKeys, countPaths, diffObjects, diffValue, isAmountWithinTolerance, isBpsWithinTolerance, isRecord, isUsdWithinTolerance, toCompareCounts, union, withExpected, withinRelative, withinRelativeBigint };
270
+ export { AMOUNT_RELATIVE_EPSILON, BPS_ABSOLUTE_EPSILON, USD_RELATIVE_EPSILON, asFiniteNumber, collapseArrayKeys, countPaths, diffObjects, diffValue, isAmountWithinTolerance, isBpsWithinTolerance, isRecord, isUsdWithinTolerance, relativeDiffBps, toCompareCounts, union, withExpected, withinRelative, withinRelativeBigint };