@1delta/margin-fetcher 5.0.62 → 5.0.63

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/index.d.ts CHANGED
@@ -7648,8 +7648,14 @@ interface LiquidationTerms {
7648
7648
  * - `default-seizure` The whole escrow is forfeit on a missed payment
7649
7649
  * (Teller).
7650
7650
  * - `delivery` Unpaid collateral is delivered to lenders (TermMax).
7651
- */
7652
- model?: Open<'repay-seize' | 'soft-band' | 'stability-pool' | 'auction' | 'default-seizure' | 'delivery' | 'none'>;
7651
+ * - `repay-to-target-hf`
7652
+ * There is no close factor at all: the liquidator repays
7653
+ * however much it takes to restore the position to
7654
+ * {@link targetHealthFactor}, and the bonus SCALES with
7655
+ * how far under water it is (Aave V4). See
7656
+ * {@link healthFactorForMaxBonus}.
7657
+ */
7658
+ model?: Open<'repay-seize' | 'soft-band' | 'stability-pool' | 'auction' | 'default-seizure' | 'delivery' | 'repay-to-target-hf' | 'none'>;
7653
7659
  /** Who ends up holding the seized collateral. */
7654
7660
  absorber?: Open<'liquidator' | 'stability-pool' | 'other-borrowers' | 'lenders' | 'amm'>;
7655
7661
  /**
@@ -7712,6 +7718,17 @@ interface LiquidationTerms {
7712
7718
  * this. Without it, `closeFactor: 0.5` understates the worst case.
7713
7719
  */
7714
7720
  fullCloseBelowHealthFactor?: number;
7721
+ /**
7722
+ * `repay-to-target-hf` only: the health factor at or below which the
7723
+ * liquidator's bonus reaches its maximum — i.e. the value {@link penalty}
7724
+ * actually describes.
7725
+ *
7726
+ * Load-bearing next to a scaling bonus, because the two fields say different
7727
+ * things: Aave V4's bonus grows from ~0 at HF 1 to `maxLiquidationBonus`
7728
+ * here, so publishing the max alone reads as a flat penalty every liquidation
7729
+ * charges, and publishing nothing reads as a market with no penalty at all.
7730
+ */
7731
+ healthFactorForMaxBonus?: number;
7715
7732
  trigger: Open<'price' | 'time' | 'price-and-time' | 'redemption' | 'none'>;
7716
7733
  /** Max LTV at open. */
7717
7734
  ltv?: number;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { parseAbi, keccak256, encodeAbiParameters, formatEther, BaseError, erc20Abi, stringToHex, isAddress, pad, encodeFunctionData, formatUnits, getAddress, decodeFunctionResult, decodeAbiParameters, concat, parseAbiParameters, AbiEncodingLengthMismatchError, concatHex, toBytes, slice, hexToString, InvalidAddressError, boolToHex, integerRegex, numberToHex, bytesRegex, BytesSizeMismatchError, arrayRegex, UnsupportedPackedAbiType } from './chunk-JUYF2XLF.js';
2
2
  import './chunk-BYTNVMX7.js';
3
3
  import './chunk-PR4QN5HX.js';
4
- import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isCooler, isDssType, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, isTwyne, isFraxlend, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isCompoundV2Type, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isMorphoType, isUsdd, isSky, hasCrossMarginRisk, isEulerType, isAaveV4Type, isInit, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isMorphoBlue, isAaveV2Type, isAaveV32Type, isAaveV3Type, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
4
+ import { Lender, isExactly, isMidnight, isTerm, isTermMax, isTeller, isListaDao, isLiquityFamily, isRiver, isInverse, isCooler, isDssType, isCompoundV3Type, isFluid, isGearboxV3, isLlamaLend, isFrankencoin, isResupply, isCurvance, isTwyne, isFraxlend, isAaveV4Type, LIQUITY_FAMILY_LENDERS, isAaveType, isCompoundV3, isCompoundV2Type, isMultiMarket, isSiloV2Type, isSiloV3Type, isDolomite, isMorphoType, isUsdd, isSky, hasCrossMarginRisk, isEulerType, isInit, isVenusType, isSumerType, AAVE_V3_LENDERS, AAVE_V2_LENDERS, isMorphoBlue, isAaveV2Type, isAaveV32Type, isAaveV3Type, isYLDR, isLista, hasSubAccounts, isTectonicType, isKineticType, isBenqiType } from '@1delta/lender-registry';
5
5
  export { hasCrossMarginRisk, isAaveType, isAaveV2Type, isAaveV32Type, isAaveV3Type, isCompoundV3, isCompoundV3Type, isInit, isMorphoType, isMultiMarket, isYLDR } from '@1delta/lender-registry';
6
6
  import { fetchTokenLists, fetchTokenList, aavePools, compoundV3Pools, initConfig, morphoPools, compoundV2Pools, eulerVaults, siloMarkets, siloV2LenderKey, siloMarketsV3, siloV3LenderKey, aaveV4Spokes, aaveV4SpokeLenderKey, fluidResolvers, gearboxResolvers, dolomiteConfigs, midnightConfig, termConfig, termMarketsByChain, exactlyConfig, liquityLendersByChain, riverLendersByChain, inverseLendersByChain, coolerLendersByChain, llamaLendLendersByChain, usddLendersByChain, skyLendersByChain, frankencoinLendersByChain, resupplyLendersByChain, curvanceLendersByChain, fraxlendLendersByChain, twyneLendersByChain, tellerConfigByChain, tellerPoolsByChain, termMaxConfigByChain, aaveReserves, compoundV3Reserves, resolveTermApiBase, liquityConfigFor, liquityBranchesByChain, riverConfigFor, riverChainData, termMaxApiBaseUrl, termMaxLenderKey, inverseConfigFor, inverseChainData, coolerConfigFor, llamaLendConfigFor, llamaLendChainData, llamaLendApiBaseUrl, resupplyConfigFor, fraxlendConfigFor, dssConfigFor, dssChainData, dssDebtToken, dssDebtJoin, frankencoinConfigFor, frankencoinChainData, aaveOracles, morphoTypeMarkets, resolveMidnightApiBase, llamaLendMarketByVault, getSiloV2MarketEntry, getSiloV3MarketEntry, dolomiteEmode, curvanceConfigFor, aaveTokens, morphoTypeVaults, listaCollateralProvider, eulerConfigs, gearboxMarketCompressor, gearboxMarketConfigurators, compoundV2TokenArray, gearboxV3LenderKey, midnightMarketsByChain, twyneConfigFor, twyneChainData, parseAaveV4SpokeLenderKey, fraxlendPairsFor, gearboxAccountCompressor, compoundV2Tokens, exactlyMarketsByChain, aaveOraclesConfig, compoundV2Oracles, compoundV2OraclesConfig, compoundV3OraclesData, compoundV3BaseData, compoundV2Reserves, aaveV4Oracles, morphoTypeOracles } from '@1delta/data-sdk';
7
7
  import lodash from 'lodash';
@@ -18906,7 +18906,9 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18906
18906
  // >= PERCENTAGE_FACTOR (README: "bonus ≥ 100% (= ≥ 0% bonus)"). Same
18907
18907
  // semantics as V3's `liquidationBonus`, so penalty = bps/BPS - 1.
18908
18908
  liquidationPenalty: Number.isFinite(dynCfg.maxLiquidationBonus) && dynCfg.maxLiquidationBonus > 0 ? dynCfg.maxLiquidationBonus / BPS - 1 : 0,
18909
- closeFactor: 0.5,
18909
+ // No close factor in V4 — the ceiling is the whole debt. See the
18910
+ // field docstring on `AaveV4TokenEntry.closeFactor`.
18911
+ closeFactor: 1,
18910
18912
  targetHealthFactor,
18911
18913
  // _validateSetUsingAsCollateral: !paused && !frozen (frozen blocks enabling)
18912
18914
  collateralDisabled: dynCfg.collateralFactor === 0 || reserve.config.paused || reserve.config.frozen,
@@ -18922,7 +18924,9 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18922
18924
  collateralFactor: 0,
18923
18925
  borrowFactor: 1,
18924
18926
  liquidationPenalty: 0,
18925
- closeFactor: 0.5,
18927
+ // No close factor in V4 — the ceiling is the whole debt. See the
18928
+ // field docstring on `AaveV4TokenEntry.closeFactor`.
18929
+ closeFactor: 1,
18926
18930
  targetHealthFactor,
18927
18931
  collateralDisabled: true,
18928
18932
  debtDisabled: !reserve.config.borrowable || reserve.config.paused || reserve.config.frozen
@@ -18991,7 +18995,8 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
18991
18995
  borrowCap,
18992
18996
  supplyCap,
18993
18997
  debtCeiling: 0,
18994
- closeFactor: 0.5,
18998
+ // No close factor in V4 — the ceiling is the whole debt.
18999
+ closeFactor: 1,
18995
19000
  targetHealthFactor,
18996
19001
  spokeActive,
18997
19002
  spokeHalted,
@@ -19010,13 +19015,20 @@ function normalizeAaveV4(spokeDataList, chainId, spokeLenderKeyArg, prices, addi
19010
19015
  };
19011
19016
  }
