@gearbox-protocol/sdk 15.1.0-next.11 → 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/{comparePositions.js → mode-parity/comparePositions.js} +23 -46
- package/dist/cjs/dev/mode-parity/compareRules.js +93 -0
- package/dist/cjs/dev/{fieldDiff.js → mode-parity/fieldDiff.js} +49 -2
- 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/esm/common-utils/index.js +3 -3
- package/dist/esm/common-utils/utils/index.js +1 -1
- package/dist/esm/dev/AccountOpener.js +1 -1
- package/dist/esm/dev/{compareOpportunities.js → mode-parity/compareOpportunities.js} +14 -46
- package/dist/esm/dev/{comparePositions.js → mode-parity/comparePositions.js} +16 -39
- package/dist/esm/dev/mode-parity/compareRules.js +91 -0
- package/dist/esm/dev/{fieldDiff.js → mode-parity/fieldDiff.js} +49 -3
- package/dist/esm/dev/mode-parity/scriptUtils.js +121 -0
- package/dist/esm/dev/withdrawalUtils.js +1 -1
- 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/adapters/contracts/ERC4626AdapterContract.js +1 -1
- package/dist/esm/plugins/apy/ApyPlugin.js +2 -2
- package/dist/esm/preview/simulate/simulatePoolOperation.js +1 -1
- package/dist/esm/preview/trace/extractTransfers.js +1 -1
- package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +2 -2
- package/dist/esm/sdk/accounts/liquidations/LiquidationsService.js +1 -1
- package/dist/esm/sdk/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
- package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
- package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
- package/dist/esm/sdk/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
- package/dist/esm/sdk/base/TokensMeta.js +3 -3
- package/dist/esm/sdk/chain/detectNetwork.js +1 -1
- package/dist/esm/sdk/core/createAddressProvider.js +1 -1
- package/dist/esm/sdk/market/credit/CreditFacadeV310BaseContract.js +1 -1
- package/dist/esm/sdk/market/pool/PoolV310Contract.js +1 -1
- package/dist/esm/sdk/market/zapper/IETHZapperContract.js +1 -1
- package/dist/esm/sdk/market/zapper/ZapperContract.js +1 -1
- package/dist/esm/sdk/pools/PoolService.js +1 -1
- package/dist/esm/sdk/utils/viem/simulateWithPriceUpdates.js +1 -1
- package/dist/types/dev/{compareOpportunities.d.ts → mode-parity/compareOpportunities.d.ts} +5 -5
- package/dist/types/dev/{comparePositions.d.ts → mode-parity/comparePositions.d.ts} +5 -5
- package/dist/types/dev/mode-parity/compareRules.d.ts +33 -0
- package/dist/types/dev/{fieldDiff.d.ts → mode-parity/fieldDiff.d.ts} +47 -5
- 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/package.json +1 -1
- package/dist/cjs/dev/compareOpportunities.js +0 -137
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_model_opportunities = require("../../model/opportunities.js");
|
|
3
|
+
const require_model_opportunities_schema = require("../../model/opportunities.schema.js");
|
|
4
|
+
require("../../model/index.js");
|
|
5
|
+
const require_dev_mode_parity_fieldDiff = require("./fieldDiff.js");
|
|
6
|
+
const require_dev_mode_parity_compareRules = require("./compareRules.js");
|
|
7
|
+
//#region src/dev/mode-parity/compareOpportunities.ts
|
|
8
|
+
const tagDiff = require_dev_mode_parity_compareRules.makeTagDiff({
|
|
9
|
+
pool: require_dev_mode_parity_compareRules.compileCompareRules(require_model_opportunities_schema.poolOpportunitySchema),
|
|
10
|
+
strategy: require_dev_mode_parity_compareRules.compileCompareRules(require_model_opportunities_schema.strategyOpportunitySchema)
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Matches two opportunity listings by {@link opportunityId} and reports every
|
|
14
|
+
* field the two sources disagree on.
|
|
15
|
+
*
|
|
16
|
+
* Nothing is filtered out. A field only the backend can fill, or a USD value
|
|
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.
|
|
20
|
+
**/
|
|
21
|
+
function compareOpportunities(input) {
|
|
22
|
+
const onchainRows = indexById(input.onchain.data);
|
|
23
|
+
const offchainRows = indexById(input.offchain.data);
|
|
24
|
+
const onlyOnchain = [];
|
|
25
|
+
const onlyOffchain = [];
|
|
26
|
+
const matched = [];
|
|
27
|
+
for (const [id, row] of onchainRows) {
|
|
28
|
+
const counterpart = offchainRows.get(id);
|
|
29
|
+
if (!counterpart) {
|
|
30
|
+
onlyOnchain.push(toRef(row));
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const diffs = diffOpportunity(row, counterpart);
|
|
34
|
+
matched.push({
|
|
35
|
+
id,
|
|
36
|
+
kind: row.kind,
|
|
37
|
+
chainId: row.chainId,
|
|
38
|
+
onchainName: row.name,
|
|
39
|
+
offchainName: counterpart.name,
|
|
40
|
+
identical: diffs.length === 0,
|
|
41
|
+
clean: diffs.every((diff) => diff.expected),
|
|
42
|
+
diffs
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
for (const [id, row] of offchainRows) if (!onchainRows.has(id)) onlyOffchain.push(toRef(row));
|
|
46
|
+
byId(onlyOnchain);
|
|
47
|
+
byId(onlyOffchain);
|
|
48
|
+
matched.sort((a, b) => a.id.localeCompare(b.id));
|
|
49
|
+
return {
|
|
50
|
+
generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
51
|
+
backendUrl: input.backendUrl,
|
|
52
|
+
networks: [...input.networks],
|
|
53
|
+
onchainChains: input.onchain.meta.chains,
|
|
54
|
+
offchainChains: input.offchain.meta.chains,
|
|
55
|
+
summary: summarize(input.onchain.data, input.offchain.data, onlyOnchain, onlyOffchain, matched),
|
|
56
|
+
onlyOnchain,
|
|
57
|
+
onlyOffchain,
|
|
58
|
+
matched
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function indexById(rows) {
|
|
62
|
+
return new Map(rows.map((row) => [require_model_opportunities.opportunityId(row), row]));
|
|
63
|
+
}
|
|
64
|
+
function byId(refs) {
|
|
65
|
+
refs.sort((a, b) => a.id.localeCompare(b.id));
|
|
66
|
+
}
|
|
67
|
+
function toRef(row) {
|
|
68
|
+
const base = {
|
|
69
|
+
id: require_model_opportunities.opportunityId(row),
|
|
70
|
+
kind: row.kind,
|
|
71
|
+
chainId: row.chainId,
|
|
72
|
+
name: row.name
|
|
73
|
+
};
|
|
74
|
+
return row.kind === "pool" ? {
|
|
75
|
+
...base,
|
|
76
|
+
pool: row.pool
|
|
77
|
+
} : {
|
|
78
|
+
...base,
|
|
79
|
+
creditManager: row.creditManager,
|
|
80
|
+
targetCollateral: row.targetCollateral.address
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Every field two versions of one opportunity disagree on.
|
|
85
|
+
**/
|
|
86
|
+
function diffOpportunity(onchain, offchain) {
|
|
87
|
+
return require_dev_mode_parity_fieldDiff.diffObjects(onchain, offchain).map((diff) => tagDiff(diff, onchain.kind));
|
|
88
|
+
}
|
|
89
|
+
function summarize(onchain, offchain, onlyOnchain, onlyOffchain, matched) {
|
|
90
|
+
const byChain = require_dev_mode_parity_fieldDiff.union(onchain.map((row) => String(row.chainId)), offchain.map((row) => String(row.chainId))).map((chainId) => ({
|
|
91
|
+
chainId: Number(chainId),
|
|
92
|
+
...require_dev_mode_parity_fieldDiff.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))
|
|
93
|
+
})).sort((a, b) => a.chainId - b.chainId);
|
|
94
|
+
return {
|
|
95
|
+
...require_dev_mode_parity_fieldDiff.toCompareCounts(onchain.length, offchain.length, onlyOnchain.length, onlyOffchain.length, matched),
|
|
96
|
+
byChain,
|
|
97
|
+
diffsByPath: require_dev_mode_parity_fieldDiff.countPaths(matched.flatMap((match) => match.diffs.map((diff) => ({
|
|
98
|
+
id: match.id,
|
|
99
|
+
diff
|
|
100
|
+
}))))
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
exports.compareOpportunities = compareOpportunities;
|
|
105
|
+
exports.diffOpportunity = diffOpportunity;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
require("
|
|
4
|
-
const
|
|
5
|
-
|
|
2
|
+
const require_model_liquidations_schema = require("../../model/liquidations.schema.js");
|
|
3
|
+
const require_model_positions = require("../../model/positions.js");
|
|
4
|
+
const require_model_positions_schema = require("../../model/positions.schema.js");
|
|
5
|
+
require("../../model/index.js");
|
|
6
|
+
const require_dev_mode_parity_fieldDiff = require("./fieldDiff.js");
|
|
7
|
+
const require_dev_mode_parity_compareRules = require("./compareRules.js");
|
|
8
|
+
//#region src/dev/mode-parity/comparePositions.ts
|
|
9
|
+
const tagDiff = require_dev_mode_parity_compareRules.makeTagDiff({
|
|
10
|
+
pool: require_dev_mode_parity_compareRules.compileCompareRules(require_model_positions_schema.poolPositionSchema),
|
|
11
|
+
strategy: require_dev_mode_parity_compareRules.compileCompareRules(require_model_positions_schema.strategyPositionSchema),
|
|
12
|
+
liquidation: require_dev_mode_parity_compareRules.compileCompareRules(require_model_liquidations_schema.liquidationPositionSchema)
|
|
13
|
+
});
|
|
6
14
|
/**
|
|
7
15
|
* Matches two position listings per wallet by {@link positionId} and reports
|
|
8
16
|
* every field the two sources disagree on.
|
|
@@ -36,7 +44,7 @@ function comparePositions(input) {
|
|
|
36
44
|
* Every field two versions of one position disagree on.
|
|
37
45
|
**/
|
|
38
46
|
function diffPosition(onchain, offchain) {
|
|
39
|
-
return
|
|
47
|
+
return require_dev_mode_parity_fieldDiff.diffObjects(onchain, offchain, { keyOf: keyOfPositionArray }).map((diff) => tagDiff(diff, onchain.kind));
|
|
40
48
|
}
|
|
41
49
|
function compareWallet(input) {
|
|
42
50
|
const onchainRows = indexById(input.onchain.data);
|
|
@@ -101,43 +109,9 @@ function toRef(row) {
|
|
|
101
109
|
* address. Rewards-points programs already identify themselves by `id`.
|
|
102
110
|
**/
|
|
103
111
|
function keyOfPositionArray(path, value) {
|
|
104
|
-
if (path !== "collaterals" || !
|
|
112
|
+
if (path !== "collaterals" || !require_dev_mode_parity_fieldDiff.isRecord(value) || !require_dev_mode_parity_fieldDiff.isRecord(value.collateral)) return;
|
|
105
113
|
const token = value.collateral.token;
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
const BPS_RATE_PATHS = /* @__PURE__ */ new Set([
|
|
109
|
-
"borrowApy",
|
|
110
|
-
"apy.organicApy",
|
|
111
|
-
"healthFactor"
|
|
112
|
-
]);
|
|
113
|
-
/**
|
|
114
|
-
* Amount fields whose bigint `value` moves with interest accrual between the
|
|
115
|
-
* backend's last sync and the current block.
|
|
116
|
-
**/
|
|
117
|
-
function isLagAmountPath(path) {
|
|
118
|
-
return path === "totalDebt.value" || path === "totalValue.value" || path === "netValue.value" || path.endsWith(".collateral.value") || path.endsWith(".quota.value");
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Fields documented `@mode offchain` or `@mode onchain` in the model.
|
|
122
|
-
**/
|
|
123
|
-
function isModeScoped(path, kind) {
|
|
124
|
-
if (path === "pnl" || path.startsWith("pnl.")) return true;
|
|
125
|
-
if (kind === "pool") return path === "apy.totalApy" || path.startsWith("apy.rewards");
|
|
126
|
-
if (kind === "strategy") return path === "netApy" || path.startsWith("netApy.") || path === "borrowRate" || path.startsWith("borrowRate.") || path === "timeToLiquidation" || path === "liquidationPrice";
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
function tagDiff(diff, kind) {
|
|
130
|
-
if (isModeScoped(diff.path, kind)) return require_dev_fieldDiff.withExpected(diff, "mode-scoped");
|
|
131
|
-
if (withinTolerance(diff)) return require_dev_fieldDiff.withExpected(diff, "tolerance");
|
|
132
|
-
return diff;
|
|
133
|
-
}
|
|
134
|
-
function withinTolerance(diff) {
|
|
135
|
-
if (diff.kind === "usd") return require_dev_fieldDiff.isUsdWithinTolerance(diff.onchain, diff.offchain);
|
|
136
|
-
if (diff.kind !== "numeric") return false;
|
|
137
|
-
if (diff.path === "leverage") return require_dev_fieldDiff.withinRelative(require_dev_fieldDiff.asFiniteNumber(diff.onchain), require_dev_fieldDiff.asFiniteNumber(diff.offchain), require_dev_fieldDiff.USD_RELATIVE_EPSILON);
|
|
138
|
-
if (BPS_RATE_PATHS.has(diff.path)) return require_dev_fieldDiff.isBpsWithinTolerance(diff.onchain, diff.offchain);
|
|
139
|
-
if (isLagAmountPath(diff.path)) return require_dev_fieldDiff.isAmountWithinTolerance(diff.onchain, diff.offchain);
|
|
140
|
-
return false;
|
|
114
|
+
return require_dev_mode_parity_fieldDiff.isRecord(token) && typeof token.address === "string" ? token.address : void 0;
|
|
141
115
|
}
|
|
142
116
|
function summarize(inputs, wallets, failed) {
|
|
143
117
|
const compared = wallets.filter((wallet) => !wallet.error);
|
|
@@ -146,24 +120,27 @@ function summarize(inputs, wallets, failed) {
|
|
|
146
120
|
const onlyOnchain = compared.flatMap((wallet) => wallet.onlyOnchain);
|
|
147
121
|
const onlyOffchain = compared.flatMap((wallet) => wallet.onlyOffchain);
|
|
148
122
|
const matched = compared.flatMap((wallet) => wallet.matched);
|
|
149
|
-
const byChain =
|
|
123
|
+
const byChain = require_dev_mode_parity_fieldDiff.union(allOnchain.map((row) => String(row.chainId)), allOffchain.map((row) => String(row.chainId))).map((chainId) => ({
|
|
150
124
|
chainId: Number(chainId),
|
|
151
|
-
...
|
|
125
|
+
...require_dev_mode_parity_fieldDiff.toCompareCounts(allOnchain.filter((row) => String(row.chainId) === chainId).length, allOffchain.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))
|
|
152
126
|
})).sort((a, b) => a.chainId - b.chainId);
|
|
153
127
|
const byWallet = wallets.map((wallet) => ({
|
|
154
128
|
wallet: wallet.wallet,
|
|
155
129
|
...wallet.error ? { error: wallet.error } : {},
|
|
156
|
-
...
|
|
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)
|
|
157
131
|
}));
|
|
158
132
|
const walletsClean = compared.filter((wallet) => wallet.onlyOnchain.length === 0 && wallet.onlyOffchain.length === 0 && wallet.matched.every((match) => match.clean)).length;
|
|
159
133
|
return {
|
|
160
|
-
...
|
|
134
|
+
...require_dev_mode_parity_fieldDiff.toCompareCounts(allOnchain.length, allOffchain.length, onlyOnchain.length, onlyOffchain.length, matched),
|
|
161
135
|
wallets: wallets.length,
|
|
162
136
|
walletsClean,
|
|
163
137
|
walletsFailed: failed,
|
|
164
138
|
byChain,
|
|
165
139
|
byWallet,
|
|
166
|
-
diffsByPath:
|
|
140
|
+
diffsByPath: require_dev_mode_parity_fieldDiff.countPaths(matched.flatMap((match) => match.diffs.map((diff) => ({
|
|
141
|
+
id: match.id,
|
|
142
|
+
diff
|
|
143
|
+
}))))
|
|
167
144
|
};
|
|
168
145
|
}
|
|
169
146
|
/**
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_model_compare_schema = require("../../model/compare.schema.js");
|
|
3
|
+
const require_dev_mode_parity_fieldDiff = require("./fieldDiff.js");
|
|
4
|
+
//#region src/dev/mode-parity/compareRules.ts
|
|
5
|
+
/**
|
|
6
|
+
* Walks a schema and records every field that carries compare metadata.
|
|
7
|
+
*
|
|
8
|
+
* `"amount"` on an object (an Amount / TokenAmount) is stored
|
|
9
|
+
* at `<path>.value`; every other tag is stored at the field's own path.
|
|
10
|
+
**/
|
|
11
|
+
function compileCompareRules(schema) {
|
|
12
|
+
const rules = /* @__PURE__ */ new Map();
|
|
13
|
+
walk(schema, "", /* @__PURE__ */ new Set(), rules);
|
|
14
|
+
return rules;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tags diffs using the rules compiled for each row kind.
|
|
18
|
+
*
|
|
19
|
+
* Mode tags match the path or anything nested under it. Tolerance tags match
|
|
20
|
+
* the path exactly and dispatch on {@link CompareTolerance}.
|
|
21
|
+
**/
|
|
22
|
+
function makeTagDiff(rulesByKind) {
|
|
23
|
+
return (diff, kind) => {
|
|
24
|
+
const rules = rulesByKind[kind];
|
|
25
|
+
if (!rules) return diff;
|
|
26
|
+
const path = require_dev_mode_parity_fieldDiff.collapseArrayKeys(diff.path);
|
|
27
|
+
if (isModeScoped(path, rules)) return require_dev_mode_parity_fieldDiff.withExpected(diff, "mode-scoped");
|
|
28
|
+
const tag = rules.get(path);
|
|
29
|
+
if (tag && typeof tag === "object" && withinTolerance(tag.tolerance, diff)) return require_dev_mode_parity_fieldDiff.withExpected(diff, "tolerance");
|
|
30
|
+
return diff;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function isModeScoped(path, rules) {
|
|
34
|
+
for (const [rulePath, tag] of rules) {
|
|
35
|
+
if (tag !== "offchainOnly" && tag !== "onchainOnly") continue;
|
|
36
|
+
if (path === rulePath || path.startsWith(`${rulePath}.`) || path.startsWith(`${rulePath}[`)) return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function withinTolerance(kind, diff) {
|
|
41
|
+
switch (kind) {
|
|
42
|
+
case "usd": return require_dev_mode_parity_fieldDiff.isUsdWithinTolerance(diff.onchain, diff.offchain);
|
|
43
|
+
case "bps": return require_dev_mode_parity_fieldDiff.isBpsWithinTolerance(diff.onchain, diff.offchain);
|
|
44
|
+
case "amount": return require_dev_mode_parity_fieldDiff.isAmountWithinTolerance(diff.onchain, diff.offchain);
|
|
45
|
+
case "float": return require_dev_mode_parity_fieldDiff.withinRelative(require_dev_mode_parity_fieldDiff.asFiniteNumber(diff.onchain), require_dev_mode_parity_fieldDiff.asFiniteNumber(diff.offchain), require_dev_mode_parity_fieldDiff.USD_RELATIVE_EPSILON);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function walk(schema, path, seen, rules) {
|
|
49
|
+
if (seen.has(schema)) return;
|
|
50
|
+
const nextSeen = new Set(seen);
|
|
51
|
+
nextSeen.add(schema);
|
|
52
|
+
const tag = require_model_compare_schema.compareTagOf(schema);
|
|
53
|
+
if (tag) record(path, tag, schema, rules);
|
|
54
|
+
const def = schema.def;
|
|
55
|
+
switch (def.type) {
|
|
56
|
+
case "optional":
|
|
57
|
+
case "nullable":
|
|
58
|
+
case "default":
|
|
59
|
+
case "prefault":
|
|
60
|
+
case "readonly":
|
|
61
|
+
if (def.innerType) walk(def.innerType, path, nextSeen, rules);
|
|
62
|
+
return;
|
|
63
|
+
case "array":
|
|
64
|
+
if (def.element) walk(def.element, `${path}[]`, nextSeen, rules);
|
|
65
|
+
return;
|
|
66
|
+
case "object":
|
|
67
|
+
if (def.shape) for (const [key, field] of Object.entries(def.shape)) walk(field, join(path, key), nextSeen, rules);
|
|
68
|
+
return;
|
|
69
|
+
case "union":
|
|
70
|
+
for (const option of def.options ?? []) walk(option, path, nextSeen, rules);
|
|
71
|
+
return;
|
|
72
|
+
case "pipe":
|
|
73
|
+
if (def.out) walk(def.out, path, nextSeen, rules);
|
|
74
|
+
return;
|
|
75
|
+
case "lazy":
|
|
76
|
+
if (def.getter) walk(def.getter(), path, nextSeen, rules);
|
|
77
|
+
return;
|
|
78
|
+
default: return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function record(path, tag, schema, rules) {
|
|
82
|
+
if (typeof tag === "object" && tag.tolerance === "amount" && schema.def.type === "object") {
|
|
83
|
+
rules.set(join(path, "value"), tag);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
rules.set(path, tag);
|
|
87
|
+
}
|
|
88
|
+
function join(path, key) {
|
|
89
|
+
return path ? `${path}.${key}` : key;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
exports.compileCompareRules = compileCompareRules;
|
|
93
|
+
exports.makeTagDiff = makeTagDiff;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region src/dev/fieldDiff.ts
|
|
2
|
+
//#region src/dev/mode-parity/fieldDiff.ts
|
|
3
3
|
/**
|
|
4
4
|
* Relative drift allowed on {@link Amount.valueUsd} before a USD float is a
|
|
5
5
|
* real disagreement: 0.1%.
|
|
@@ -70,7 +70,7 @@ function withExpected(diff, reason) {
|
|
|
70
70
|
**/
|
|
71
71
|
function countPaths(diffs) {
|
|
72
72
|
const counts = /* @__PURE__ */ new Map();
|
|
73
|
-
for (const diff of diffs) {
|
|
73
|
+
for (const { id, diff } of diffs) {
|
|
74
74
|
const path = collapseArrayKeys(diff.path);
|
|
75
75
|
const entry = counts.get(path) ?? {
|
|
76
76
|
path,
|
|
@@ -83,10 +83,31 @@ function countPaths(diffs) {
|
|
|
83
83
|
if (diff.expected) entry.expected += 1;
|
|
84
84
|
else entry.unexpected += 1;
|
|
85
85
|
if (!entry.kinds.includes(diff.kind)) entry.kinds.push(diff.kind);
|
|
86
|
+
recordWorst(entry, id, diff);
|
|
86
87
|
counts.set(path, entry);
|
|
87
88
|
}
|
|
88
89
|
return [...counts.values()].sort((a, b) => b.unexpected - a.unexpected || b.count - a.count || a.path.localeCompare(b.path));
|
|
89
90
|
}
|
|
91
|
+
function recordWorst(entry, id, diff) {
|
|
92
|
+
const bps = relativeDiffBps(diff.onchain, diff.offchain);
|
|
93
|
+
if (bps === void 0) return;
|
|
94
|
+
const worst = {
|
|
95
|
+
id,
|
|
96
|
+
path: diff.path,
|
|
97
|
+
bps,
|
|
98
|
+
onchain: diff.onchain,
|
|
99
|
+
offchain: diff.offchain
|
|
100
|
+
};
|
|
101
|
+
if (diff.expected) {
|
|
102
|
+
if (isWorse(worst, entry.worstExpected)) entry.worstExpected = worst;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (isWorse(worst, entry.worstUnexpected)) entry.worstUnexpected = worst;
|
|
106
|
+
}
|
|
107
|
+
function isWorse(candidate, current) {
|
|
108
|
+
if (!current) return true;
|
|
109
|
+
return candidate.bps > current.bps || candidate.bps === current.bps && candidate.id.localeCompare(current.id) < 0;
|
|
110
|
+
}
|
|
90
111
|
/**
|
|
91
112
|
* Collapse `collateralTokens[0xa0b8...].symbol` to `collateralTokens[].symbol`.
|
|
92
113
|
**/
|
|
@@ -152,6 +173,31 @@ function withinRelativeBigint(onchain, offchain, epsilon) {
|
|
|
152
173
|
function asFiniteNumber(value) {
|
|
153
174
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
154
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Relative difference of two numbers or bigints, in bps:
|
|
178
|
+
* `|a − b| / max(|a|, |b|) * 10_000`. `undefined` when the values are not
|
|
179
|
+
* a comparable pair of finite numbers or of bigints.
|
|
180
|
+
**/
|
|
181
|
+
function relativeDiffBps(onchain, offchain) {
|
|
182
|
+
if (typeof onchain === "bigint" && typeof offchain === "bigint") return relativeDiffBpsBigint(onchain, offchain);
|
|
183
|
+
const left = asFiniteNumber(onchain);
|
|
184
|
+
const right = asFiniteNumber(offchain);
|
|
185
|
+
if (left === void 0 || right === void 0) return;
|
|
186
|
+
const scale = Math.max(Math.abs(left), Math.abs(right));
|
|
187
|
+
return scale === 0 ? 0 : Math.abs(left - right) / scale * 1e4;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Same formula as {@link relativeDiffBps} for bigints, computed in integer
|
|
191
|
+
* arithmetic to milli-bps so a 1e18-scale amount does not round through
|
|
192
|
+
* `Number`.
|
|
193
|
+
**/
|
|
194
|
+
function relativeDiffBpsBigint(onchain, offchain) {
|
|
195
|
+
if (onchain === offchain) return 0;
|
|
196
|
+
const diff = onchain > offchain ? onchain - offchain : offchain - onchain;
|
|
197
|
+
const scale = abs(onchain) > abs(offchain) ? abs(onchain) : abs(offchain);
|
|
198
|
+
if (scale === 0n) return 0;
|
|
199
|
+
return Number(diff * 10000n * 1000n / scale) / 1e3;
|
|
200
|
+
}
|
|
155
201
|
function union(left, right) {
|
|
156
202
|
return [.../* @__PURE__ */ new Set([...left, ...right])];
|
|
157
203
|
}
|
|
@@ -234,6 +280,7 @@ exports.isAmountWithinTolerance = isAmountWithinTolerance;
|
|
|
234
280
|
exports.isBpsWithinTolerance = isBpsWithinTolerance;
|
|
235
281
|
exports.isRecord = isRecord;
|
|
236
282
|
exports.isUsdWithinTolerance = isUsdWithinTolerance;
|
|
283
|
+
exports.relativeDiffBps = relativeDiffBps;
|
|
237
284
|
exports.toCompareCounts = toCompareCounts;
|
|
238
285
|
exports.union = union;
|
|
239
286
|
exports.withExpected = withExpected;
|
|
@@ -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
|
});
|