@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
|
@@ -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}
|
|
@@ -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 };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { ierc20Abi } from "../abi/iERC20.js";
|
|
1
2
|
import { iCreditFacadeV310Abi } from "../abi/310/generated.js";
|
|
2
3
|
import { AddressMap } from "../sdk/utils/AddressMap.js";
|
|
3
4
|
import { AddressSet } from "../sdk/utils/AddressSet.js";
|
|
4
5
|
import { AssetsMap } from "../sdk/utils/AssetsMap.js";
|
|
5
6
|
import { childLogger } from "../sdk/utils/childLogger.js";
|
|
6
|
-
import { ierc20Abi } from "../abi/iERC20.js";
|
|
7
7
|
import "../sdk/constants/addresses.js";
|
|
8
8
|
import { MAX_UINT256, PERCENTAGE_FACTOR } from "../sdk/constants/math.js";
|
|
9
9
|
import { SDKConstruct } from "../sdk/base/SDKConstruct.js";
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { opportunityId } from "
|
|
2
|
-
import "
|
|
3
|
-
import
|
|
4
|
-
|
|
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 {
|
|
2
|
-
import "
|
|
3
|
-
import {
|
|
4
|
-
|
|
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 };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { getAlchemyUrl } from "../providers.js";
|
|
2
|
+
import { pino } from "pino";
|
|
3
|
+
//#region src/dev/mode-parity/scriptUtils.ts
|
|
4
|
+
const NETWORKS = [
|
|
5
|
+
"Mainnet",
|
|
6
|
+
"Monad",
|
|
7
|
+
"Plasma",
|
|
8
|
+
"Somnia",
|
|
9
|
+
"Etherlink"
|
|
10
|
+
];
|
|
11
|
+
const BACKEND_URL = process.env.BACKEND_URL ?? "https://api.gear-dev.dev";
|
|
12
|
+
const TIMEOUT = 48e4;
|
|
13
|
+
function requireEnv(name) {
|
|
14
|
+
const value = process.env[name];
|
|
15
|
+
if (!value) throw new Error(`${name} is required to reach the chains this script reads`);
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function rpcUrls() {
|
|
19
|
+
const alchemyKey = requireEnv("ALCHEMY_KEY");
|
|
20
|
+
const alchemy = (network) => {
|
|
21
|
+
const url = getAlchemyUrl(network, alchemyKey);
|
|
22
|
+
if (!url) throw new Error(`Alchemy serves no URL for ${network}`);
|
|
23
|
+
return url;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
Mainnet: alchemy("Mainnet"),
|
|
27
|
+
Monad: alchemy("Monad"),
|
|
28
|
+
Plasma: alchemy("Plasma"),
|
|
29
|
+
Somnia: requireEnv("SOMNIA_PROVIDER"),
|
|
30
|
+
Etherlink: requireEnv("ETHERLINK_PROVIDER")
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createLogger() {
|
|
34
|
+
return pino({
|
|
35
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
36
|
+
formatters: {
|
|
37
|
+
bindings: () => ({}),
|
|
38
|
+
level: (label) => ({ level: label })
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function errorMessage(error) {
|
|
43
|
+
return error instanceof Error ? error.message : String(error);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Runs `fn` over `items` with at most `concurrency` in flight.
|
|
47
|
+
**/
|
|
48
|
+
async function mapPool(items, concurrency, fn) {
|
|
49
|
+
let next = 0;
|
|
50
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
51
|
+
while (true) {
|
|
52
|
+
const index = next;
|
|
53
|
+
next += 1;
|
|
54
|
+
if (index >= items.length) return;
|
|
55
|
+
await fn(items[index]);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
await Promise.all(workers);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Formats a relative difference in bps as a percent, e.g. 12.5 → `"0.125%"`.
|
|
62
|
+
**/
|
|
63
|
+
function formatBpsAsPercent(bps) {
|
|
64
|
+
if (!Number.isFinite(bps)) return "";
|
|
65
|
+
const percent = bps / 100;
|
|
66
|
+
if (percent === 0) return "0%";
|
|
67
|
+
return `${Math.abs(percent) >= 1 ? trimTrailingZeros(percent.toFixed(3)) : trimTrailingZeros(percent.toPrecision(4))}%`;
|
|
68
|
+
}
|
|
69
|
+
function trimTrailingZeros(value) {
|
|
70
|
+
return value.includes(".") ? value.replace(/\.?0+$/, "") : value;
|
|
71
|
+
}
|
|
72
|
+
function worstColumns(worst) {
|
|
73
|
+
return {
|
|
74
|
+
"max diff": worst ? formatBpsAsPercent(worst.bps) : "",
|
|
75
|
+
"worst entity": worst?.id ?? ""
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Prints the shared membership table, the fields that differed most often,
|
|
80
|
+
* and any chain that failed to answer.
|
|
81
|
+
**/
|
|
82
|
+
function printCompareSummary(noun, report, extraLines = []) {
|
|
83
|
+
const { summary } = report;
|
|
84
|
+
console.log(`\n${noun} from ${report.backendUrl} vs the chain`);
|
|
85
|
+
console.table(summary.byChain.map((chain) => ({
|
|
86
|
+
chain: chain.chainId,
|
|
87
|
+
onchain: chain.onchainRows,
|
|
88
|
+
offchain: chain.offchainRows,
|
|
89
|
+
matched: chain.matched,
|
|
90
|
+
identical: chain.identical,
|
|
91
|
+
clean: chain.clean,
|
|
92
|
+
differing: chain.differing,
|
|
93
|
+
"only onchain": chain.onlyOnchain,
|
|
94
|
+
"only offchain": chain.onlyOffchain
|
|
95
|
+
})));
|
|
96
|
+
for (const line of extraLines) console.log(line);
|
|
97
|
+
const unexpected = summary.diffsByPath.filter((entry) => entry.unexpected > 0);
|
|
98
|
+
const expected = summary.diffsByPath.filter((entry) => entry.unexpected === 0 && entry.expected > 0);
|
|
99
|
+
if (unexpected.length) {
|
|
100
|
+
console.log("\nunexpected fields that differed most often:");
|
|
101
|
+
console.table(unexpected.slice(0, 25).map((entry) => ({
|
|
102
|
+
field: entry.path,
|
|
103
|
+
unexpected: entry.unexpected,
|
|
104
|
+
expected: entry.expected,
|
|
105
|
+
kinds: entry.kinds.join(", "),
|
|
106
|
+
...worstColumns(entry.worstUnexpected)
|
|
107
|
+
})));
|
|
108
|
+
}
|
|
109
|
+
if (expected.length) {
|
|
110
|
+
console.log("\nexpected fields (mode-scoped or within tolerance):");
|
|
111
|
+
console.table(expected.slice(0, 25).map((entry) => ({
|
|
112
|
+
field: entry.path,
|
|
113
|
+
rows: entry.expected,
|
|
114
|
+
kinds: entry.kinds.join(", "),
|
|
115
|
+
...worstColumns(entry.worstExpected)
|
|
116
|
+
})));
|
|
117
|
+
}
|
|
118
|
+
for (const chain of [...report.onchainChains, ...report.offchainChains]) if (chain.status === "error") console.log(`chain ${chain.chainId} failed on ${chain.source}:`, chain.error);
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { BACKEND_URL, NETWORKS, TIMEOUT, createLogger, errorMessage, formatBpsAsPercent, mapPool, printCompareSummary, requireEnv, rpcUrls };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { iWithdrawalCompressorV313Abi } from "../abi/IWithdrawalCompressorV313.js";
|
|
1
2
|
import { getNetworkType } from "../sdk/chain/chains.js";
|
|
2
3
|
import { getWithdrawalCompressorAddress } from "../sdk/accounts/withdrawal-compressor/addresses.js";
|
|
3
|
-
import { iWithdrawalCompressorV313Abi } from "../abi/IWithdrawalCompressorV313.js";
|
|
4
4
|
import "../sdk/index.js";
|
|
5
5
|
import { iMidasDataFeedAbi, iMidasRedemptionVaultAbi, midasGatewayAbi, midasRedeemerAbi, midasRedemptionVaultPhantomTokenAbi, securitizeRedeemerAbi, securitizeRedemptionGatewayAbi, securitizeRedemptionPhantomTokenAbi } from "./withdrawalAbi.js";
|
|
6
6
|
import { erc20Abi, hexToString, parseAbi, parseEther } from "viem";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/model/compare.schema.ts
|
|
2
|
+
/**
|
|
3
|
+
* Marks a field that only the backend fills.
|
|
4
|
+
**/
|
|
5
|
+
function offchainOnly(schema) {
|
|
6
|
+
return schema.meta({ compare: "offchainOnly" });
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Marks a field that only the chain fills.
|
|
10
|
+
**/
|
|
11
|
+
function onchainOnly(schema) {
|
|
12
|
+
return schema.meta({ compare: "onchainOnly" });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Marks a numeric field whose two sources may drift within {@link kind}.
|
|
16
|
+
**/
|
|
17
|
+
function tolerance(schema, kind) {
|
|
18
|
+
return schema.meta({ compare: { tolerance: kind } });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The compare tag registered on a schema, if any.
|
|
22
|
+
**/
|
|
23
|
+
function compareTagOf(schema) {
|
|
24
|
+
const meta = schema.meta();
|
|
25
|
+
if (!meta || !("compare" in meta)) return;
|
|
26
|
+
return meta.compare;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { compareTagOf, offchainOnly, onchainOnly, tolerance };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ZodAddress } from "../sdk/utils/zod.js";
|
|
2
|
+
import { offchainOnly } from "./compare.schema.js";
|
|
2
3
|
import { z } from "zod/v4";
|
|
3
4
|
//#region src/model/curators.schema.ts
|
|
4
5
|
/**
|
|
@@ -30,7 +31,7 @@ const curatorNameSchema = z.enum([
|
|
|
30
31
|
const curatorSchema = z.object({
|
|
31
32
|
address: ZodAddress(),
|
|
32
33
|
name: curatorNameSchema.optional(),
|
|
33
|
-
url: z.string().nullable()
|
|
34
|
+
url: offchainOnly(z.string().nullable())
|
|
34
35
|
});
|
|
35
36
|
//#endregion
|
|
36
37
|
export { curatorNameSchema, curatorSchema };
|
package/dist/esm/model/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CHART_METRIC_UNITS, CHART_RANGES, CHART_UNAVAILABLE_CODES, POOL_OPPORTUNITY_CHART_METRICS, POOL_POSITION_CHART_METRICS, STRATEGY_OPPORTUNITY_CHART_METRICS, STRATEGY_POSITION_CHART_METRICS } from "./charts.js";
|
|
2
|
+
import { compareTagOf, offchainOnly, onchainOnly, tolerance } from "./compare.schema.js";
|
|
2
3
|
import { amountSchema, assetTypeSchema, bpsSchema, chainIdSchema, leverageSchema, timestampSchema, tokenAmountSchema, tokenSchema, txCallSchema } from "./primitives.schema.js";
|
|
3
4
|
import { chartBundleSchemaFor, chartDenominationSchema, chartMetricSchema, chartQueryCodec, chartQueryParamsSchema, chartQuerySchema, chartRangeSchema, chartSeriesSchema, chartValueSchema, chartWindowSchema, poolOpportunityChartMetricSchema, poolPositionChartMetricSchema, strategyOpportunityChartMetricSchema, strategyPositionChartMetricSchema } from "./charts.schema.js";
|
|
4
5
|
import "./curators.js";
|
|
@@ -16,4 +17,4 @@ import { borrowRateBreakdownSchema, pnlBreakdownSchema, pointsProgramPnLSchema,
|
|
|
16
17
|
import "./primitives.js";
|
|
17
18
|
import "./response.js";
|
|
18
19
|
import { chainFailedSchema, chainMetadataSchema, chainSucceededSchema, dataSourceSchema, responseMetadataSchema, responseSchema } from "./response.schema.js";
|
|
19
|
-
export { CHART_METRIC_UNITS, CHART_RANGES, CHART_UNAVAILABLE_CODES, FILTER_ALL, POOL_OPPORTUNITY_CHART_METRICS, POOL_POSITION_CHART_METRICS, STRATEGY_OPPORTUNITY_CHART_METRICS, STRATEGY_POSITION_CHART_METRICS, amountSchema, apyBreakdownSchema, assetTypeSchema, booleanParamSchema, borrowRateBreakdownSchema, bpsSchema, chainFailedSchema, chainIdSchema, chainMetadataSchema, chainSucceededSchema, chartBundleSchemaFor, chartDenominationSchema, chartMetricSchema, chartQueryCodec, chartQueryParamsSchema, chartQuerySchema, chartRangeSchema, chartSeriesSchema, chartValueSchema, chartWindowSchema, curatorNameSchema, curatorSchema, dataSourceSchema, delayedReceivedAssetSchema, encodeFlag, filterAllSchema, filterable, instantReceivedAssetSchema, isFilterSet, leverageSchema, liquidatableAccountFilterSchema, liquidatableAccountSchema, liquidationApprovalSchema, liquidationDetailsSchema, liquidationPositionId, liquidationPositionSchema, matchesLiquidatableAccountFilter, matchesOpportunityFilter, matchesPositionFilter, noticeKindSchema, noticeSchema, opportunityBaseSchema, opportunityDetailSchema, opportunityFilterQueryParamsSchema, opportunityFilterQuerySchema, opportunityFilterSchema, opportunityId, opportunityKeySchema, opportunityKindSchema, opportunitySchema, pnlBreakdownSchema, pointRewardsSchema, pointsProgramPnLSchema, pointsProgramSchema, pointsRewardsPnLSchema, poolOpportunityChartMetricSchema, poolOpportunityDetailSchema, poolOpportunityId, poolOpportunityKeySchema, poolOpportunitySchema, poolPositionChartMetricSchema, poolPositionId, poolPositionKeySchema, poolPositionSchema, positionCollateralSchema, positionFilterQueryParamsSchema, positionFilterQuerySchema, positionFilterSchema, positionId, positionKeySchema, positionKindSchema, positionSchema, positionTransactionKindSchema, positionTransactionSchema, positionsTotalsSchema, priceFeedDataSchema, priceFeedSummarySchema, quotaAssetSchema, rateCurvePointSchema, rateCurveSchema, receivedAssetSchema, responseMetadataSchema, responseSchema, rewardsPnLSchema, rewardsSchema, strategyOpportunityChartMetricSchema, strategyOpportunityDetailSchema, strategyOpportunityId, strategyOpportunityKeySchema, strategyOpportunitySchema, strategyPositionChartMetricSchema, strategyPositionId, strategyPositionKeySchema, strategyPositionSchema, timestampSchema, tokenAmountSchema, tokenRewardsPnLSchema, tokenRewardsSchema, tokenSchema, txCallSchema };
|
|
20
|
+
export { CHART_METRIC_UNITS, CHART_RANGES, CHART_UNAVAILABLE_CODES, FILTER_ALL, POOL_OPPORTUNITY_CHART_METRICS, POOL_POSITION_CHART_METRICS, STRATEGY_OPPORTUNITY_CHART_METRICS, STRATEGY_POSITION_CHART_METRICS, amountSchema, apyBreakdownSchema, assetTypeSchema, booleanParamSchema, borrowRateBreakdownSchema, bpsSchema, chainFailedSchema, chainIdSchema, chainMetadataSchema, chainSucceededSchema, chartBundleSchemaFor, chartDenominationSchema, chartMetricSchema, chartQueryCodec, chartQueryParamsSchema, chartQuerySchema, chartRangeSchema, chartSeriesSchema, chartValueSchema, chartWindowSchema, compareTagOf, curatorNameSchema, curatorSchema, dataSourceSchema, delayedReceivedAssetSchema, encodeFlag, filterAllSchema, filterable, instantReceivedAssetSchema, isFilterSet, leverageSchema, liquidatableAccountFilterSchema, liquidatableAccountSchema, liquidationApprovalSchema, liquidationDetailsSchema, liquidationPositionId, liquidationPositionSchema, matchesLiquidatableAccountFilter, matchesOpportunityFilter, matchesPositionFilter, noticeKindSchema, noticeSchema, offchainOnly, onchainOnly, opportunityBaseSchema, opportunityDetailSchema, opportunityFilterQueryParamsSchema, opportunityFilterQuerySchema, opportunityFilterSchema, opportunityId, opportunityKeySchema, opportunityKindSchema, opportunitySchema, pnlBreakdownSchema, pointRewardsSchema, pointsProgramPnLSchema, pointsProgramSchema, pointsRewardsPnLSchema, poolOpportunityChartMetricSchema, poolOpportunityDetailSchema, poolOpportunityId, poolOpportunityKeySchema, poolOpportunitySchema, poolPositionChartMetricSchema, poolPositionId, poolPositionKeySchema, poolPositionSchema, positionCollateralSchema, positionFilterQueryParamsSchema, positionFilterQuerySchema, positionFilterSchema, positionId, positionKeySchema, positionKindSchema, positionSchema, positionTransactionKindSchema, positionTransactionSchema, positionsTotalsSchema, priceFeedDataSchema, priceFeedSummarySchema, quotaAssetSchema, rateCurvePointSchema, rateCurveSchema, receivedAssetSchema, responseMetadataSchema, responseSchema, rewardsPnLSchema, rewardsSchema, strategyOpportunityChartMetricSchema, strategyOpportunityDetailSchema, strategyOpportunityId, strategyOpportunityKeySchema, strategyOpportunitySchema, strategyPositionChartMetricSchema, strategyPositionId, strategyPositionKeySchema, strategyPositionSchema, timestampSchema, tokenAmountSchema, tokenRewardsPnLSchema, tokenRewardsSchema, tokenSchema, tolerance, txCallSchema };
|