19012
19017
  if (Object.keys(data).length === 0) return void 0;
19018
+ const liqCfg = spokeData.liquidationConfig;
19019
+ const wad = (v) => v != null && v > 0n ? Number(v) / 1e18 : void 0;
19013
19020
  return {
19014
19021
  data,
19015
19022
  chainId,
19016
19023
  params: {
19017
19024
  spoke: spokeAddr,
19018
19025
  oracle: spokeData.oracle.toLowerCase(),
19019
- label: spokeData.label
19026
+ label: spokeData.label,
19027
+ liquidation: {
19028
+ targetHealthFactor: wad(liqCfg?.targetHealthFactor),
19029
+ healthFactorForMaxBonus: wad(liqCfg?.healthFactorForMaxBonus),
19030
+ liquidationBonusFactorBps: liqCfg?.liquidationBonusFactor || void 0
19031
+ }
19020
19032
  }
19021
19033
  };
19022
19034
  }
@@ -23272,7 +23284,7 @@ async function fetchTellerMarkets(chainId) {
23272
23284
  }
23273
23285
  return null;
23274
23286
  };
23275
- const num18 = (i) => {
23287
+ const num19 = (i) => {
23276
23288
  const b = big22(i);
23277
23289
  return b === null ? null : Number(b);
23278
23290
  };
@@ -23282,9 +23294,9 @@ async function fetchTellerMarkets(chainId) {
23282
23294
  config,
23283
23295
  available: big22(base),
23284
23296
  committed: big22(base + 1),
23285
- minRateBps: num18(base + 2),
23297
+ minRateBps: num19(base + 2),
23286
23298
  collateralPerPrincipal: big22(base + 3),
23287
- maxLoanDuration: num18(base + 4),
23299
+ maxLoanDuration: num19(base + 4),
23288
23300
  marketId: big22(base + 5),
23289
23301
  totalAssets: big22(base + 6)
23290
23302
  };
@@ -24417,20 +24429,20 @@ async function fetchInverseMarkets(lender, chainId) {
24417
24429
  if (Object.keys(byAddr).length > 0) {
24418
24430
  const rows = markets.map((market) => {
24419
24431
  const m = byAddr[market.address.toLowerCase()];
24420
- const num18 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
24432
+ const num19 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
24421
24433
  return {
24422
24434
  market,
24423
- totalDebt: num18(m?.totalDebt),
24424
- dolaLiquidity: num18(m?.dolaLiquidity),
24425
- leftToBorrow: num18(m?.leftToBorrow),
24426
- price: num18(m?.price),
24435
+ totalDebt: num19(m?.totalDebt),
24436
+ dolaLiquidity: num19(m?.dolaLiquidity),
24437
+ leftToBorrow: num19(m?.leftToBorrow),
24438
+ price: num19(m?.price),
24427
24439
  borrowPaused: typeof m?.borrowPaused === "boolean" ? m.borrowPaused : null,
24428
- dailyBorrows: num18(m?.dailyBorrows),
24440
+ dailyBorrows: num19(m?.dailyBorrows),
24429
24441
  // The API serves it as a DECIMAL (`replenishmentIncentive: 0.1`),
24430
24442
  // unlike the on-chain bps — normalize here, and fall back to the
24431
24443
  // curated metadata value when the field is missing.
24432
24444
  replenishmentIncentiveBps: (() => {
24433
- const dec2 = num18(m?.replenishmentIncentive);
24445
+ const dec2 = num19(m?.replenishmentIncentive);
24434
24446
  if (dec2 !== null) return Math.round(dec2 * 1e4);
24435
24447
  const meta = Number(market.replenishmentIncentiveBps);
24436
24448
  return Number.isFinite(meta) ? meta : null;
@@ -69931,6 +69943,12 @@ function borrowDescription(b) {
69931
69943
  parts.push(
69932
69944
  "Liquidation is a public auction rather than a loan-to-value threshold" + (win ? `, open for ${duration(win)} once started` : "") + (pen != null ? `, and the liquidator is paid ${pct(pen * 100)}` : "") + "."
69933
69945
  );
69946
+ } else if (b.liquidation.model === "repay-to-target-hf") {
69947
+ const l = b.liquidation;
69948
+ const start = l.liquidationLtv != null && l.liquidationLtv > 0 ? `Liquidation starts at ${pct(l.liquidationLtv * 100)} LTV and` : "A liquidation";
69949
+ const sized = l.targetHealthFactor != null ? ` repays as much of your debt as it takes to restore a health factor of ${l.targetHealthFactor.toFixed(2)}` : " repays as much of your debt as it takes to restore the spoke\u2019s target health factor";
69950
+ const bonus = l.penalty != null && l.penalty > 0 ? ` \u2014 the liquidator\u2019s bonus scales up to ${pct(l.penalty * 100)}` + (l.healthFactorForMaxBonus != null ? ` at a health factor of ${l.healthFactorForMaxBonus.toFixed(2)}` : "") + "." : ".";
69951
+ parts.push(`${start}${sized} rather than a fixed share of it${bonus}`);
69934
69952
  } else if (b.liquidation.liquidationLtv != null && b.liquidation.liquidationLtv > 0) {
69935
69953
  const pen = b.liquidation.penalty;
69936
69954
  parts.push(
@@ -69964,6 +69982,27 @@ var TERM_PROFILES = [
69964
69982
  ]
69965
69983
  }
69966
69984
  }),
69985
+ P({
69986
+ id: "aave-v4.spoke@v1",
69987
+ name: "Aave V4 spoke (liquidated to a target health factor)",
69988
+ family: "pool",
69989
+ supply: {
69990
+ description: "A shared liquidity pool on an Aave V4 spoke. Deposits earn interest paid by borrowers of the hub asset, at a rate that moves with utilization. Withdrawals are instant up to the cash left in the pool.",
69991
+ implications: [
69992
+ "Your yield is not fixed \u2014 it rises and falls with borrowing demand.",
69993
+ "If utilization reaches 100 %, withdrawals are blocked until borrowers repay or new deposits arrive.",
69994
+ "Each spoke is its own market: the same asset on another spoke is a separate position with its own risk parameters."
69995
+ ]
69996
+ },
69997
+ borrow: {
69998
+ description: "Borrow against collateral at a rate that moves with utilization. Liquidation here is sized to a target health factor rather than capped at a fixed share of your debt: a liquidator repays whatever it takes to bring the position back up to the target, and their bonus grows the further under water it is.",
69999
+ implications: [
70000
+ "There is no 50 % close-factor protection \u2014 how much of your debt is taken depends on how far the position fell, and a deep enough drop takes all of it.",
70001
+ "The published liquidation penalty is the MAXIMUM bonus, charged only once the position is far enough under water; a shallow liquidation costs less.",
70002
+ "V4 uses ONE loan-to-value per asset for both borrowing and liquidation \u2014 there is no buffer between the LTV you can borrow to and the one that gets you liquidated."
70003
+ ]
70004
+ }
70005
+ }),
69967
70006
  // ── Fixed-term families ──────────────────────────────────────────────────
69968
70007
  P({
69969
70008
  id: "exactly.fixed@v1",
@@ -73740,8 +73779,18 @@ function toTermSheetInput(row, ctx = {}) {
73740
73779
  "flags.variableBorrowDisabled"
73741
73780
  ),
73742
73781
  config,
73743
- closeFactor: num15(pick2(row, "closeFactor")),
73744
- targetHealthFactor: num15(pick2(row, "targetHealthFactor")),
73782
+ // `caps.` is NOT optional here. `/pools/latest` serializes both of these as
73783
+ // TOP-LEVEL row fields, while `/lending/latest` — the endpoint the lending
73784
+ // term sheets are actually stamped on — nests them under `caps` alongside
73785
+ // the supply/borrow caps (yield-tracer `utils/lending.ts`). Reading only
73786
+ // the flat key made every sheet on that endpoint fall through to the
73787
+ // builder's `?? 1`, so Aave and Compound V2 published "the whole debt can
73788
+ // be repaid in one liquidation" against a real close factor of 0.5, and
73789
+ // Aave V4's `targetHealthFactor` vanished entirely.
73790
+ closeFactor: num15(pick2(row, "closeFactor", "caps.closeFactor")),
73791
+ targetHealthFactor: num15(
73792
+ pick2(row, "targetHealthFactor", "caps.targetHealthFactor")
73793
+ ),
73745
73794
  fixedTerm: pick2(row, "fixedTerm") ?? ctx.fixedTerm,
73746
73795
  terms: pick2(row, "terms"),
73747
73796
  market: resolveMarketDescriptors(row, ctx.market)
@@ -73773,6 +73822,11 @@ var ROW_DESCRIPTOR_KEYS = [
73773
73822
  // Fluid: distinguishes a SMART (T2/T3/T4) vault, whose collateral and/or
73774
73823
  // debt is a two-token DEX LP position, from an ordinary T1 pool row.
73775
73824
  "fluid",
73825
+ // Aave V4: the liquidation BONUS CURVE (`healthFactorForMaxBonus`,
73826
+ // `liquidationBonusFactor`). The sizing knob `targetHealthFactor` is a
73827
+ // market-level column and arrives without this; the curve is what turns the
73828
+ // published max bonus from a flat penalty into a bound.
73829
+ "aaveV4",
73776
73830
  "broker",
73777
73831
  "collateralProvider",
73778
73832
  "loanProvider",
@@ -75347,6 +75401,32 @@ var morphoBlueAdapter = {
75347
75401
  };
75348
75402
  }
75349
75403
  };
75404
+ var aaveV4Adapter = {
75405
+ id: "aave-v4",
75406
+ matches: isAaveV4Type,
75407
+ profileId: () => "aave-v4.spoke@v1",
75408
+ build: (input) => {
75409
+ const target = input.targetHealthFactor;
75410
+ const desc = input.market?.aaveV4 ?? {};
75411
+ const maxBonusHf = num17(desc.healthFactorForMaxBonus);
75412
+ return {
75413
+ borrow: {
75414
+ liquidation: {
75415
+ model: "repay-to-target-hf",
75416
+ // NOT a close factor — the ceiling. See the header.
75417
+ closeFactor: 1,
75418
+ targetHealthFactor: target,
75419
+ healthFactorForMaxBonus: maxBonusHf
75420
+ }
75421
+ }
75422
+ };
75423
+ }
75424
+ };
75425
+ function num17(v) {
75426
+ if (v == null) return void 0;
75427
+ const n = typeof v === "string" ? Number(v) : v;
75428
+ return Number.isFinite(n) && n > 0 ? n : void 0;
75429
+ }
75350
75430
 
75351
75431
  // src/terms/adapters/index.ts
75352
75432
  var TERM_ADAPTERS = [
@@ -75370,7 +75450,8 @@ var TERM_ADAPTERS = [
75370
75450
  curvanceAdapter,
75371
75451
  twyneAdapter,
75372
75452
  fraxlendAdapter,
75373
- morphoBlueAdapter
75453
+ morphoBlueAdapter,
75454
+ aaveV4Adapter
75374
75455
  ];
75375
75456
  function resolveAdapter(lender) {
75376
75457
  return TERM_ADAPTERS.find((a) => a.matches(lender));
@@ -76336,7 +76417,7 @@ function legEarnUid(marketUid) {
76336
76417
  if (isVaultVenue(parts[0])) return void 0;
76337
76418
  return marketUid;
76338
76419
  }
76339
- function num17(v) {
76420
+ function num18(v) {
76340
76421
  const n = typeof v === "number" ? v : Number(v);
76341
76422
  return Number.isFinite(n) ? n : 0;
76342
76423
  }
@@ -76358,10 +76439,10 @@ function legAsset(pos) {
76358
76439
  };
76359
76440
  }
76360
76441
  function toLeg(pos) {
76361
- const depositsUsd = num17(pos.depositsUSD);
76362
- const debtUsd = num17(pos.debtUSD) + num17(pos.debtStableUSD);
76363
- const hasSupply = depositsUsd !== 0 || num17(pos.deposits) !== 0;
76364
- const hasDebt = debtUsd !== 0 || num17(pos.debt) !== 0 || num17(pos.debtStable) !== 0;
76442
+ const depositsUsd = num18(pos.depositsUSD);
76443
+ const debtUsd = num18(pos.debtUSD) + num18(pos.debtStableUSD);
76444
+ const hasSupply = depositsUsd !== 0 || num18(pos.deposits) !== 0;
76445
+ const hasDebt = debtUsd !== 0 || num18(pos.debt) !== 0 || num18(pos.debtStable) !== 0;
76365
76446
  return {
76366
76447
  earnUid: legEarnUid(pos.marketUid),
76367
76448
  marketUid: pos.marketUid,
@@ -76401,12 +76482,12 @@ function earnPositionFromLenderEntry(entry) {
76401
76482
  (s) => s.legs.some((l) => l.side !== "none") || s.netUsd !== 0
76402
76483
  );
76403
76484
  const crossMargin = active.length <= 1;
76404
- const suppliedUsd = num17(entry.balanceData?.deposits);
76405
- const borrowedUsd = num17(entry.balanceData?.debt);
76485
+ const suppliedUsd = num18(entry.balanceData?.deposits);
76486
+ const borrowedUsd = num18(entry.balanceData?.debt);
76406
76487
  const aprBreakdown = {
76407
- market: num17(entry.aprData?.apr),
76408
- rewards: num17(entry.aprData?.rewardApr),
76409
- intrinsic: num17(entry.aprData?.intrinsicApr)
76488
+ market: num18(entry.aprData?.apr),
76489
+ rewards: num18(entry.aprData?.rewardApr),
76490
+ intrinsic: num18(entry.aprData?.intrinsicApr)
76410
76491
  };
76411
76492
  return {
76412
76493
  positionUid: buildLendingPositionUid(entry.lender, entry.chainId),
@@ -76420,15 +76501,15 @@ function earnPositionFromLenderEntry(entry) {
76420
76501
  ...entry.lenderInfo?.logoUri ? { logoURI: entry.lenderInfo.logoUri } : {},
76421
76502
  suppliedUsd,
76422
76503
  borrowedUsd,
76423
- netUsd: num17(entry.balanceData?.nav),
76504
+ netUsd: num18(entry.balanceData?.nav),
76424
76505
  apr: aprBreakdown.market + aprBreakdown.rewards + aprBreakdown.intrinsic,
76425
76506
  aprBreakdown,
76426
- depositApr: num17(entry.aprData?.depositApr),
76427
- borrowApr: num17(entry.aprData?.borrowApr),
76507
+ depositApr: num18(entry.aprData?.depositApr),
76508
+ borrowApr: num18(entry.aprData?.borrowApr),
76428
76509
  // Only a cross-margin account HAS one health factor. Publishing the first
76429
76510
  // sub-account's as the row's would be a number about a different position.
76430
76511
  health: crossMargin ? active[0]?.health ?? null : null,
76431
- leverage: num17(entry.leverage),
76512
+ leverage: num18(entry.leverage),
76432
76513
  crossMargin,
76433
76514
  legs: subAccounts.flatMap((s) => s.legs),
76434
76515
  subAccounts: active,