@gearbox-protocol/sdk 15.1.0-next.10 → 15.1.0-next.12
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.
- package/dist/cjs/dev/mode-parity/compareOpportunities.js +105 -0
- package/dist/cjs/dev/mode-parity/comparePositions.js +160 -0
- package/dist/cjs/dev/mode-parity/compareRules.js +93 -0
- package/dist/cjs/dev/mode-parity/fieldDiff.js +288 -0
- package/dist/cjs/dev/mode-parity/scriptUtils.js +131 -0
- package/dist/cjs/model/compare.schema.js +33 -0
- package/dist/cjs/model/curators.schema.js +2 -1
- package/dist/cjs/model/index.js +5 -0
- package/dist/cjs/model/opportunities.schema.js +13 -12
- package/dist/cjs/model/positions.schema.js +15 -14
- package/dist/cjs/model/primitives.schema.js +2 -1
- package/dist/cjs/sdk/market/math.js +8 -1
- package/dist/cjs/sdk/pools/PoolService.js +1 -1
- package/dist/cjs/sdk/positions/PositionsService.js +6 -1
- package/dist/esm/common-utils/index.js +3 -3
- package/dist/esm/common-utils/utils/index.js +1 -1
- package/dist/esm/dev/mode-parity/compareOpportunities.js +103 -0
- package/dist/esm/dev/mode-parity/comparePositions.js +158 -0
- package/dist/esm/dev/mode-parity/compareRules.js +91 -0
- package/dist/esm/dev/mode-parity/fieldDiff.js +270 -0
- package/dist/esm/dev/mode-parity/scriptUtils.js +121 -0
- package/dist/esm/model/compare.schema.js +29 -0
- package/dist/esm/model/curators.schema.js +2 -1
- package/dist/esm/model/index.js +2 -1
- package/dist/esm/model/opportunities.schema.js +13 -12
- package/dist/esm/model/positions.schema.js +15 -14
- package/dist/esm/model/primitives.schema.js +2 -1
- package/dist/esm/plugins/apy/ApyPlugin.js +2 -2
- package/dist/esm/sdk/market/math.js +8 -1
- package/dist/esm/sdk/pools/PoolService.js +1 -1
- package/dist/esm/sdk/positions/PositionsService.js +6 -1
- package/dist/types/dev/{compareOpportunities.d.ts → mode-parity/compareOpportunities.d.ts} +18 -66
- package/dist/types/dev/mode-parity/comparePositions.d.ts +149 -0
- package/dist/types/dev/mode-parity/compareRules.d.ts +33 -0
- package/dist/types/dev/mode-parity/fieldDiff.d.ts +209 -0
- package/dist/types/dev/mode-parity/scriptUtils.d.ts +47 -0
- package/dist/types/model/compare.schema.d.ts +47 -0
- package/dist/types/model/index.d.ts +2 -1
- package/dist/types/sdk/market/math.d.ts +2 -0
- package/package.json +1 -1
- package/dist/cjs/dev/compareOpportunities.js +0 -218
- package/dist/esm/dev/compareOpportunities.js +0 -216
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_dev_providers = require("../providers.js");
|
|
3
|
+
let pino = require("pino");
|
|
4
|
+
//#region src/dev/mode-parity/scriptUtils.ts
|
|
5
|
+
const NETWORKS = [
|
|
6
|
+
"Mainnet",
|
|
7
|
+
"Monad",
|
|
8
|
+
"Plasma",
|
|
9
|
+
"Somnia",
|
|
10
|
+
"Etherlink"
|
|
11
|
+
];
|
|
12
|
+
const BACKEND_URL = process.env.BACKEND_URL ?? "https://api.gear-dev.dev";
|
|
13
|
+
const TIMEOUT = 48e4;
|
|
14
|
+
function requireEnv(name) {
|
|
15
|
+
const value = process.env[name];
|
|
16
|
+
if (!value) throw new Error(`${name} is required to reach the chains this script reads`);
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function rpcUrls() {
|
|
20
|
+
const alchemyKey = requireEnv("ALCHEMY_KEY");
|
|
21
|
+
const alchemy = (network) => {
|
|
22
|
+
const url = require_dev_providers.getAlchemyUrl(network, alchemyKey);
|
|
23
|
+
if (!url) throw new Error(`Alchemy serves no URL for ${network}`);
|
|
24
|
+
return url;
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
Mainnet: alchemy("Mainnet"),
|
|
28
|
+
Monad: alchemy("Monad"),
|
|
29
|
+
Plasma: alchemy("Plasma"),
|
|
30
|
+
Somnia: requireEnv("SOMNIA_PROVIDER"),
|
|
31
|
+
Etherlink: requireEnv("ETHERLINK_PROVIDER")
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function createLogger() {
|
|
35
|
+
return (0, pino.pino)({
|
|
36
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
37
|
+
formatters: {
|
|
38
|
+
bindings: () => ({}),
|
|
39
|
+
level: (label) => ({ level: label })
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function errorMessage(error) {
|
|
44
|
+
return error instanceof Error ? error.message : String(error);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Runs `fn` over `items` with at most `concurrency` in flight.
|
|
48
|
+
**/
|
|
49
|
+
async function mapPool(items, concurrency, fn) {
|
|
50
|
+
let next = 0;
|
|
51
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
52
|
+
while (true) {
|
|
53
|
+
const index = next;
|
|
54
|
+
next += 1;
|
|
55
|
+
if (index >= items.length) return;
|
|
56
|
+
await fn(items[index]);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
await Promise.all(workers);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Formats a relative difference in bps as a percent, e.g. 12.5 → `"0.125%"`.
|
|
63
|
+
**/
|
|
64
|
+
function formatBpsAsPercent(bps) {
|
|
65
|
+
if (!Number.isFinite(bps)) return "";
|
|
66
|
+
const percent = bps / 100;
|
|
67
|
+
if (percent === 0) return "0%";
|
|
68
|
+
return `${Math.abs(percent) >= 1 ? trimTrailingZeros(percent.toFixed(3)) : trimTrailingZeros(percent.toPrecision(4))}%`;
|
|
69
|
+
}
|
|
70
|
+
function trimTrailingZeros(value) {
|
|
71
|
+
return value.includes(".") ? value.replace(/\.?0+$/, "") : value;
|
|
72
|
+
}
|
|
73
|
+
function worstColumns(worst) {
|
|
74
|
+
return {
|
|
75
|
+
"max diff": worst ? formatBpsAsPercent(worst.bps) : "",
|
|
76
|
+
"worst entity": worst?.id ?? ""
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Prints the shared membership table, the fields that differed most often,
|
|
81
|
+
* and any chain that failed to answer.
|
|
82
|
+
**/
|
|
83
|
+
function printCompareSummary(noun, report, extraLines = []) {
|
|
84
|
+
const { summary } = report;
|
|
85
|
+
console.log(`\n${noun} from ${report.backendUrl} vs the chain`);
|
|
86
|
+
console.table(summary.byChain.map((chain) => ({
|
|
87
|
+
chain: chain.chainId,
|
|
88
|
+
onchain: chain.onchainRows,
|
|
89
|
+
offchain: chain.offchainRows,
|
|
90
|
+
matched: chain.matched,
|
|
91
|
+
identical: chain.identical,
|
|
92
|
+
clean: chain.clean,
|
|
93
|
+
differing: chain.differing,
|
|
94
|
+
"only onchain": chain.onlyOnchain,
|
|
95
|
+
"only offchain": chain.onlyOffchain
|
|
96
|
+
})));
|
|
97
|
+
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);
|
|
100
|
+
if (unexpected.length) {
|
|
101
|
+
console.log("\nunexpected fields that differed most often:");
|
|
102
|
+
console.table(unexpected.slice(0, 25).map((entry) => ({
|
|
103
|
+
field: entry.path,
|
|
104
|
+
unexpected: entry.unexpected,
|
|
105
|
+
expected: entry.expected,
|
|
106
|
+
kinds: entry.kinds.join(", "),
|
|
107
|
+
...worstColumns(entry.worstUnexpected)
|
|
108
|
+
})));
|
|
109
|
+
}
|
|
110
|
+
if (expected.length) {
|
|
111
|
+
console.log("\nexpected fields (mode-scoped or within tolerance):");
|
|
112
|
+
console.table(expected.slice(0, 25).map((entry) => ({
|
|
113
|
+
field: entry.path,
|
|
114
|
+
rows: entry.expected,
|
|
115
|
+
kinds: entry.kinds.join(", "),
|
|
116
|
+
...worstColumns(entry.worstExpected)
|
|
117
|
+
})));
|
|
118
|
+
}
|
|
119
|
+
for (const chain of [...report.onchainChains, ...report.offchainChains]) if (chain.status === "error") console.log(`chain ${chain.chainId} failed on ${chain.source}:`, chain.error);
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
exports.BACKEND_URL = BACKEND_URL;
|
|
123
|
+
exports.NETWORKS = NETWORKS;
|
|
124
|
+
exports.TIMEOUT = TIMEOUT;
|
|
125
|
+
exports.createLogger = createLogger;
|
|
126
|
+
exports.errorMessage = errorMessage;
|
|
127
|
+
exports.formatBpsAsPercent = formatBpsAsPercent;
|
|
128
|
+
exports.mapPool = mapPool;
|
|
129
|
+
exports.printCompareSummary = printCompareSummary;
|
|
130
|
+
exports.requireEnv = requireEnv;
|
|
131
|
+
exports.rpcUrls = rpcUrls;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/model/compare.schema.ts
|
|
3
|
+
/**
|
|
4
|
+
* Marks a field that only the backend fills.
|
|
5
|
+
**/
|
|
6
|
+
function offchainOnly(schema) {
|
|
7
|
+
return schema.meta({ compare: "offchainOnly" });
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Marks a field that only the chain fills.
|
|
11
|
+
**/
|
|
12
|
+
function onchainOnly(schema) {
|
|
13
|
+
return schema.meta({ compare: "onchainOnly" });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Marks a numeric field whose two sources may drift within {@link kind}.
|
|
17
|
+
**/
|
|
18
|
+
function tolerance(schema, kind) {
|
|
19
|
+
return schema.meta({ compare: { tolerance: kind } });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The compare tag registered on a schema, if any.
|
|
23
|
+
**/
|
|
24
|
+
function compareTagOf(schema) {
|
|
25
|
+
const meta = schema.meta();
|
|
26
|
+
if (!meta || !("compare" in meta)) return;
|
|
27
|
+
return meta.compare;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
exports.compareTagOf = compareTagOf;
|
|
31
|
+
exports.offchainOnly = offchainOnly;
|
|
32
|
+
exports.onchainOnly = onchainOnly;
|
|
33
|
+
exports.tolerance = tolerance;
|
|
@@ -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/curators.schema.ts
|
|
5
6
|
/**
|
|
@@ -31,7 +32,7 @@ const curatorNameSchema = zod_v4.z.enum([
|
|
|
31
32
|
const curatorSchema = zod_v4.z.object({
|
|
32
33
|
address: require_sdk_utils_zod.ZodAddress(),
|
|
33
34
|
name: curatorNameSchema.optional(),
|
|
34
|
-
url: zod_v4.z.string().nullable()
|
|
35
|
+
url: require_model_compare_schema.offchainOnly(zod_v4.z.string().nullable())
|
|
35
36
|
});
|
|
36
37
|
//#endregion
|
|
37
38
|
exports.curatorNameSchema = curatorNameSchema;
|
package/dist/cjs/model/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_model_charts = require("./charts.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_charts_schema = require("./charts.schema.js");
|
|
5
6
|
require("./curators.js");
|
|
@@ -45,6 +46,7 @@ exports.chartRangeSchema = require_model_charts_schema.chartRangeSchema;
|
|
|
45
46
|
exports.chartSeriesSchema = require_model_charts_schema.chartSeriesSchema;
|
|
46
47
|
exports.chartValueSchema = require_model_charts_schema.chartValueSchema;
|
|
47
48
|
exports.chartWindowSchema = require_model_charts_schema.chartWindowSchema;
|
|
49
|
+
exports.compareTagOf = require_model_compare_schema.compareTagOf;
|
|
48
50
|
exports.curatorNameSchema = require_model_curators_schema.curatorNameSchema;
|
|
49
51
|
exports.curatorSchema = require_model_curators_schema.curatorSchema;
|
|
50
52
|
exports.dataSourceSchema = require_model_response_schema.dataSourceSchema;
|
|
@@ -66,6 +68,8 @@ exports.matchesOpportunityFilter = require_model_opportunities.matchesOpportunit
|
|
|
66
68
|
exports.matchesPositionFilter = require_model_positions.matchesPositionFilter;
|
|
67
69
|
exports.noticeKindSchema = require_model_notices_schema.noticeKindSchema;
|
|
68
70
|
exports.noticeSchema = require_model_notices_schema.noticeSchema;
|
|
71
|
+
exports.offchainOnly = require_model_compare_schema.offchainOnly;
|
|
72
|
+
exports.onchainOnly = require_model_compare_schema.onchainOnly;
|
|
69
73
|
exports.opportunityBaseSchema = require_model_opportunities_schema.opportunityBaseSchema;
|
|
70
74
|
exports.opportunityDetailSchema = require_model_opportunities_schema.opportunityDetailSchema;
|
|
71
75
|
exports.opportunityFilterQueryParamsSchema = require_model_opportunities_schema.opportunityFilterQueryParamsSchema;
|
|
@@ -124,4 +128,5 @@ exports.tokenAmountSchema = require_model_primitives_schema.tokenAmountSchema;
|
|
|
124
128
|
exports.tokenRewardsPnLSchema = require_model_positions_schema.tokenRewardsPnLSchema;
|
|
125
129
|
exports.tokenRewardsSchema = require_model_opportunities_schema.tokenRewardsSchema;
|
|
126
130
|
exports.tokenSchema = require_model_primitives_schema.tokenSchema;
|
|
131
|
+
exports.tolerance = require_model_compare_schema.tolerance;
|
|
127
132
|
exports.txCallSchema = require_model_primitives_schema.txCallSchema;
|
|
@@ -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_curators_schema = require("./curators.schema.js");
|
|
5
6
|
const require_model_filters = require("./filters.js");
|
|
@@ -46,9 +47,9 @@ const rewardsSchema = zod_v4.z.discriminatedUnion("kind", [tokenRewardsSchema, p
|
|
|
46
47
|
* {@link ApyBreakdown}
|
|
47
48
|
**/
|
|
48
49
|
const apyBreakdownSchema = zod_v4.z.object({
|
|
49
|
-
totalApy: require_model_primitives_schema.bpsSchema.optional(),
|
|
50
|
-
organicApy: require_model_primitives_schema.bpsSchema,
|
|
51
|
-
rewards: zod_v4.z.array(rewardsSchema).optional()
|
|
50
|
+
totalApy: require_model_compare_schema.offchainOnly(require_model_primitives_schema.bpsSchema).optional(),
|
|
51
|
+
organicApy: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps"),
|
|
52
|
+
rewards: require_model_compare_schema.offchainOnly(zod_v4.z.array(rewardsSchema)).optional()
|
|
52
53
|
});
|
|
53
54
|
/**
|
|
54
55
|
* {@link OpportunityBase}
|
|
@@ -71,9 +72,9 @@ const poolOpportunitySchema = zod_v4.z.object({
|
|
|
71
72
|
...opportunityBaseSchema.shape,
|
|
72
73
|
kind: zod_v4.z.literal("pool"),
|
|
73
74
|
pool: require_sdk_utils_zod.ZodAddress(),
|
|
74
|
-
totalSupply: require_model_primitives_schema.amountSchema,
|
|
75
|
-
availableLiquidity: require_model_primitives_schema.amountSchema,
|
|
76
|
-
utilization: require_model_primitives_schema.bpsSchema,
|
|
75
|
+
totalSupply: require_model_compare_schema.tolerance(require_model_primitives_schema.amountSchema, "amount"),
|
|
76
|
+
availableLiquidity: require_model_compare_schema.tolerance(require_model_primitives_schema.amountSchema, "amount"),
|
|
77
|
+
utilization: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps"),
|
|
77
78
|
supplyApy: apyBreakdownSchema
|
|
78
79
|
});
|
|
79
80
|
/**
|
|
@@ -88,12 +89,12 @@ const strategyOpportunitySchema = zod_v4.z.object({
|
|
|
88
89
|
liquidationPremium: require_model_primitives_schema.bpsSchema,
|
|
89
90
|
liquidationFee: require_model_primitives_schema.bpsSchema,
|
|
90
91
|
expirationDate: require_model_primitives_schema.timestampSchema.nullable(),
|
|
91
|
-
collateralApy: apyBreakdownSchema.optional(),
|
|
92
|
-
maxLeverageApy: apyBreakdownSchema.optional(),
|
|
93
|
-
borrowApy: require_model_primitives_schema.bpsSchema.optional(),
|
|
94
|
-
additionalBorrowApy: require_model_primitives_schema.bpsSchema.optional(),
|
|
95
|
-
totalValue: require_model_primitives_schema.amountSchema.optional(),
|
|
96
|
-
utilization: require_model_primitives_schema.bpsSchema.optional(),
|
|
92
|
+
collateralApy: require_model_compare_schema.offchainOnly(apyBreakdownSchema).optional(),
|
|
93
|
+
maxLeverageApy: require_model_compare_schema.offchainOnly(apyBreakdownSchema).optional(),
|
|
94
|
+
borrowApy: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps").optional(),
|
|
95
|
+
additionalBorrowApy: require_model_compare_schema.tolerance(require_model_primitives_schema.bpsSchema, "bps").optional(),
|
|
96
|
+
totalValue: require_model_compare_schema.offchainOnly(require_model_primitives_schema.amountSchema).optional(),
|
|
97
|
+
utilization: require_model_compare_schema.offchainOnly(require_model_primitives_schema.bpsSchema).optional(),
|
|
97
98
|
maxBorrowAmount: require_model_primitives_schema.amountSchema,
|
|
98
99
|
maxLeverage: require_model_primitives_schema.leverageSchema
|
|
99
100
|
});
|
|
@@ -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}
|
|
@@ -24,8 +24,15 @@ function rayToBps(ray) {
|
|
|
24
24
|
return Number(ray * require_sdk_constants_math.PERCENTAGE_FACTOR / require_sdk_constants_math.RAY);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
+
* Raw 8-decimal USD values below this are compressor leftover dust (typically
|
|
28
|
+
* 1-wei balances on empty accounts) and report as `0`.
|
|
29
|
+
**/
|
|
30
|
+
const USD_DUST_THRESHOLD = 1000n;
|
|
31
|
+
/**
|
|
27
32
|
* Converts a USD value in the oracle's 8-decimal fixed point to a float.
|
|
28
33
|
*
|
|
34
|
+
* Values below {@link USD_DUST_THRESHOLD} report as `0`.
|
|
35
|
+
*
|
|
29
36
|
* @example
|
|
30
37
|
* ```ts
|
|
31
38
|
* // usd: $1500.50 in 8-decimal fixed point
|
|
@@ -33,7 +40,7 @@ function rayToBps(ray) {
|
|
|
33
40
|
* ```
|
|
34
41
|
**/
|
|
35
42
|
function usdToNumber(usd) {
|
|
36
|
-
return Number(usd) / Number(require_sdk_constants_math.PRICE_DECIMALS);
|
|
43
|
+
return usd < USD_DUST_THRESHOLD ? 0 : Number(usd) / Number(require_sdk_constants_math.PRICE_DECIMALS);
|
|
37
44
|
}
|
|
38
45
|
/**
|
|
39
46
|
* Share of capital currently borrowed, in basis points. Returns `0` when there
|
|
@@ -415,7 +415,7 @@ var PoolService = class extends require_sdk_base_SDKConstruct.SDKConstruct {
|
|
|
415
415
|
const { pool } = market.pool;
|
|
416
416
|
return {
|
|
417
417
|
kind: "pool",
|
|
418
|
-
name:
|
|
418
|
+
name: this.sdk.tokensMeta.mustGetToken(pool.address).name,
|
|
419
419
|
chainId: this.chainId,
|
|
420
420
|
pool: pool.address,
|
|
421
421
|
netValue: {
|
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_sdk_utils_AddressMap = require("../utils/AddressMap.js");
|
|
3
3
|
require("../constants/math.js");
|
|
4
4
|
require("../constants/index.js");
|
|
5
|
+
const require_sdk_utils_hex = require("../utils/hex.js");
|
|
5
6
|
require("../utils/index.js");
|
|
6
7
|
const require_sdk_base_SDKConstruct = require("../base/SDKConstruct.js");
|
|
7
8
|
require("../base/index.js");
|
|
@@ -147,7 +148,11 @@ var PositionsService = class extends require_sdk_base_SDKConstruct.SDKConstruct
|
|
|
147
148
|
const { pool } = market.pool;
|
|
148
149
|
const token = this.sdk.tokensMeta.mustGetToken(market.unwrappedUnderlying);
|
|
149
150
|
const totalDebtValue = ca.debt + ca.accruedInterest + ca.accruedFees;
|
|
150
|
-
|
|
151
|
+
let collateral = require_sdk_market_credit_dominantCollateral.dominantCollateral(ca, market);
|
|
152
|
+
if (collateral) {
|
|
153
|
+
const source = this.sdk.withdrawalCompressor?.getWithdrawalSourceToken(collateral);
|
|
154
|
+
if (source) collateral = require_sdk_utils_hex.hexEq(source, market.underlying) ? void 0 : source;
|
|
155
|
+
}
|
|
151
156
|
const snapshot = require_sdk_positions_types.accountSnapshotFromCreditAccountData(ca);
|
|
152
157
|
const borrowRate = this.borrowRate(snapshot);
|
|
153
158
|
const timeToLiquidation = this.timeToLiquidation(snapshot);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { AxiosCache } from "./axios-cache/AxiosCache.js";
|
|
2
|
-
import "./axios-cache/index.js";
|
|
3
1
|
import { BigIntMath } from "../sdk/utils/bigint-math.js";
|
|
4
2
|
import { calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, roundUpQuota } from "./utils/creditAccount/quota-utils.js";
|
|
3
|
+
import { EXECUTE_BYTES_SELECTOR, collectTraces, findCallTo, findCallWithInput, findExecuteBytes, resolveProtocolCall } from "./utils/trace.js";
|
|
4
|
+
import { AxiosCache } from "./axios-cache/AxiosCache.js";
|
|
5
|
+
import "./axios-cache/index.js";
|
|
5
6
|
import { ChartsCreditManagerData } from "./charts/credit-manager.js";
|
|
6
7
|
import { CREDIT_SESSION_ID_BY_STATUS, CREDIT_SESSION_STATUS_BY_ID, CreditSession, CreditSessionFiltered, UserCreditSessionsBuilder } from "./charts/credit-session.js";
|
|
7
8
|
import { ChartsPoolData, UserPoolData } from "./charts/pool.js";
|
|
@@ -91,6 +92,5 @@ import { getCMYouCanEarn } from "./utils/strategies/strategy-info/get-cm-you-can
|
|
|
91
92
|
import { getStrategyInfoCore } from "./utils/strategies/strategy-info/get-strategy-info-core.js";
|
|
92
93
|
import { getStrategyInfo } from "./utils/strategies/strategy-info/get-strategy-info.js";
|
|
93
94
|
import { getStrategyYouCanEarn } from "./utils/strategies/strategy-info/get-strategy-you-can-earn.js";
|
|
94
|
-
import { EXECUTE_BYTES_SELECTOR, collectTraces, findCallTo, findCallWithInput, findExecuteBytes, resolveProtocolCall } from "./utils/trace.js";
|
|
95
95
|
import "./utils/index.js";
|
|
96
96
|
export { AssetUtils, AxiosCache, BONUS_APY_FROM_POINTS, BigIntMath, CREDIT_SESSION_ID_BY_STATUS, CREDIT_SESSION_STATUS_BY_ID, ChartsCreditManagerData, ChartsPoolData, CreditSession, CreditSessionFiltered, EMPTY_ADDRESS, EMPTY_ARRAY, EMPTY_OBJECT, EXECUTE_BYTES_SELECTOR, MIN_HF_LIMITED, PriceUtils, RST_ETH_ADDRESS, TokenData, UserCreditSessionsBuilder, UserPoolData, addAmountInTarget, amountAbcComparator, assetComparator, calcDefaultQuota, calcHealthFactor, calcMaxDebtIncrease, calcMaxLendingDebt, calcOverallAPY, calcQuotaBorrowRate, calcQuotaUpdate, calcRecommendedQuota, calcRelativeBaseBorrowRate, calculateBorrowRateFromUtilization, calculateEarnings, calculateEffectiveBorrowRate, calculateLossCoefficient, calculateMaxLeverageFactor, calculateMaxStrategyDebt, calculateSafeBorrowRate, calculateTotalAPY, calculateTotalPoints, checkBoolean, checkDegenNFT, cmAvailabilityCondition, collectTraces, createLegacyStrategyDataSource, findCallTo, findCallWithInput, findExecuteBytes, getAvailableAndDisabledStrategies, getCMAllowedCollaterals, getCMYouCanEarn, getChainPhantomTokens, getCollateralByDebt, getComplexAPYList, getComplexPointsList, getDefaultAsset, getFactorFromLeverage, getLeverageFromFactor, getListWithAmountInTarget, getNativeTokenAddress, getPointsInfo, getPointsRates, getRateWithFee, getRecommendedDebt, getRecommendedLeverageFactor, getReleasedStrategiesList, getReleasedStrategiesListCore, getSafeBaseBorrowRate, getSingleQuotaBorrowRate, getStrategyCreditManagers, getStrategyCreditManagersList, getStrategyCreditManagersListCore, getStrategyInfo, getStrategyInfoCore, getStrategyMaxAPY, getStrategyPoints, getStrategyYouCanEarn, getTimeToLiquidation, getWalletBalancesAllowedOnCM, isActivatedToken, isApyWithPointsException, isCollateralToken, isCreditManagerUsable, isForbiddenToken, isObtainableToken, isStrategyCMDisabled, isStrategyDisabled, isStrategyEligible, isStrategyReleased, isUsableToken, isValidExtraCollateralToken, isZeroBalance, liquidationPrice, maxAPYFormula, maxLeverage, maxLeverageThreshold, resolveProtocolCall, roundUpQuota, sortAssets, sortBalances, sortStrategyCMsByAvailability, tokensAbcComparator, validateBalance, validateBalances, validateCreditManager, validateHF, validateOpenAccount, validateOpenAccountPoolQuotaStatus, validateOpenAccountPoolStatus, validateQuota, validateTokenToObtain, wrapTokenAddress };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BigIntMath } from "../../sdk/utils/bigint-math.js";
|
|
2
2
|
import { calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, roundUpQuota } from "./creditAccount/quota-utils.js";
|
|
3
|
+
import { EXECUTE_BYTES_SELECTOR, collectTraces, findCallTo, findCallWithInput, findExecuteBytes, resolveProtocolCall } from "./trace.js";
|
|
3
4
|
import { BONUS_APY_FROM_POINTS } from "./apy/bonus-apy-from-points.js";
|
|
4
5
|
import { calculateBorrowRateFromUtilization } from "./apy/calculate-borrow-rate-from-utilization.js";
|
|
5
6
|
import { PriceUtils } from "./price-math.js";
|
|
@@ -86,6 +87,5 @@ import { getStrategyInfoCore } from "./strategies/strategy-info/get-strategy-inf
|
|
|
86
87
|
import { getStrategyInfo } from "./strategies/strategy-info/get-strategy-info.js";
|
|
87
88
|
import { getStrategyYouCanEarn } from "./strategies/strategy-info/get-strategy-you-can-earn.js";
|
|
88
89
|
import "./strategies/index.js";
|
|
89
|
-
import { EXECUTE_BYTES_SELECTOR, collectTraces, findCallTo, findCallWithInput, findExecuteBytes, resolveProtocolCall } from "./trace.js";
|
|
90
90
|
import "./validation/index.js";
|
|
91
91
|
export { AssetUtils, BONUS_APY_FROM_POINTS, BigIntMath, EMPTY_ADDRESS, EMPTY_ARRAY, EMPTY_OBJECT, EXECUTE_BYTES_SELECTOR, MIN_HF_LIMITED, PriceUtils, RST_ETH_ADDRESS, addAmountInTarget, amountAbcComparator, assetComparator, calcDefaultQuota, calcHealthFactor, calcMaxDebtIncrease, calcMaxLendingDebt, calcOverallAPY, calcQuotaBorrowRate, calcQuotaUpdate, calcRecommendedQuota, calcRelativeBaseBorrowRate, calculateBorrowRateFromUtilization, calculateEarnings, calculateEffectiveBorrowRate, calculateLossCoefficient, calculateMaxLeverageFactor, calculateMaxStrategyDebt, calculateSafeBorrowRate, calculateTotalAPY, calculateTotalPoints, checkBoolean, checkDegenNFT, cmAvailabilityCondition, collectTraces, createLegacyStrategyDataSource, findCallTo, findCallWithInput, findExecuteBytes, getAvailableAndDisabledStrategies, getCMAllowedCollaterals, getCMYouCanEarn, getChainPhantomTokens, getCollateralByDebt, getComplexAPYList, getComplexPointsList, getDefaultAsset, getFactorFromLeverage, getLeverageFromFactor, getListWithAmountInTarget, getNativeTokenAddress, getPointsInfo, getPointsRates, getRateWithFee, getRecommendedDebt, getRecommendedLeverageFactor, getReleasedStrategiesList, getReleasedStrategiesListCore, getSafeBaseBorrowRate, getSingleQuotaBorrowRate, getStrategyCreditManagers, getStrategyCreditManagersList, getStrategyCreditManagersListCore, getStrategyInfo, getStrategyInfoCore, getStrategyMaxAPY, getStrategyPoints, getStrategyYouCanEarn, getTimeToLiquidation, getWalletBalancesAllowedOnCM, isActivatedToken, isApyWithPointsException, isCollateralToken, isCreditManagerUsable, isForbiddenToken, isObtainableToken, isStrategyCMDisabled, isStrategyDisabled, isStrategyEligible, isStrategyReleased, isUsableToken, isValidExtraCollateralToken, isZeroBalance, liquidationPrice, maxAPYFormula, maxLeverage, maxLeverageThreshold, resolveProtocolCall, roundUpQuota, sortAssets, sortBalances, sortStrategyCMsByAvailability, tokensAbcComparator, validateBalance, validateBalances, validateCreditManager, validateHF, validateOpenAccount, validateOpenAccountPoolQuotaStatus, validateOpenAccountPoolStatus, validateQuota, validateTokenToObtain, wrapTokenAddress };
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Matches two opportunity listings by {@link opportunityId} and reports every
|
|
13
|
+
* field the two sources disagree on.
|
|
14
|
+
*
|
|
15
|
+
* Nothing is filtered out. A field only the backend can fill, or a USD value
|
|
16
|
+
* that drifted within snapshot-lag noise, is still reported — tagged
|
|
17
|
+
* {@link FieldDiff.expected} so that {@link CompareCounts.clean} can ignore it
|
|
18
|
+
* while {@link CompareCounts.identical} stays strict.
|
|
19
|
+
**/
|
|
20
|
+
function compareOpportunities(input) {
|
|
21
|
+
const onchainRows = indexById(input.onchain.data);
|
|
22
|
+
const offchainRows = indexById(input.offchain.data);
|
|
23
|
+
const onlyOnchain = [];
|
|
24
|
+
const onlyOffchain = [];
|
|
25
|
+
const matched = [];
|
|
26
|
+
for (const [id, row] of onchainRows) {
|
|
27
|
+
const counterpart = offchainRows.get(id);
|
|
28
|
+
if (!counterpart) {
|
|
29
|
+
onlyOnchain.push(toRef(row));
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const diffs = diffOpportunity(row, counterpart);
|
|
33
|
+
matched.push({
|
|
34
|
+
id,
|
|
35
|
+
kind: row.kind,
|
|
36
|
+
chainId: row.chainId,
|
|
37
|
+
onchainName: row.name,
|
|
38
|
+
offchainName: counterpart.name,
|
|
39
|
+
identical: diffs.length === 0,
|
|
40
|
+
clean: diffs.every((diff) => diff.expected),
|
|
41
|
+
diffs
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
for (const [id, row] of offchainRows) if (!onchainRows.has(id)) onlyOffchain.push(toRef(row));
|
|
45
|
+
byId(onlyOnchain);
|
|
46
|
+
byId(onlyOffchain);
|
|
47
|
+
matched.sort((a, b) => a.id.localeCompare(b.id));
|
|
48
|
+
return {
|
|
49
|
+
generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
50
|
+
backendUrl: input.backendUrl,
|
|
51
|
+
networks: [...input.networks],
|
|
52
|
+
onchainChains: input.onchain.meta.chains,
|
|
53
|
+
offchainChains: input.offchain.meta.chains,
|
|
54
|
+
summary: summarize(input.onchain.data, input.offchain.data, onlyOnchain, onlyOffchain, matched),
|
|
55
|
+
onlyOnchain,
|
|
56
|
+
onlyOffchain,
|
|
57
|
+
matched
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function indexById(rows) {
|
|
61
|
+
return new Map(rows.map((row) => [opportunityId(row), row]));
|
|
62
|
+
}
|
|
63
|
+
function byId(refs) {
|
|
64
|
+
refs.sort((a, b) => a.id.localeCompare(b.id));
|
|
65
|
+
}
|
|
66
|
+
function toRef(row) {
|
|
67
|
+
const base = {
|
|
68
|
+
id: opportunityId(row),
|
|
69
|
+
kind: row.kind,
|
|
70
|
+
chainId: row.chainId,
|
|
71
|
+
name: row.name
|
|
72
|
+
};
|
|
73
|
+
return row.kind === "pool" ? {
|
|
74
|
+
...base,
|
|
75
|
+
pool: row.pool
|
|
76
|
+
} : {
|
|
77
|
+
...base,
|
|
78
|
+
creditManager: row.creditManager,
|
|
79
|
+
targetCollateral: row.targetCollateral.address
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Every field two versions of one opportunity disagree on.
|
|
84
|
+
**/
|
|
85
|
+
function diffOpportunity(onchain, offchain) {
|
|
86
|
+
return diffObjects(onchain, offchain).map((diff) => tagDiff(diff, onchain.kind));
|
|
87
|
+
}
|
|
88
|
+
function summarize(onchain, offchain, onlyOnchain, onlyOffchain, matched) {
|
|
89
|
+
const byChain = union(onchain.map((row) => String(row.chainId)), offchain.map((row) => String(row.chainId))).map((chainId) => ({
|
|
90
|
+
chainId: Number(chainId),
|
|
91
|
+
...toCompareCounts(onchain.filter((row) => String(row.chainId) === chainId).length, offchain.filter((row) => String(row.chainId) === chainId).length, onlyOnchain.filter((ref) => String(ref.chainId) === chainId).length, onlyOffchain.filter((ref) => String(ref.chainId) === chainId).length, matched.filter((match) => String(match.chainId) === chainId))
|
|
92
|
+
})).sort((a, b) => a.chainId - b.chainId);
|
|
93
|
+
return {
|
|
94
|
+
...toCompareCounts(onchain.length, offchain.length, onlyOnchain.length, onlyOffchain.length, matched),
|
|
95
|
+
byChain,
|
|
96
|
+
diffsByPath: countPaths(matched.flatMap((match) => match.diffs.map((diff) => ({
|
|
97
|
+
id: match.id,
|
|
98
|
+
diff
|
|
99
|
+
}))))
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { compareOpportunities, diffOpportunity };
|