{"version":3,"file":"index.cjs","sources":["../../../../src/tbv/integrations/aave/constants.ts","../../../../src/tbv/integrations/aave/clients/query.ts","../../../../src/tbv/integrations/aave/clients/spoke.ts","../../../../src/tbv/integrations/aave/clients/positionProxy.ts","../../../../src/tbv/integrations/aave/clients/oracle.ts","../../../../src/tbv/integrations/aave/clients/hub.ts","../../../../src/tbv/integrations/aave/clients/transaction.ts","../../../../src/tbv/integrations/aave/utils/aaveConversions.ts","../../../../src/tbv/integrations/aave/utils/debtUtils.ts","../../../../src/tbv/integrations/aave/utils/healthFactor.ts","../../../../src/tbv/integrations/aave/utils/cascadeSimulation.ts","../../../../src/tbv/integrations/aave/utils/optimalOrder.ts","../../../../src/tbv/integrations/aave/utils/vaultSplit.ts"],"sourcesContent":["/**\n * Aave Protocol Constants\n *\n * Constants for interacting with Aave v4 protocol.\n * Reference: https://github.com/aave/aave-v4 ISpoke.sol\n */\n\n/**\n * Aave contract function names\n * Centralized constants for contract interactions\n */\nexport const AAVE_FUNCTION_NAMES = {\n  /** Withdraw selected vaults from position (partial withdrawal) */\n  WITHDRAW_COLLATERALS: \"withdrawCollaterals\",\n  /** Borrow from Core Spoke position */\n  BORROW: \"borrowFromCorePosition\",\n  /** Repay debt to Core Spoke position */\n  REPAY: \"repayToCorePosition\",\n  /** Reorder vault prefix ordering for liquidation priority */\n  REORDER_VAULTS: \"reorderVaults\",\n} as const;\n\n/**\n * Full basis points scale (10000 BPS = 100%)\n *\n * Use this when converting BPS directly to decimal:\n * Example: 8000 BPS / 10000 = 0.80\n */\nexport const BPS_SCALE = 10000;\n\n/**\n * Aave base currency decimals\n * Account data values (collateral, debt) use 1e26 = $1 USD\n *\n * Reference: ISpoke.sol UserAccountData\n */\nexport const AAVE_BASE_CURRENCY_DECIMALS = 26;\n\n/**\n * Aave RAY-scaled base currency decimals\n * Debt values (totalDebtValueRay) use 1e53 = $1 USD\n * (base currency 1e26 scaled by RAY 1e27).\n *\n * Reference: IAaveSpoke.sol UserAccountData.totalDebtValueRay\n */\nexport const AAVE_BASE_CURRENCY_RAY_DECIMALS = 53;\n\n/**\n * WAD decimals (1e18 = 1.0)\n * Used for health factor and collateral factor values\n *\n * Reference: ISpoke.sol - \"healthFactor expressed in WAD. 1e18 represents a health factor of 1.00\"\n */\nexport const WAD_DECIMALS = 18;\n\n/**\n * Health factor warning threshold\n * Positions below this are considered at risk of liquidation\n */\nexport const HEALTH_FACTOR_WARNING_THRESHOLD = 1.5;\n\n/**\n * Minimum health factor allowed for borrowing. Collateral factor doubles as the\n * liquidation threshold here, so this floor is the only borrow→liquidation cushion.\n */\nexport const MIN_HEALTH_FACTOR_FOR_BORROW = 1.05;\n\n/**\n * Approval headroom for repay-all, sized against interest accrual between\n * quoting the debt and transaction execution.\n *\n * 0.5% buffer (50 basis points). Sized to absorb hours of execution delay\n * (e.g. Safe-multisig quorum collection). The repay itself sends the\n * repay-all sentinel and the adapter pulls only what's actually owed; the\n * buffer only pads the approval cap, and the cap is additionally bounded by\n * the user's balance, so a larger buffer never blocks a legitimate repay.\n */\nexport const FULL_REPAY_BUFFER_DIVISOR = 200n; // 1/200 = 0.5% buffer\n\n","/**\n * Aave Integration Adapter - Read operations (queries)\n *\n * Only includes functions that provide data NOT available from the indexer.\n * Most position/vault data should be fetched from the GraphQL indexer instead.\n */\n\nimport { type Address, type Hex, type PublicClient, zeroAddress } from \"viem\";\n\nimport type { AaveMarketPosition, PositionSizeParams } from \"../types.js\";\nimport AaveIntegrationAdapterABI from \"./abis/AaveIntegrationAdapter.abi.json\";\n\n/**\n * The adapter's custom error for \"this account has no position proxy\".\n *\n * `AaveAdapter._getBorrowerProxy` reverts with it when `userToProxy[user]` is\n * the zero address, which is every account that has never opened a position —\n * so on a freshly deployed adapter it is the normal answer for a new user, not\n * a failure. Matched by name (the ABI carries the error entry, so viem decodes\n * it) rather than by raw selector.\n */\nconst NO_POSITION_ERROR_NAME = \"InvalidProxyContract\";\n\n/** Bound on the `cause` walk, so a self-referencing chain cannot spin. */\nconst MAX_ERROR_CAUSE_DEPTH = 10;\n\n/**\n * True when `err` is the adapter reverting because the account has no proxy.\n *\n * Matches structurally on viem's decoded `data.errorName` rather than with\n * `instanceof BaseError` / `err.walk(...)`. The SDK lists `viem` as external,\n * so a consumer can — and in this monorepo does — resolve a physically\n * different copy of the same viem version than the one bundled here (pnpm\n * keys the store path on peer versions). Errors thrown by the caller's client\n * are instances of the caller's classes, so `instanceof` silently returns\n * false across that boundary and the revert would escape as an opaque throw.\n *\n * Narrow on purpose: any other revert, and every transport/RPC failure, must\n * keep propagating. Reporting those as \"no position\" would turn an infra\n * outage into a silent, wrong answer for callers that gate signing on it.\n */\nfunction isNoPositionRevert(err: unknown): boolean {\n  let current: unknown = err;\n  for (let depth = 0; depth < MAX_ERROR_CAUSE_DEPTH; depth++) {\n    if (typeof current !== \"object\" || current === null) return false;\n    const { data, cause } = current as {\n      data?: { errorName?: unknown };\n      cause?: unknown;\n    };\n    if (data?.errorName === NO_POSITION_ERROR_NAME) return true;\n    if (cause === undefined || cause === current) return false;\n    current = cause;\n  }\n  return false;\n}\n\n/**\n * Get a position by user address.\n *\n * The adapter resolves the user's proxy contract and collateralized vault IDs.\n *\n * NOTE: Prefer using the indexer (fetchAavePositionWithCollaterals) for position data.\n * This function is only needed when you need data not available in the indexer,\n * or when you need to verify on-chain state.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @param user - User's Ethereum address\n * @returns Market position data or null if position doesn't exist\n */\nexport async function getPosition(\n  publicClient: PublicClient,\n  contractAddress: Address,\n  user: Address,\n): Promise<AaveMarketPosition | null> {\n  type PositionResult = {\n    proxyContract: Address;\n    vaultIds: Hex[];\n    totalCollateralBTC: bigint;\n  };\n\n  let result: unknown;\n  try {\n    result = await publicClient.readContract({\n      address: contractAddress,\n      abi: AaveIntegrationAdapterABI,\n      functionName: \"getPosition\",\n      args: [user],\n    });\n  } catch (err) {\n    // \"No proxy yet\" is a revert, not a zero-address return value, so it has\n    // to be caught here for this function to honour its documented contract.\n    if (isNoPositionRevert(err)) return null;\n    throw err;\n  }\n\n  const position = result as PositionResult;\n\n  // Defence in depth: the adapter reverts rather than returning a zero proxy,\n  // so this should be unreachable. Kept because the return value is external\n  // input — a zero proxy must never reach a caller as a real position.\n  if (position.proxyContract === zeroAddress) {\n    return null;\n  }\n\n  return {\n    proxyContract: position.proxyContract,\n    vaultIds: position.vaultIds,\n    totalCollateralBTC: position.totalCollateralBTC,\n  };\n}\n\n/**\n * Get position size parameters from the adapter contract.\n *\n * Returns the maximum BTC position size and maximum vaults per position\n * as configured on-chain.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @returns Position size parameters (maxPositionBTC, maxVaultsPerPosition)\n */\nexport async function getPositionSizeParams(\n  publicClient: PublicClient,\n  contractAddress: Address,\n): Promise<PositionSizeParams> {\n  const result = await publicClient.readContract({\n    address: contractAddress,\n    abi: AaveIntegrationAdapterABI,\n    functionName: \"getPositionSizeParams\",\n  });\n\n  const [maxPositionBTC, maxVaultsPerPosition] = result as [bigint, bigint];\n\n  return {\n    maxPositionBTC,\n    maxVaultsPerPosition,\n  };\n}\n","/**\n * Aave Spoke Client - Read operations\n *\n * Provides read operations for interacting with Aave v4 Spoke contracts.\n * Used to fetch live user position data (debt, collateral) from the Core Spoke.\n *\n * Note: Reserve data should be fetched from the indexer via fetchReserves.ts\n * since it doesn't need to be live and benefits from caching.\n */\n\nimport type { Abi, Address, PublicClient } from \"viem\";\n\nimport type {\n  AaveSpokeUserAccountData,\n  AaveSpokeUserPosition,\n} from \"../types.js\";\nimport AaveSpokeABI from \"./abis/AaveSpoke.abi.json\";\n\n/** Account data result type from contract */\ntype AccountDataResult = {\n  riskPremium: bigint;\n  avgCollateralFactor: bigint;\n  healthFactor: bigint;\n  totalCollateralValue: bigint;\n  totalDebtValueRay: bigint;\n  activeCollateralCount: bigint;\n  borrowCount: bigint;\n};\n\n/** Position result type from contract */\ntype PositionResult = {\n  drawnShares: bigint;\n  premiumShares: bigint;\n  premiumOffsetRay: bigint;\n  suppliedShares: bigint;\n  dynamicConfigKey: number;\n};\n\n/**\n * Maps contract result to AaveSpokeUserPosition\n */\nfunction mapPositionResult(result: PositionResult): AaveSpokeUserPosition {\n  return {\n    drawnShares: result.drawnShares,\n    premiumShares: result.premiumShares,\n    premiumOffsetRay: result.premiumOffsetRay,\n    suppliedShares: result.suppliedShares,\n    dynamicConfigKey: result.dynamicConfigKey,\n  };\n}\n\n/** Maps contract result to AaveSpokeUserAccountData */\nfunction mapAccountDataResult(\n  data: AccountDataResult,\n): AaveSpokeUserAccountData {\n  return {\n    riskPremium: data.riskPremium,\n    avgCollateralFactor: data.avgCollateralFactor,\n    healthFactor: data.healthFactor,\n    totalCollateralValue: data.totalCollateralValue,\n    totalDebtValueRay: data.totalDebtValueRay,\n    activeCollateralCount: data.activeCollateralCount,\n    borrowCount: data.borrowCount,\n  };\n}\n\n/**\n * Get aggregated user account health data from AAVE spoke.\n *\n * **Live data** - Fetches real-time account health including health factor, total collateral,\n * and total debt across all reserves. Values are calculated on-chain using AAVE oracles\n * and are the authoritative source for liquidation decisions.\n *\n * @param publicClient - Viem public client for reading contracts (from `createPublicClient()`)\n * @param spokeAddress - AAVE Spoke contract address (BTC Vault Core Spoke for vBTC collateral)\n * @param userAddress - User's proxy contract address (NOT user's wallet address)\n * @returns User account data with health metrics, collateral, and debt values\n *\n * @example\n * ```typescript\n * import { getUserAccountData } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n * import { createPublicClient, http } from \"viem\";\n * import { sepolia } from \"viem/chains\";\n *\n * const publicClient = createPublicClient({\n *   chain: sepolia,\n *   transport: http()\n * });\n *\n * const accountData = await getUserAccountData(\n *   publicClient,\n *   \"0x123...\", // AAVE Spoke address\n *   \"0x456...\"  // User's AAVE proxy address (from getPosition)\n * );\n *\n * console.log(\"Health Factor:\", accountData.healthFactor);\n * console.log(\"Collateral (USD):\", accountData.totalCollateralValue);\n * console.log(\"Debt (USD):\", accountData.totalDebtValueRay);\n * ```\n *\n * @remarks\n * **Return values:**\n * - `healthFactor` - WAD format (1e18 = 1.0). Below 1.0 = liquidatable\n * - `totalCollateralValue` - USD value in base currency (1e26 = $1)\n * - `totalDebtValueRay` - USD value in RAY-scaled base currency (1e53 = $1)\n * - `avgCollateralFactor` - Weighted average collateral factor in WAD (1e18 = 100%)\n * - `riskPremium` - Additional risk premium\n *\n * **Use cases:**\n * - Check liquidation risk before borrowing\n * - Calculate safe borrow amount\n * - Monitor position health\n * - Display UI health indicators\n */\nexport async function getUserAccountData(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  userAddress: Address,\n): Promise<AaveSpokeUserAccountData> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getUserAccountData\",\n    args: [userAddress],\n  });\n\n  return mapAccountDataResult(result as AccountDataResult);\n}\n\n/**\n * Read a user's position for one reserve and their aggregate account data in a\n * single hard-fail multicall. Both reads are required for the live position\n * view, so a revert on either rejects the whole call (matching the prior\n * `Promise.all`); the gain is one round-trip instead of two `eth_call`s.\n */\nexport async function getUserPositionAndAccountData(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveId: bigint,\n  userAddress: Address,\n): Promise<{\n  position: AaveSpokeUserPosition;\n  accountData: AaveSpokeUserAccountData;\n}> {\n  const [positionResult, accountDataResult] = await publicClient.multicall({\n    contracts: [\n      {\n        address: spokeAddress,\n        abi: AaveSpokeABI as Abi,\n        functionName: \"getUserPosition\" as const,\n        args: [reserveId, userAddress] as const,\n      },\n      {\n        address: spokeAddress,\n        abi: AaveSpokeABI as Abi,\n        functionName: \"getUserAccountData\" as const,\n        args: [userAddress] as const,\n      },\n    ],\n    allowFailure: false,\n  });\n\n  return {\n    position: mapPositionResult(positionResult as unknown as PositionResult),\n    accountData: mapAccountDataResult(\n      accountDataResult as unknown as AccountDataResult,\n    ),\n  };\n}\n\n/**\n * Get user position from the Spoke\n *\n * This fetches live data from the contract because debt accrues interest\n * and needs to be current for accurate health factor calculations.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param spokeAddress - Aave Spoke contract address\n * @param reserveId - Reserve ID\n * @param userAddress - User's proxy contract address\n * @returns User position data\n */\nexport async function getUserPosition(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveId: bigint,\n  userAddress: Address,\n): Promise<AaveSpokeUserPosition> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getUserPosition\",\n    args: [reserveId, userAddress],\n  });\n\n  return mapPositionResult(result as PositionResult);\n}\n\n/**\n * Get user's exact total debt in a reserve (token units, not shares).\n *\n * Returns the Spoke-side amount owed including accrued interest — but NOT the\n * adapter's uncollected interest fee. Display/routing only; for full repayment\n * see the remarks below. Debt accrues interest every block, so fetch it live.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param spokeAddress - AAVE Spoke contract address\n * @param reserveId - Reserve ID for the debt asset (e.g., `2n` for USDC)\n * @param userAddress - User's proxy contract address\n * @returns Total debt amount in token units (e.g., for USDC: `100000000n` = 100 USDC)\n *\n * @example\n * ```typescript\n * import { getUserTotalDebt } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n * import { formatUnits } from \"viem\";\n *\n * const totalDebt = await getUserTotalDebt(\n *   publicClient,\n *   AAVE_SPOKE_ADDRESS,\n *   2n, // USDC reserve\n *   proxyAddress\n * );\n *\n * console.log(\"Debt:\", formatUnits(totalDebt, 6), \"USDC\");\n * ```\n *\n * @remarks\n * **Important for full repayment:** do NOT repay a plain amount derived from\n * this quote — it excludes the adapter's interest fee, and rounding can leave\n * residual debt shares (dust). Send the repay-all sentinel\n * (`type(uint256).max`) with an approval sized from the position proxy's\n * fee-inclusive `getPositionReserveTotalDebt` plus\n * `FULL_REPAY_BUFFER_DIVISOR` headroom; the adapter pulls only what's owed.\n * For partial repayment, use any amount less than total debt.\n */\nexport async function getUserTotalDebt(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveId: bigint,\n  userAddress: Address,\n): Promise<bigint> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getUserTotalDebt\",\n    args: [reserveId, userAddress],\n  });\n\n  return result as bigint;\n}\n\n/**\n * Probe `getUserPosition` for many reserves in a single multicall.\n *\n * Returns one entry per `reserveId` in input order. Per-reserve reverts are\n * isolated (`allowFailure: true`): that entry is `null` while the rest of the\n * batch still resolves. Use for debt-reserve discovery, where a failed read\n * means \"treat as no debt\", not a fatal error.\n */\nexport async function getUserPositions(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveIds: bigint[],\n  userAddress: Address,\n): Promise<(AaveSpokeUserPosition | null)[]> {\n  if (reserveIds.length === 0) return [];\n  const results = await publicClient.multicall({\n    contracts: reserveIds.map((reserveId) => ({\n      address: spokeAddress,\n      abi: AaveSpokeABI as Abi,\n      functionName: \"getUserPosition\" as const,\n      args: [reserveId, userAddress] as const,\n    })),\n    allowFailure: true,\n  });\n  return results.map((r) =>\n    r.status === \"success\"\n      ? mapPositionResult(r.result as PositionResult)\n      : null,\n  );\n}\n\n/**\n * Read `getUserTotalDebt` for many reserves in a single multicall.\n *\n * Hard-fails (`allowFailure: false`): any reserve's revert rejects the whole\n * call. Use only for reserves already known to carry debt — there a failed\n * read is a genuine error, not a \"no debt\" signal.\n */\nexport async function getUserTotalDebts(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveIds: bigint[],\n  userAddress: Address,\n): Promise<bigint[]> {\n  if (reserveIds.length === 0) return [];\n  const results = await publicClient.multicall({\n    contracts: reserveIds.map((reserveId) => ({\n      address: spokeAddress,\n      abi: AaveSpokeABI as Abi,\n      functionName: \"getUserTotalDebt\" as const,\n      args: [reserveId, userAddress] as const,\n    })),\n    allowFailure: false,\n  });\n  return results as unknown as bigint[];\n}\n\n/** Result type from the `getReserve` contract call.\n *\n * Matches the on-chain `Reserve` struct defined in `ITBVAaveSpoke.sol`:\n *   struct Reserve {\n *     address underlying;\n *     address hub;\n *     uint16 assetId;\n *     uint8 decimals;\n *     uint24 collateralRisk;\n *     ReserveFlags flags;   // uint8 bitmap\n *     uint32 dynamicConfigKey;\n *   }\n *\n * Note: this is the `Reserve` struct, NOT `ReserveConfig` — the contract\n * exposes both as separate functions and they return different shapes.\n */\ntype ReserveResult = {\n  underlying: Address;\n  hub: Address;\n  assetId: number;\n  decimals: number;\n  collateralRisk: number;\n  flags: number;\n  dynamicConfigKey: number;\n};\n\n/**\n * Get reserve data from the Core Spoke contract via the `getReserve` selector.\n *\n * Returns static reserve properties including the `dynamicConfigKey` needed\n * for `getDynamicReserveConfig` calls. Use this as a fallback when reserve\n * data is not available from the GraphQL indexer.\n *\n * Do NOT confuse with the contract's separate `getReserveConfig` function,\n * which returns `{collateralRisk, paused, frozen, borrowable, receiveSharesEnabled}`.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param spokeAddress - Core Spoke contract address\n * @param reserveId - Reserve ID\n * @returns Reserve data including `dynamicConfigKey`\n */\nexport async function getReserve(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveId: bigint,\n): Promise<ReserveResult> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getReserve\",\n    args: [reserveId],\n  });\n  return result as ReserveResult;\n}\n\n/** Result type from getLiquidationConfig contract call */\ntype LiquidationConfigResult = {\n  targetHealthFactor: bigint;\n  healthFactorForMaxBonus: bigint;\n  liquidationBonusFactor: bigint;\n};\n\n/** Result type from getDynamicReserveConfig contract call */\ntype DynamicReserveConfigResult = {\n  collateralFactor: bigint;\n  maxLiquidationBonus: bigint;\n  liquidationFee: bigint;\n};\n\n/**\n * Get the target health factor (THF) from the Core Spoke contract.\n *\n * Per-spoke governance parameter. After a liquidation, the protocol targets\n * restoring the position to this health factor.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param spokeAddress - Core Spoke contract address\n * @returns Target health factor in WAD (1e18 = 1.0). Example: 1.10 = 1_100_000_000_000_000_000n\n */\nexport async function getTargetHealthFactor(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n): Promise<bigint> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getLiquidationConfig\",\n  });\n  const config = result as LiquidationConfigResult;\n  return config.targetHealthFactor;\n}\n\n/**\n * Get the dynamic reserve config from the Core Spoke contract.\n *\n * Returns collateral factor, max liquidation bonus, and liquidation fee\n * for a specific reserve and dynamic config key.\n *\n * @param publicClient - Viem public client for reading contracts\n * @param spokeAddress - Core Spoke contract address\n * @param reserveId - Reserve ID (e.g., vBTC reserve ID from indexer config)\n * @param dynamicConfigKey - Dynamic config key (from reserve data)\n * @returns Dynamic reserve config with collateralFactor (BPS), maxLiquidationBonus (BPS), liquidationFee (BPS)\n */\nexport async function getDynamicReserveConfig(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n  reserveId: bigint,\n  dynamicConfigKey: number,\n): Promise<DynamicReserveConfigResult> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"getDynamicReserveConfig\",\n    args: [reserveId, dynamicConfigKey],\n  });\n  return result as DynamicReserveConfigResult;\n}\n","/**\n * Aave Adapter Position Proxy Client - Read operations\n *\n * Reads against a depositor's own AaveAdapterPositionProxy instance.\n *\n * NOT the Spoke's same-signature `getReserveTotalDebt` (reserve-wide debt of\n * ALL users): the proxy's returns THIS position's total debt including the\n * adapter's uncollected interest fee — the value the adapter resolves the\n * repay-all sentinel to, and therefore the right quote for sizing a repay-all\n * approval.\n */\n\nimport type { Address, PublicClient } from \"viem\";\n\nimport AaveAdapterPositionProxyABI from \"./abis/AaveAdapterPositionProxy.abi.json\";\n\n/**\n * Fee-inclusive total debt of the position held by `proxyContract` for\n * `reserveId`: Spoke debt plus the adapter's uncollected interest fee\n * (rounded up), computed lazily at the current block.\n */\nexport async function getPositionReserveTotalDebt(\n  publicClient: PublicClient,\n  proxyContract: Address,\n  reserveId: bigint,\n): Promise<bigint> {\n  const result = await publicClient.readContract({\n    address: proxyContract,\n    abi: AaveAdapterPositionProxyABI,\n    functionName: \"getReserveTotalDebt\",\n    args: [reserveId],\n  });\n\n  // This quote sizes a real approval — assert the shape instead of casting.\n  if (typeof result !== \"bigint\") {\n    throw new Error(\n      `getReserveTotalDebt returned a non-bigint result for reserve ${reserveId}`,\n    );\n  }\n  return result;\n}\n","/**\n * Read-only access to `IAaveOracle`. Prices are 8-decimal base units\n * ($80,000 = 8_000_000_000_000n).\n */\n\nimport type { Abi, Address, PublicClient } from \"viem\";\n\nimport AaveOracleABI from \"./abis/AaveOracle.abi.json\";\nimport AaveSpokeABI from \"./abis/AaveSpoke.abi.json\";\n\n/** `Spoke.ORACLE` is `immutable`; the result is safe to cache forever. */\nexport async function getOracleAddress(\n  publicClient: PublicClient,\n  spokeAddress: Address,\n): Promise<Address> {\n  const result = await publicClient.readContract({\n    address: spokeAddress,\n    abi: AaveSpokeABI,\n    functionName: \"ORACLE\",\n  });\n  return result as Address;\n}\n\n/** Batch read; reverts the WHOLE batch on the first bad reserve. */\nexport async function getReservesPrices(\n  publicClient: PublicClient,\n  oracleAddress: Address,\n  reserveIds: bigint[],\n): Promise<bigint[]> {\n  const result = await publicClient.readContract({\n    address: oracleAddress,\n    abi: AaveOracleABI,\n    functionName: \"getReservesPrices\",\n    args: [reserveIds],\n  });\n  return result as bigint[];\n}\n\nexport interface ReservePriceResult {\n  reserveId: bigint;\n  /** Raw 1e8 base units, or null on revert. */\n  priceRaw: bigint | null;\n  error: Error | null;\n}\n\n/**\n * Per-reserve isolated read for display lists (one bad source ≠ whole list\n * blank). One multicall round-trip instead of one `eth_call` per reserve:\n * each entry is `getReservesPrices([reserveId])` with `allowFailure: true`, so\n * a single reverting reserve isolates to its own error entry. A network-level\n * multicall failure marks every reserve failed rather than throwing — callers\n * (display hooks) rely on always getting a per-reserve result array.\n */\nexport async function getReservesPricesSafe(\n  publicClient: PublicClient,\n  oracleAddress: Address,\n  reserveIds: bigint[],\n): Promise<ReservePriceResult[]> {\n  if (reserveIds.length === 0) return [];\n\n  let results;\n  try {\n    results = await publicClient.multicall({\n      contracts: reserveIds.map((reserveId) => ({\n        address: oracleAddress,\n        abi: AaveOracleABI as Abi,\n        functionName: \"getReservesPrices\" as const,\n        args: [[reserveId]] as const,\n      })),\n      allowFailure: true,\n    });\n  } catch (err) {\n    const error = err instanceof Error ? err : new Error(String(err));\n    return reserveIds.map((reserveId) => ({\n      reserveId,\n      priceRaw: null,\n      error,\n    }));\n  }\n\n  return results.map((result, i): ReservePriceResult => {\n    const reserveId = reserveIds[i];\n    if (result.status !== \"success\") {\n      const error =\n        result.error instanceof Error\n          ? result.error\n          : new Error(String(result.error ?? \"getReservesPrices reverted\"));\n      return { reserveId, priceRaw: null, error };\n    }\n    const [priceRaw] = result.result as bigint[];\n    return { reserveId, priceRaw, error: null };\n  });\n}\n","/**\n * Read-only access to the Aave v4 Hub (`IHub`). Each spoke reserve points at\n * a Hub asset (`reserve.hub` + `reserve.assetId`); the Hub is where interest\n * accrues, so live borrow rates are read here rather than from the Spoke.\n */\n\nimport type { Abi, Address, PublicClient } from \"viem\";\n\nimport AaveHubABI from \"./abis/AaveHub.abi.json\";\n\n/** Identifies one Hub asset to read the drawn rate for. */\nexport interface AssetDrawnRateRequest {\n  /** Hub contract address (from the reserve's `hub` field). */\n  hub: Address;\n  /** Asset identifier on that Hub (from the reserve's `assetId` field). */\n  assetId: number;\n}\n\nexport interface AssetDrawnRateResult {\n  hub: Address;\n  assetId: number;\n  /** Annual borrow (drawn) rate in RAY (1e27 = 100%), or null on revert. */\n  rateRay: bigint | null;\n  error: Error | null;\n}\n\n/**\n * Per-asset isolated read of `getAssetDrawnRate` for display lists (one bad\n * asset ≠ whole list blank). One multicall round-trip instead of one\n * `eth_call` per asset, with `allowFailure: true` so a single reverting asset\n * isolates to its own error entry. A network-level multicall failure marks\n * every asset failed rather than throwing — callers (display hooks) rely on\n * always getting a per-asset result array.\n *\n * The returned rate is the linear annual rate in RAY (the Hub accrues\n * interest as `rate * dt / SECONDS_PER_YEAR`), i.e. an APR, not an APY.\n */\nexport async function getAssetDrawnRatesSafe(\n  publicClient: PublicClient,\n  requests: AssetDrawnRateRequest[],\n): Promise<AssetDrawnRateResult[]> {\n  if (requests.length === 0) return [];\n\n  let results;\n  try {\n    results = await publicClient.multicall({\n      contracts: requests.map(({ hub, assetId }) => ({\n        address: hub,\n        abi: AaveHubABI as Abi,\n        functionName: \"getAssetDrawnRate\" as const,\n        args: [BigInt(assetId)] as const,\n      })),\n      allowFailure: true,\n    });\n  } catch (err) {\n    const error = err instanceof Error ? err : new Error(String(err));\n    return requests.map(({ hub, assetId }) => ({\n      hub,\n      assetId,\n      rateRay: null,\n      error,\n    }));\n  }\n\n  return results.map((result, i): AssetDrawnRateResult => {\n    const { hub, assetId } = requests[i];\n    if (result.status !== \"success\") {\n      const error =\n        result.error instanceof Error\n          ? result.error\n          : new Error(String(result.error ?? \"getAssetDrawnRate reverted\"));\n      return { hub, assetId, rateRay: null, error };\n    }\n    return { hub, assetId, rateRay: result.result as bigint, error: null };\n  });\n}\n","/**\n * Aave Integration Adapter - Transaction builders\n *\n * Provides transaction builders for the AaveIntegrationAdapter contract.\n * Only includes Core Spoke operations for regular users (no Arbitrageur operations).\n *\n * These functions return unsigned transaction parameters that can be executed\n * by the vault service using its wallet client and transaction factory.\n */\n\nimport { type Address, type Hex, encodeFunctionData } from \"viem\";\n\nimport { AAVE_FUNCTION_NAMES } from \"../config.js\";\nimport type { TransactionParams } from \"../types.js\";\nimport AaveIntegrationAdapterABI from \"./abis/AaveIntegrationAdapter.abi.json\";\n\n/**\n * Build transaction to reorder vaults for liquidation priority.\n *\n * The permuted array must contain exactly the same vault IDs as the\n * current position, in the desired new order. Vaults are seized in\n * prefix order (index 0 first) during liquidation.\n *\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @param permutedVaultIds - Vault IDs in desired new order (must be a permutation of current vaults)\n * @returns Unsigned transaction parameters\n */\nexport function buildReorderVaultsTx(\n  contractAddress: Address,\n  permutedVaultIds: Hex[],\n): TransactionParams {\n  const data = encodeFunctionData({\n    abi: AaveIntegrationAdapterABI,\n    functionName: AAVE_FUNCTION_NAMES.REORDER_VAULTS,\n    args: [permutedVaultIds],\n  });\n\n  return {\n    to: contractAddress,\n    data,\n  };\n}\n\n/**\n * Build transaction to withdraw selected vaults from AAVE position.\n *\n * Withdraws specific vaults (partial withdrawal) and redeems them back to the depositor.\n * **Requires zero debt** - position must have no outstanding borrows.\n *\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @param vaultIds - Array of vault IDs (bytes32) to withdraw\n * @returns Unsigned transaction parameters for execution with viem wallet\n */\nexport function buildWithdrawCollateralsTx(\n  contractAddress: Address,\n  vaultIds: Hex[],\n): TransactionParams {\n  const data = encodeFunctionData({\n    abi: AaveIntegrationAdapterABI,\n    functionName: AAVE_FUNCTION_NAMES.WITHDRAW_COLLATERALS,\n    args: [vaultIds],\n  });\n\n  return {\n    to: contractAddress,\n    data,\n  };\n}\n\n/**\n * Build transaction to borrow assets against vBTC collateral.\n *\n * Borrows stablecoins (e.g., USDC) against your BTC collateral position.\n * Health factor must remain above 1.0 after borrowing, otherwise transaction will revert.\n *\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @param debtReserveId - AAVE reserve ID for the debt asset (e.g., `2n` for USDC reserve)\n * @param amount - Amount to borrow in token units with decimals (e.g., for USDC with 6 decimals: `100000000n` = 100 USDC). Use `parseUnits()` from viem.\n * @param receiver - Address to receive borrowed tokens (usually user's address)\n * @returns Unsigned transaction parameters for execution with viem wallet\n *\n * @example\n * ```typescript\n * import { buildBorrowTx } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n * import { parseUnits } from \"viem\";\n *\n * // Borrow 100 USDC (6 decimals)\n * const borrowAmount = parseUnits(\"100\", 6);\n *\n * const txParams = buildBorrowTx(\n *   \"0x123...\", // Adapter address\n *   2n, // USDC reserve ID\n *   borrowAmount,\n *   \"0x456...\" // Receiver address\n * );\n *\n * const hash = await walletClient.sendTransaction({\n *   to: txParams.to,\n *   data: txParams.data,\n *   chain: sepolia,\n * });\n * ```\n *\n * @remarks\n * **What happens on-chain:**\n * 1. Checks health factor won't drop below liquidation threshold (1.0)\n * 2. Mints debt tokens to user's proxy contract\n * 3. Transfers borrowed asset to receiver address\n * 4. Updates position debt\n * 5. Emits `Borrowed` event\n *\n * **Possible errors:**\n * - Borrow would make health factor < 1.0\n * - Insufficient collateral\n * - Reserve doesn't exist\n * - Position doesn't exist\n *\n * **Important:** Calculate safe borrow amount using `calculateHealthFactor()` to avoid liquidation.\n */\nexport function buildBorrowTx(\n  contractAddress: Address,\n  debtReserveId: bigint,\n  amount: bigint,\n  receiver: Address,\n): TransactionParams {\n  const data = encodeFunctionData({\n    abi: AaveIntegrationAdapterABI,\n    functionName: AAVE_FUNCTION_NAMES.BORROW,\n    args: [debtReserveId, amount, receiver],\n  });\n\n  return {\n    to: contractAddress,\n    data,\n  };\n}\n\n/**\n * Build transaction to repay debt on AAVE position.\n *\n * **Requires token approval** - user must approve adapter to spend debt token first.\n * Repays borrowed assets (partial or full repayment supported).\n *\n * @param contractAddress - AaveIntegrationAdapter contract address\n * @param borrower - Borrower's address (for self-repay, use connected wallet address)\n * @param debtReserveId - AAVE reserve ID for the debt asset\n * @param amount - Amount to repay in token units for a partial repay. For a\n *   FULL repay, pass `type(uint256).max` (the repay-all sentinel): the adapter\n *   resolves it to the position's fee-inclusive debt in the same transaction\n *   and pulls exactly that. Size the prior approval from\n *   `getPositionReserveTotalDebt()` plus ceiling-divided\n *   `FULL_REPAY_BUFFER_DIVISOR` headroom.\n * @returns Unsigned transaction parameters for execution with viem wallet\n *\n * @example\n * ```typescript\n * import { buildRepayTx } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n *\n * // Build repay transaction (self-repay)\n * const txParams = buildRepayTx(\n *   AAVE_ADAPTER,\n *   borrowerAddress, // Connected wallet address for self-repay\n *   USDC_RESERVE_ID,\n *   repayAmount\n * );\n *\n * const hash = await walletClient.sendTransaction({\n *   to: txParams.to,\n *   data: txParams.data,\n *   chain: sepolia,\n * });\n * ```\n *\n * @remarks\n * **What happens on-chain:**\n * 1. Transfers tokens from user to adapter (requires approval)\n * 2. Burns debt tokens from user's proxy\n * 3. Updates position debt\n * 4. Emits `Repaid` event\n *\n * **Possible errors:**\n * - Insufficient token approval\n * - User doesn't have enough tokens\n * - Repay amount exceeds debt\n * - Position doesn't exist\n */\nexport function buildRepayTx(\n  contractAddress: Address,\n  borrower: Address,\n  debtReserveId: bigint,\n  amount: bigint,\n): TransactionParams {\n  const data = encodeFunctionData({\n    abi: AaveIntegrationAdapterABI,\n    functionName: AAVE_FUNCTION_NAMES.REPAY,\n    args: [borrower, debtReserveId, amount],\n  });\n\n  return {\n    to: contractAddress,\n    data,\n  };\n}\n\n","/**\n * Aave Value Conversion Utilities\n *\n * Converts Aave on-chain values to human-readable numbers.\n */\n\nimport {\n  AAVE_BASE_CURRENCY_DECIMALS,\n  AAVE_BASE_CURRENCY_RAY_DECIMALS,\n  WAD_DECIMALS,\n} from \"../constants.js\";\n\n/**\n * Convert Aave base currency value to USD\n *\n * Aave uses 1e26 = $1 USD for collateral values.\n *\n * @param value - Value in Aave base currency (1e26 = $1)\n * @returns Value in USD\n */\nexport function aaveValueToUsd(value: bigint): number {\n  return Number(value) / 10 ** AAVE_BASE_CURRENCY_DECIMALS;\n}\n\n/**\n * Convert Aave RAY-scaled base currency value to USD\n *\n * Debt values use higher precision: 1e53 = $1 USD.\n *\n * @param value - Value in RAY-scaled base currency (1e53 = $1)\n * @returns Value in USD\n */\nexport function aaveRayValueToUsd(value: bigint): number {\n  return Number(value) / 10 ** AAVE_BASE_CURRENCY_RAY_DECIMALS;\n}\n\n/**\n * Convert Aave WAD value to number\n *\n * WAD is used for health factor and collateral factor (1e18 = 1.0).\n *\n * @param value - Value in WAD (1e18 = 1.0)\n * @returns Decimal number\n */\nexport function wadToNumber(value: bigint): number {\n  return Number(value) / 10 ** WAD_DECIMALS;\n}\n","/**\n * Aave Debt Utilities\n *\n * Shared utility functions for debt calculations.\n */\n\nimport type { AaveSpokeUserPosition } from \"../types.js\";\n\n/**\n * Check if a position has any debt based on Spoke position data.\n *\n * A position is considered to have debt if any of:\n * - drawnShares > 0 (borrowed principal)\n * - premiumShares > 0 (accrued interest shares)\n *\n * @param position - User position data from Spoke\n * @returns true if the position has any debt\n */\nexport function hasDebtFromPosition(position: AaveSpokeUserPosition): boolean {\n  return position.drawnShares > 0n || position.premiumShares > 0n;\n}\n","/**\n * Health Factor Utilities for Aave\n *\n * Health factor is calculated by Aave on-chain using oracle prices.\n * A health factor below 1.0 means the position can be liquidated.\n *\n * Status thresholds:\n * - no_debt: No active debt (null health factor)\n * - danger: < 1.0 (can be liquidated)\n * - warning: < HEALTH_FACTOR_WARNING_THRESHOLD (at risk)\n * - safe: >= HEALTH_FACTOR_WARNING_THRESHOLD (healthy)\n */\n\nimport { BPS_SCALE, HEALTH_FACTOR_WARNING_THRESHOLD } from \"../constants.js\";\n\nexport type HealthFactorStatus = \"safe\" | \"warning\" | \"danger\" | \"no_debt\";\n\n/**\n * Determine health factor status for UI display\n *\n * @param healthFactor - The health factor as a number (null if no debt)\n * @param hasDebt - Whether the position has active debt\n * @returns The status classification\n */\nexport function getHealthFactorStatus(\n  healthFactor: number | null,\n  hasDebt: boolean,\n): HealthFactorStatus {\n  if (!hasDebt) return \"no_debt\";\n  if (healthFactor === null) return \"safe\";\n  if (healthFactor < 1.0) return \"danger\";\n  if (healthFactor < HEALTH_FACTOR_WARNING_THRESHOLD) return \"warning\";\n  return \"safe\";\n}\n\n/**\n * Get health factor status from a numeric value.\n * Used for UI components that work with Infinity for no-debt scenarios.\n *\n * @param value - Health factor value (Infinity when no debt)\n * @returns The status classification\n */\nexport function getHealthFactorStatusFromValue(\n  value: number,\n): HealthFactorStatus {\n  const hasDebt = isFinite(value);\n  const healthFactor = isFinite(value) ? value : null;\n  return getHealthFactorStatus(healthFactor, hasDebt);\n}\n\n/**\n * Calculate health factor for an AAVE position.\n *\n * **Formula:** `HF = (Collateral × Liquidation Threshold) / Total Debt`\n *\n * Health factor determines liquidation risk:\n * - `>= 1.5` - Safe (green)\n * - `1.0 - 1.5` - Warning (amber)\n * - `< 1.0` - Danger, position can be liquidated (red)\n *\n * @param collateralValueUsd - Total collateral value in USD (as number, not bigint)\n * @param totalDebtUsd - Total debt value in USD (as number, not bigint)\n * @param liquidationThresholdBps - Liquidation threshold in basis points (e.g., `8000` = 80%)\n * @returns Health factor value (e.g., `1.5`), or `Infinity` if no debt\n *\n * @example\n * ```typescript\n * import { calculateHealthFactor, HEALTH_FACTOR_WARNING_THRESHOLD } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n *\n * // User has $10,000 BTC collateral, $5,000 debt, 80% LT\n * const hf = calculateHealthFactor(10000, 5000, 8000);\n * // Result: 1.6 (safe to borrow more)\n *\n * if (hf < 1.0) {\n *   console.error(\"Position can be liquidated!\");\n * } else if (hf < HEALTH_FACTOR_WARNING_THRESHOLD) {\n *   console.warn(\"Position at risk, consider repaying\");\n * } else {\n *   console.log(\"Position is safe\");\n * }\n * ```\n *\n * @remarks\n * **Before borrowing:**\n * Use this to calculate resulting health factor and ensure it stays above safe threshold.\n *\n * **Unit conversions:**\n * - Convert AAVE base currency (1e26) to USD by dividing by 1e26\n * - Use `aaveValueToUsd()` helper for automatic conversion\n */\nexport function calculateHealthFactor(\n  collateralValueUsd: number,\n  totalDebtUsd: number,\n  liquidationThresholdBps: number,\n): number {\n  if (totalDebtUsd <= 0) return Infinity;\n  return (\n    (collateralValueUsd * (liquidationThresholdBps / BPS_SCALE)) / totalDebtUsd\n  );\n}\n","/**\n * Cascade Liquidation Simulation\n *\n * Simulates multi-group liquidation cascades for Aave positions backed by\n * indivisible BTC vaults (UTXOs). Each liquidation event seizes a prefix of\n * vaults until the target seizure is covered, then debt is reduced and the\n * cascade continues with remaining vaults.\n *\n * Used by the optimizer to score different vault orderings by how much\n * collateral survives the cascade.\n */\n\n/**\n * Minimal vault shape for cascade simulation.\n * UI layers extend this with display fields (e.g. `name`).\n */\nexport interface CascadeVault {\n  id: string;\n  btc: number;\n}\n\n/** 1% tolerance for prefix walk coverage — avoids cliff flip at boundary */\nexport const SEIZURE_TOL = 0.01;\n\n/** Circuit breaker for group cascade loop */\nexport const MAX_GROUPS = 20;\n\n/** Minimum debt threshold to continue cascade (avoids infinite loop on dust) */\nexport const MIN_DEBT_THRESHOLD = 0.01;\n\n/**\n * Prefix walk: consume vaults front-to-back until target seizure is covered.\n * Returns the vaults in the first liquidation group.\n */\nexport function getGroup1FromOrder<T extends CascadeVault>(\n  order: T[],\n  seizedFraction: number,\n  seizureTol: number,\n): T[] {\n  if (order.length === 0) return [];\n  const totalBtc = order.reduce((s, v) => s + v.btc, 0);\n  const coverThreshold = totalBtc * seizedFraction * (1 - seizureTol);\n  let prefixSum = 0;\n  let i = 0;\n  while (i < order.length && prefixSum < coverThreshold) {\n    prefixSum += order[i].btc;\n    i++;\n  }\n  return order.slice(0, i);\n}\n\n/**\n * Simulate one liquidation group, returning updated debt and remaining BTC.\n * Handles both safe (non-last) and full (last) liquidation paths.\n */\nfunction simulateOneGroup(\n  seizedBtc: number,\n  totalBtc: number,\n  debt: number,\n  isLastGroup: boolean,\n  seizedFraction: number,\n  CF: number,\n  THF: number,\n  maxLB: number,\n  expectedHF: number,\n): { debtAfter: number; btcAfter: number } {\n  const liqPenalty = maxLB * CF;\n  const pLiq = debt / (totalBtc * CF);\n  const targetSeizure = totalBtc * seizedFraction;\n  const overSeizureBtc = Math.max(0, seizedBtc - targetSeizure);\n  const denominator = THF - liqPenalty;\n  const debtToRepay =\n    denominator === 0 ? debt : debt * ((THF - expectedHF) / denominator);\n\n  let debtAfter: number;\n  if (isLastGroup) {\n    debtAfter = 0;\n  } else {\n    const overSeizureVal = (overSeizureBtc * pLiq) / maxLB;\n    const fairnessDebtRepay = Math.min(overSeizureVal, debt - debtToRepay);\n    debtAfter = Math.max(0, debt - debtToRepay - fairnessDebtRepay);\n  }\n  return { debtAfter, btcAfter: Math.max(0, totalBtc - seizedBtc) };\n}\n\n/**\n * Simulate full liquidation cascade with debt model.\n *\n * PRIMARY score:  sumBtcAfterEvents — sum of BTC remaining after every event.\n *                 Captures how much collateral survives at each stage.\n * TIEBREAKER:     btcAfterG1 — BTC remaining after the first (most likely) event.\n */\nexport function simulateCascade<T extends CascadeVault>(\n  order: T[],\n  totalDebt: number,\n  seizedFraction: number,\n  seizureTol: number,\n  CF: number,\n  THF: number,\n  maxLB: number,\n  expectedHF: number,\n): { sumBtcAfterEvents: number; btcAfterG1: number } {\n  let remaining = [...order];\n  let debt = totalDebt;\n  const initialTotalBtc = order.reduce((s, v) => s + v.btc, 0);\n  let btcAfterG1 = -1;\n  let sumBtcAfterEvents = 0;\n  let groupCount = 0;\n\n  while (\n    remaining.length > 0 &&\n    debt > MIN_DEBT_THRESHOLD &&\n    groupCount < MAX_GROUPS\n  ) {\n    const totalBtc = remaining.reduce((s, v) => s + v.btc, 0);\n    const coverThreshold = totalBtc * seizedFraction * (1 - seizureTol);\n    let prefixSum = 0;\n    let i = 0;\n    while (i < remaining.length && prefixSum < coverThreshold) {\n      prefixSum += remaining[i].btc;\n      i++;\n    }\n    const isLastGroup = i >= remaining.length;\n    const { debtAfter } = simulateOneGroup(\n      prefixSum,\n      totalBtc,\n      debt,\n      isLastGroup,\n      seizedFraction,\n      CF,\n      THF,\n      maxLB,\n      expectedHF,\n    );\n    remaining = remaining.slice(i);\n    debt = debtAfter;\n    const btcNow = totalBtc - prefixSum;\n    sumBtcAfterEvents += btcNow;\n    if (btcAfterG1 < 0) btcAfterG1 = btcNow;\n    groupCount++;\n  }\n\n  return {\n    sumBtcAfterEvents,\n    btcAfterG1:\n      btcAfterG1 < 0 ? initialTotalBtc : btcAfterG1,\n  };\n}\n","/**\n * Optimal Vault Ordering for Liquidation Protection\n *\n * Finds the vault ordering that maximizes collateral surviving a multi-group\n * liquidation cascade.\n *\n * The optimizer is a bitmask DP over seized subsets. 2^n memory and 3^n work\n * blow up past `MAX_DP_N` vaults, so for larger sets the optimizer falls back to\n * a largest-first heuristic — the suggested order is then no longer guaranteed\n * optimal, and `calculate()` surfaces a `too-many-vaults` warning to say so.\n */\n\nimport {\n  simulateCascade,\n  type CascadeVault,\n} from \"./cascadeSimulation.js\";\n\n/**\n * Hard cap on vault count for the bitmask DP optimizer. 2^n memory + 3^n work\n * blow up past this. For n > MAX_DP_N the optimizer falls back to a\n * largest-first heuristic. Benchmark: n=18 ≈ 720ms, n=20 ≈ 5.8s — anything past\n * n=17 is too slow for interactive UI, so we cap here.\n */\nexport const MAX_DP_N = 17;\n\n/**\n * Main optimizer: bitmask DP over seized subsets.\n *\n * State: T = bitmask of vaults that have already been seized.\n * Transition: for each valid \"last group\" G ⊆ T, dp[T] = dp[T\\G] + btcAfter\n *   where btcAfter = totalBtc − btcOf(T)   (BTC remaining after T is seized).\n * Validation: btcOf(G) must cover target seizure at the moment G fires, i.e.\n *   btcOf(G) ≥ (totalBtc − btcOf(T\\G)) × seizedFraction × (1 − seizureTol).\n *\n * Complexity: O(3^n) — the subset-of-subset enumeration visits exactly\n * Σ C(n,k) × 2^k = 3^n state-transition pairs. Single pass, no refinement loop.\n *\n * Objective: maximize sumBtcAfterEvents assuming all events fire. Debt is not\n * part of the DP state — it is used only when computing final metrics via\n * simulateCascade() on the reconstructed order.\n */\nexport function computeOptimalOrder<T extends CascadeVault>(\n  vaults: T[],\n  totalDebt: number,\n  seizedFraction: number,\n  seizureTol: number,\n  CF: number,\n  THF: number,\n  maxLB: number,\n  expectedHF: number,\n): { order: T[]; sumBtcAfterEvents: number; btcAfterG1: number } {\n  const n = vaults.length;\n  if (n === 0) return { order: [], sumBtcAfterEvents: 0, btcAfterG1: 0 };\n  if (n === 1) {\n    const sim = simulateCascade(\n      vaults,\n      totalDebt,\n      seizedFraction,\n      seizureTol,\n      CF,\n      THF,\n      maxLB,\n      expectedHF,\n    );\n    return { order: [...vaults], ...sim };\n  }\n\n  // Safety cap: 2^n memory and 3^n work blow up past MAX_DP_N vaults. Real\n  // users have far fewer — each vault is a separate peg-in with a fixed fee.\n  // For the unlikely n > MAX_DP_N, fall back to a largest-first heuristic;\n  // calculate() surfaces a 'too-many-vaults' warning to flag the loss of the\n  // optimality guarantee.\n  if (n > MAX_DP_N) {\n    const order = [...vaults].sort((a, b) => b.btc - a.btc);\n    const sim = simulateCascade(\n      order,\n      totalDebt,\n      seizedFraction,\n      seizureTol,\n      CF,\n      THF,\n      maxLB,\n      expectedHF,\n    );\n    return { order, ...sim };\n  }\n\n  const N = 1 << n;\n  const totalBtc = vaults.reduce((s, v) => s + v.btc, 0);\n  const coverFraction = seizedFraction * (1 - seizureTol);\n\n  // Precompute btcOf[T] — standard bitmask sum trick in O(2^n).\n  const btcOf = new Float64Array(N);\n  for (let T = 1; T < N; T++) {\n    const lsb = T & -T;\n    const bit = 31 - Math.clz32(lsb);\n    btcOf[T] = btcOf[T ^ lsb] + vaults[bit].btc;\n  }\n\n  // dpSum[T] = best sumBtcAfterEvents to reach state T (T = seized subset).\n  // dpG1[T]  = among the paths achieving dpSum[T], the largest BTC remaining\n  //            after the FIRST event — the tiebreaker, so a tie on total\n  //            cascade survival prefers the order that survives the first\n  //            (most likely) liquidation event best. Matches what calculate()\n  //            uses to decide whether a reorder is worth suggesting.\n  // prev[T]  = the \"last group\" G chosen when reaching T (for reconstruction).\n  const dpSum = new Float64Array(N);\n  const dpG1 = new Float64Array(N);\n  const prev = new Int32Array(N);\n  dpSum.fill(-Infinity);\n  prev.fill(-1);\n  dpSum[0] = 0;\n  // dpG1[0] is never read: when Tprev === 0 the first event is scored directly.\n\n  const EPS = 1e-9;\n\n  for (let T = 1; T < N; T++) {\n    const btcAfter = totalBtc - btcOf[T]; // BTC remaining after the last group brings us to state T\n    let bestSum = -Infinity;\n    let bestG1 = -Infinity;\n    let bestG = -1;\n\n    // Enumerate non-empty subsets G ⊆ T. Across all T this visits exactly 3^n pairs.\n    for (let G = T; G > 0; G = (G - 1) & T) {\n      const Tprev = T ^ G;\n      const prevSum = dpSum[Tprev];\n      if (prevSum === -Infinity) continue;\n\n      // Validate: G covers target seizure when fired from state Tprev.\n      const remainingBeforeG = totalBtc - btcOf[Tprev];\n      if (btcOf[G] < remainingBeforeG * coverFraction) continue;\n\n      const candSum = prevSum + btcAfter;\n      // First-event remainder along this path: if Tprev is empty, G itself is\n      // the first event; otherwise it was fixed earlier in the subpath.\n      const candG1 = Tprev === 0 ? totalBtc - btcOf[G] : dpG1[Tprev];\n\n      if (\n        candSum > bestSum + EPS ||\n        (Math.abs(candSum - bestSum) <= EPS && candG1 > bestG1 + EPS)\n      ) {\n        bestSum = candSum;\n        bestG1 = candG1;\n        bestG = G;\n      }\n    }\n\n    dpSum[T] = bestSum;\n    dpG1[T] = bestG1;\n    prev[T] = bestG;\n  }\n\n  // Reconstruct firing order by walking prev[] from fullMask back to 0.\n  const fullMask = N - 1;\n  const groupsReversed: T[][] = [];\n  for (let T = fullMask; T > 0; ) {\n    const G = prev[T];\n    if (G === -1) break; // unreachable chain — fall back below\n    const gVaults: T[] = [];\n    for (let bit = 0; bit < n; bit++) {\n      if (G & (1 << bit)) gVaults.push(vaults[bit]);\n    }\n    gVaults.sort((a, b) => b.btc - a.btc); // canonical within-group order\n    groupsReversed.push(gVaults);\n    T ^= G;\n  }\n  groupsReversed.reverse();\n  const order = groupsReversed.flat();\n\n  // Reconstruction safety: if the chain is incomplete for any reason, fall back\n  // to largest-first rather than throwing (calculate() runs in the notification\n  // render and must not crash). This path is a DP-invariant violation that\n  // should never occur for valid input — log it so a regression is observable\n  // even though the fallback keeps the UI alive and the result well-shaped.\n  if (order.length !== n) {\n    console.error(\n      `computeOptimalOrder: DP reconstruction produced ${order.length}/${n} vaults; ` +\n        `falling back to largest-first. This indicates a DP-invariant regression.`,\n    );\n    const fallback = [...vaults].sort((a, b) => b.btc - a.btc);\n    const sim = simulateCascade(\n      fallback,\n      totalDebt,\n      seizedFraction,\n      seizureTol,\n      CF,\n      THF,\n      maxLB,\n      expectedHF,\n    );\n    return { order: fallback, ...sim };\n  }\n\n  // Compute real metrics via cascade simulation (debt-aware, matches what\n  // calculate() produces for the group breakdown shown in the UI).\n  const sim = simulateCascade(\n    order,\n    totalDebt,\n    seizedFraction,\n    seizureTol,\n    CF,\n    THF,\n    maxLB,\n    expectedHF,\n  );\n  return {\n    order,\n    sumBtcAfterEvents: sim.sumBtcAfterEvents,\n    btcAfterG1: sim.btcAfterG1,\n  };\n}\n","/**\n * Vault Split Utilities for Aave Liquidation Protection\n *\n * BTC vaults are indivisible UTXOs. During liquidation, the protocol seizes\n * whole vaults as a prefix of the borrower's ordered vault list until the\n * target seizure amount is covered. Splitting deposits into 2 optimally-sized\n * vaults (sacrificial + protected) minimizes over-seizure loss.\n *\n * The sacrificial vault (index 0) is sized to cover the expected target seizure\n * plus a safety margin. The protected vault (index 1) holds the remainder and\n * survives liquidation.\n *\n * Seizure formula (from Aave v4 Section 4.2):\n * ```\n * liq_penalty = LB × CF\n * debt_to_repay = total_debt × (THF - current_HF) / (THF - liq_penalty)\n * target_seizure = debt_to_repay × LB\n * ```\n */\n\nconst MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);\n\n/**\n * Effective dust threshold for HTLC outputs in satoshis.\n *\n * Standard P2TR dust is 330 sats, but HTLC scripts are larger than a standard\n * P2TR key-path spend. A conservative estimate for an HTLC script-path output\n * is ~2000 sats. This is a defense-in-depth check — in practice, minDeposit\n * from the on-chain contract is orders of magnitude larger.\n */\nconst HTLC_EFFECTIVE_DUST_THRESHOLD = 2000n;\n\nfunction assertSafePrecision(value: bigint, name: string): void {\n  if (value > MAX_SAFE_BIGINT) {\n    throw new RangeError(\n      `${name} (${value}) exceeds Number.MAX_SAFE_INTEGER; precision would be lost`,\n    );\n  }\n}\n\n/**\n * Parameters for computing the optimal vault split.\n */\nexport interface OptimalSplitParams {\n  /** Total deposit amount in satoshis */\n  totalBtc: bigint;\n  /** Collateral factor (e.g. 0.75 for 75%) */\n  CF: number;\n  /** Liquidation bonus (e.g. 1.05 for 5% bonus) */\n  LB: number;\n  /** Target health factor (e.g. 1.10) */\n  THF: number;\n  /** Expected health factor at liquidation (e.g. 0.95) */\n  expectedHF: number;\n  /** Safety margin multiplier for the sacrificial vault (e.g. 1.05 for 5% buffer) */\n  safetyMargin: number;\n}\n\n/**\n * Result of the optimal vault split computation.\n */\nexport interface OptimalSplitResult {\n  /** Sacrificial vault amount in satoshis (index 0, seized first) */\n  sacrificialVault: bigint;\n  /** Protected vault amount in satoshis (index 1, survives liquidation) */\n  protectedVault: bigint;\n  /** Fraction of collateral that would be seized (0–1) */\n  seizedFraction: number;\n  /** Raw target seizure amount in satoshis (before safety margin) */\n  targetSeizureBtc: bigint;\n}\n\n/**\n * Parameters for computing the minimum deposit required for a split.\n */\nexport interface MinDepositForSplitParams {\n  /** Minimum peg-in amount in satoshis */\n  minPegin: bigint;\n  /** Seized fraction (0–1), from computeOptimalSplit or computeSeizedFraction */\n  seizedFraction: number;\n  /** Safety margin multiplier (e.g. 1.05) */\n  safetyMargin: number;\n}\n\n/**\n * Compute the fraction of collateral that would be seized during liquidation,\n * returning both the raw (unclamped) and clamped values.\n *\n * The raw value is useful for detecting unusual protocol parameter combinations\n * (values outside [0, 1] indicate something unexpected).\n *\n * Formula:\n * ```\n * liq_penalty = LB × CF\n * seized_fraction = CF × (THF - expectedHF) / (THF - liq_penalty) × LB / expectedHF\n * ```\n *\n * @param CF - Collateral factor (e.g. 0.75)\n * @param LB - Liquidation bonus (e.g. 1.05)\n * @param THF - Target health factor (e.g. 1.10)\n * @param expectedHF - Expected health factor at liquidation (e.g. 0.95)\n * @returns Both the raw seized fraction and the clamped [0, 1] value\n */\nexport function computeSeizedFractionDetailed(\n  CF: number,\n  LB: number,\n  THF: number,\n  expectedHF: number,\n): { seizedFraction: number; seizedFractionRaw: number } {\n  // HF ≤ 0 means position is fully underwater — full seizure\n  if (expectedHF <= 0) {\n    return { seizedFraction: 1, seizedFractionRaw: Infinity };\n  }\n\n  const liqPenalty = LB * CF;\n\n  // If THF <= liq_penalty, full liquidation is inevitable\n  if (THF <= liqPenalty) {\n    return { seizedFraction: 1, seizedFractionRaw: Infinity };\n  }\n\n  // Floating-point errors here are ~1e-15, negligible relative to the 5%\n  // safety margin applied by callers (computeOptimalSplit).\n  const seizedFractionRaw =\n    ((CF * (THF - expectedHF)) / (THF - liqPenalty)) * (LB / expectedHF);\n\n  return {\n    seizedFraction: Math.max(0, Math.min(1, seizedFractionRaw)),\n    seizedFractionRaw,\n  };\n}\n\n/**\n * Compute the fraction of collateral that would be seized during liquidation.\n *\n * @param CF - Collateral factor (e.g. 0.75)\n * @param LB - Liquidation bonus (e.g. 1.05)\n * @param THF - Target health factor (e.g. 1.10)\n * @param expectedHF - Expected health factor at liquidation (e.g. 0.95)\n * @returns Seized fraction clamped to [0, 1]\n */\nexport function computeSeizedFraction(\n  CF: number,\n  LB: number,\n  THF: number,\n  expectedHF: number,\n): number {\n  return computeSeizedFractionDetailed(CF, LB, THF, expectedHF).seizedFraction;\n}\n\n/**\n * Compute the optimal split between a sacrificial vault and a protected vault.\n *\n * The sacrificial vault (index 0) is sized to cover the target seizure amount\n * plus a safety margin. The protected vault (index 1) holds the remainder.\n *\n * @param params - Split parameters including total BTC, risk params, and safety margin\n * @returns Split result with vault sizes, seized fraction, and target seizure\n *\n * @example\n * ```typescript\n * import { computeOptimalSplit } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n *\n * const result = computeOptimalSplit({\n *   totalBtc: 1_000_000_000n, // 10 BTC in sats\n *   CF: 0.75,\n *   LB: 1.05,\n *   THF: 1.10,\n *   expectedHF: 0.95,\n *   safetyMargin: 1.05,\n * });\n * // result.sacrificialVault ≈ 418_000_000n (4.18 BTC)\n * // result.protectedVault ≈ 582_000_000n (5.82 BTC)\n * ```\n */\nexport function computeOptimalSplit(\n  params: OptimalSplitParams,\n): OptimalSplitResult {\n  const { totalBtc, CF, LB, THF, expectedHF, safetyMargin } = params;\n\n  if (totalBtc <= 0n) {\n    return {\n      sacrificialVault: 0n,\n      protectedVault: 0n,\n      seizedFraction: 0,\n      targetSeizureBtc: 0n,\n    };\n  }\n\n  assertSafePrecision(totalBtc, \"totalBtc\");\n\n  const seizedFraction = computeSeizedFraction(CF, LB, THF, expectedHF);\n\n  const totalBtcNum = Number(totalBtc);\n  const targetSeizureBtc = BigInt(Math.ceil(totalBtcNum * seizedFraction));\n\n  const sacrificialRaw = BigInt(\n    Math.ceil(totalBtcNum * seizedFraction * safetyMargin),\n  );\n  const sacrificialVault =\n    sacrificialRaw > totalBtc ? totalBtc : sacrificialRaw;\n  const protectedVault = totalBtc - sacrificialVault;\n\n  // If either vault is non-zero but below the effective dust threshold for\n  // HTLC outputs, the split is not viable — return zeroed vaults so the\n  // caller treats this as non-splittable. We avoid throwing because this\n  // function is called during render (useMemo) and throwing would crash\n  // the component instead of showing validation feedback.\n  if (\n    (sacrificialVault > 0n &&\n      sacrificialVault < HTLC_EFFECTIVE_DUST_THRESHOLD) ||\n    (protectedVault > 0n && protectedVault < HTLC_EFFECTIVE_DUST_THRESHOLD)\n  ) {\n    return {\n      sacrificialVault: 0n,\n      protectedVault: 0n,\n      seizedFraction,\n      targetSeizureBtc: 0n,\n    };\n  }\n\n  return {\n    sacrificialVault,\n    protectedVault,\n    seizedFraction,\n    targetSeizureBtc,\n  };\n}\n\n/**\n * Compute the minimum total deposit required for a 2-vault split.\n *\n * Both vaults must be at least `minPegin` satoshis. This function returns\n * the minimum total deposit where both the sacrificial and protected vaults\n * would meet the minimum peg-in requirement.\n *\n * @param params - Parameters including minimum peg-in, seized fraction, and safety margin\n * @returns Minimum total deposit in satoshis. Returns 0n in two cases:\n *   - `seizedFraction * safetyMargin >= 1`: split impossible (sacrificial vault would consume entire deposit)\n *   - `seizedFraction <= 0`: split not useful (no seizure expected at this health factor)\n *\n * @example\n * ```typescript\n * import { computeMinDepositForSplit } from \"@babylonlabs-io/ts-sdk/tbv/integrations/aave\";\n *\n * const minDeposit = computeMinDepositForSplit({\n *   minPegin: 50_000n, // 0.0005 BTC\n *   seizedFraction: 0.398,\n *   safetyMargin: 1.05,\n * });\n * ```\n */\nexport function computeMinDepositForSplit(\n  params: MinDepositForSplitParams,\n): bigint {\n  const { minPegin, seizedFraction, safetyMargin } = params;\n\n  assertSafePrecision(minPegin, \"minPegin\");\n\n  const sacrificialShare = seizedFraction * safetyMargin;\n\n  // If sacrificial vault would consume the entire deposit, split is not possible\n  if (sacrificialShare >= 1) {\n    return 0n;\n  }\n\n  // If seized fraction is effectively zero, split is not useful\n  if (sacrificialShare <= 0) {\n    return 0n;\n  }\n\n  // Minimum total so the protected vault (smaller share) >= minPegin\n  const protectedShare = 1 - sacrificialShare;\n  const minFromProtected = Math.ceil(Number(minPegin) / protectedShare);\n\n  // Minimum total so the sacrificial vault >= minPegin\n  const minFromSacrificial = Math.ceil(Number(minPegin) / sacrificialShare);\n\n  return BigInt(Math.max(minFromProtected, minFromSacrificial));\n}\n"],"names":["AAVE_FUNCTION_NAMES","BPS_SCALE","AAVE_BASE_CURRENCY_DECIMALS","AAVE_BASE_CURRENCY_RAY_DECIMALS","WAD_DECIMALS","HEALTH_FACTOR_WARNING_THRESHOLD","MIN_HEALTH_FACTOR_FOR_BORROW","FULL_REPAY_BUFFER_DIVISOR","NO_POSITION_ERROR_NAME","MAX_ERROR_CAUSE_DEPTH","isNoPositionRevert","err","current","depth","data","cause","getPosition","publicClient","contractAddress","user","result","AaveIntegrationAdapterABI","position","zeroAddress","getPositionSizeParams","maxPositionBTC","maxVaultsPerPosition","mapPositionResult","mapAccountDataResult","getUserAccountData","spokeAddress","userAddress","AaveSpokeABI","getUserPositionAndAccountData","reserveId","positionResult","accountDataResult","getUserPosition","getUserTotalDebt","getUserPositions","reserveIds","getUserTotalDebts","getReserve","getTargetHealthFactor","getDynamicReserveConfig","dynamicConfigKey","getPositionReserveTotalDebt","proxyContract","AaveAdapterPositionProxyABI","getOracleAddress","getReservesPrices","oracleAddress","AaveOracleABI","getReservesPricesSafe","results","error","i","priceRaw","getAssetDrawnRatesSafe","requests","hub","assetId","AaveHubABI","buildReorderVaultsTx","permutedVaultIds","encodeFunctionData","buildWithdrawCollateralsTx","vaultIds","buildBorrowTx","debtReserveId","amount","receiver","buildRepayTx","borrower","aaveValueToUsd","value","aaveRayValueToUsd","wadToNumber","hasDebtFromPosition","getHealthFactorStatus","healthFactor","hasDebt","getHealthFactorStatusFromValue","calculateHealthFactor","collateralValueUsd","totalDebtUsd","liquidationThresholdBps","SEIZURE_TOL","MAX_GROUPS","MIN_DEBT_THRESHOLD","getGroup1FromOrder","order","seizedFraction","seizureTol","coverThreshold","s","v","prefixSum","simulateOneGroup","seizedBtc","totalBtc","debt","isLastGroup","CF","THF","maxLB","expectedHF","liqPenalty","pLiq","targetSeizure","overSeizureBtc","denominator","debtToRepay","debtAfter","overSeizureVal","fairnessDebtRepay","simulateCascade","totalDebt","remaining","initialTotalBtc","btcAfterG1","sumBtcAfterEvents","groupCount","btcNow","MAX_DP_N","computeOptimalOrder","vaults","n","sim","a","b","N","coverFraction","btcOf","T","lsb","bit","dpSum","dpG1","prev","EPS","btcAfter","bestSum","bestG1","bestG","G","Tprev","prevSum","remainingBeforeG","candSum","candG1","fullMask","groupsReversed","gVaults","fallback","MAX_SAFE_BIGINT","HTLC_EFFECTIVE_DUST_THRESHOLD","assertSafePrecision","name","computeSeizedFractionDetailed","LB","seizedFractionRaw","computeSeizedFraction","computeOptimalSplit","params","safetyMargin","totalBtcNum","targetSeizureBtc","sacrificialRaw","sacrificialVault","protectedVault","computeMinDepositForSplit","minPegin","sacrificialShare","protectedShare","minFromProtected","minFromSacrificial"],"mappings":"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"}