origin-morpho-utils 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/abi/erc20.ts","../src/abi/irm-adaptive-curve.ts","../src/abi/meta-morpho.ts","../src/abi/morpho.ts","../src/fetch.ts","../src/addresses.ts","../src/deposit-impact.ts","../src/withdrawal-impact.ts","../src/find-max.ts","../src/find-max-impact.ts","../src/rpc.ts"],"sourcesContent":["/** Minimal ERC20 ABI — decimals + symbol. */\nexport const erc20Abi = [\n {\n inputs: [],\n name: 'decimals',\n outputs: [{ name: '', type: 'uint8' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [],\n name: 'symbol',\n outputs: [{ name: '', type: 'string' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const\n","/** Minimal IRM Adaptive Curve ABI — rateAtTarget only. */\nexport const irmAdaptiveCurveAbi = [\n {\n inputs: [{ name: 'id', type: 'bytes32' }],\n name: 'rateAtTarget',\n outputs: [{ name: '', type: 'int256' }],\n stateMutability: 'view',\n type: 'function',\n },\n] as const\n","/** Minimal MetaMorpho (ERC4626 vault) ABI — only view functions used by fetchVaultMarketsViem. */\nexport const metaMorphoAbi = [\n {\n inputs: [],\n name: 'supplyQueueLength',\n outputs: [{ name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [],\n name: 'withdrawQueueLength',\n outputs: [{ name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [],\n name: 'totalAssets',\n outputs: [{ name: 'totalManagedAssets', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [{ name: '', type: 'uint256' }],\n name: 'supplyQueue',\n outputs: [{ name: '', type: 'bytes32' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [{ name: '', type: 'uint256' }],\n name: 'withdrawQueue',\n outputs: [{ name: '', type: 'bytes32' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [{ name: '', type: 'bytes32' }],\n name: 'config',\n outputs: [\n { name: 'cap', type: 'uint184' },\n { name: 'enabled', type: 'bool' },\n { name: 'removableAt', type: 'uint64' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n] as const\n","/** Minimal Morpho Blue ABI — only view functions used by fetchVaultMarketsViem. */\nexport const morphoAbi = [\n {\n inputs: [{ name: 'id', type: 'bytes32' }],\n name: 'market',\n outputs: [\n { name: 'totalSupplyAssets', type: 'uint128' },\n { name: 'totalSupplyShares', type: 'uint128' },\n { name: 'totalBorrowAssets', type: 'uint128' },\n { name: 'totalBorrowShares', type: 'uint128' },\n { name: 'lastUpdate', type: 'uint128' },\n { name: 'fee', type: 'uint128' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [\n { name: 'id', type: 'bytes32' },\n { name: 'user', type: 'address' },\n ],\n name: 'position',\n outputs: [\n { name: 'supplyShares', type: 'uint256' },\n { name: 'borrowShares', type: 'uint128' },\n { name: 'collateral', type: 'uint128' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [{ name: 'id', type: 'bytes32' }],\n name: 'idToMarketParams',\n outputs: [\n { name: 'loanToken', type: 'address' },\n { name: 'collateralToken', type: 'address' },\n { name: 'oracle', type: 'address' },\n { name: 'irm', type: 'address' },\n { name: 'lltv', type: 'uint256' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n] as const\n","/**\n * Market data fetching for Morpho Blue / MetaMorpho vaults using viem.\n */\nimport type { PublicClient } from 'viem'\nimport { getAddress } from 'viem'\n\nimport { erc20Abi } from './abi/erc20.js'\nimport { irmAdaptiveCurveAbi } from './abi/irm-adaptive-curve.js'\nimport { metaMorphoAbi } from './abi/meta-morpho.js'\nimport { morphoAbi } from './abi/morpho.js'\nimport { weightedVaultApyDetailed } from './math.js'\nimport type { MarketForApy, VaultApyResult, VaultFetchResult } from './types.js'\n\nconst ADDRESS_ZERO = '0x0000000000000000000000000000000000000000'\n\n/**\n * Fetch all market data for a MetaMorpho vault using viem multicall.\n *\n * Returns market data needed for APY computation and simulation,\n * plus the vault's idle (undeployed) assets.\n */\nexport async function fetchVaultMarketsViem(\n client: PublicClient,\n vaultAddress: string,\n morphoAddress: string,\n): Promise<VaultFetchResult> {\n const vault = getAddress(vaultAddress)\n const morpho = getAddress(morphoAddress)\n\n // 1. Queue lengths + total assets\n const [supplyLen, withdrawLen, totalAssets] = await client.multicall({\n contracts: [\n { address: vault, abi: metaMorphoAbi, functionName: 'supplyQueueLength' },\n { address: vault, abi: metaMorphoAbi, functionName: 'withdrawQueueLength' },\n { address: vault, abi: metaMorphoAbi, functionName: 'totalAssets' },\n ],\n allowFailure: false,\n })\n\n const supplyLenN = Number(supplyLen)\n const withdrawLenN = Number(withdrawLen)\n if (supplyLenN === 0 && withdrawLenN === 0) return { markets: [], idleAssets: 0n }\n\n // 2. Market IDs from both queues\n const queueCalls = [\n ...Array.from({ length: supplyLenN }, (_, i) => ({\n address: vault,\n abi: metaMorphoAbi,\n functionName: 'supplyQueue' as const,\n args: [BigInt(i)] as const,\n })),\n ...Array.from({ length: withdrawLenN }, (_, i) => ({\n address: vault,\n abi: metaMorphoAbi,\n functionName: 'withdrawQueue' as const,\n args: [BigInt(i)] as const,\n })),\n ]\n\n const queueResults = await client.multicall({ contracts: queueCalls, allowFailure: false })\n const supplyIds = queueResults.slice(0, supplyLenN) as `0x${string}`[]\n const withdrawIds = queueResults.slice(supplyLenN) as `0x${string}`[]\n\n // 3. Union market IDs + withdraw queue ordering\n const supplySet = new Set(supplyIds)\n const withdrawSet = new Set(withdrawIds)\n const withdrawQueueOrder = new Map<string, number>()\n ;(withdrawIds as string[]).forEach((id, i) => withdrawQueueOrder.set(id, i))\n const allIds = [...new Set([...supplyIds, ...withdrawIds])]\n\n if (allIds.length === 0) return { markets: [], idleAssets: 0n }\n\n // 4. Fetch market state, position, params, config (parallel multicalls)\n const [marketStates, positions, marketParams, configs] = await Promise.all([\n client.multicall({\n contracts: allIds.map((id) => ({\n address: morpho,\n abi: morphoAbi,\n functionName: 'market' as const,\n args: [id] as const,\n })),\n allowFailure: false,\n }),\n client.multicall({\n contracts: allIds.map((id) => ({\n address: morpho,\n abi: morphoAbi,\n functionName: 'position' as const,\n args: [id, vault] as const,\n })),\n allowFailure: false,\n }),\n client.multicall({\n contracts: allIds.map((id) => ({\n address: morpho,\n abi: morphoAbi,\n functionName: 'idToMarketParams' as const,\n args: [id] as const,\n })),\n allowFailure: false,\n }),\n client.multicall({\n contracts: allIds.map((id) => ({\n address: vault,\n abi: metaMorphoAbi,\n functionName: 'config' as const,\n args: [id] as const,\n })),\n allowFailure: false,\n }),\n ])\n\n // 5. Per-market IRM + decimals + token symbols (single multicall batch)\n type MulticallContract = Parameters<PublicClient['multicall']>[0]['contracts'][number]\n const irmAndDecimalCalls: MulticallContract[] = marketParams.flatMap(\n ([loanToken, collateralToken, , irm]: readonly [string, string, string, string, bigint], i: number) => {\n const hasIrm = irm && irm.toLowerCase() !== ADDRESS_ZERO\n const hasCollateral = collateralToken && collateralToken.toLowerCase() !== ADDRESS_ZERO\n const calls: (MulticallContract | null)[] = [\n hasIrm\n ? {\n address: getAddress(irm),\n abi: irmAdaptiveCurveAbi,\n functionName: 'rateAtTarget' as const,\n args: [allIds[i]] as const,\n }\n : null,\n { address: getAddress(loanToken), abi: erc20Abi, functionName: 'decimals' as const },\n { address: getAddress(loanToken), abi: erc20Abi, functionName: 'symbol' as const },\n hasCollateral ? { address: getAddress(collateralToken), abi: erc20Abi, functionName: 'symbol' as const } : null,\n ]\n return calls.filter((c): c is MulticallContract => c !== null)\n },\n )\n\n const irmAndDecimalResults = await client.multicall({\n contracts: irmAndDecimalCalls as readonly MulticallContract[],\n allowFailure: true,\n })\n\n // Map results back, accounting for skipped IRM and collateral symbol calls\n let resultIdx = 0\n const ratesAtTarget: bigint[] = []\n const decimals: number[] = []\n const loanSymbols: string[] = []\n const collateralSymbols: string[] = []\n for (const [, collateralToken, , irm] of marketParams as unknown as readonly [\n string,\n string,\n string,\n string,\n bigint,\n ][]) {\n const hasIrm = irm && irm.toLowerCase() !== ADDRESS_ZERO\n const hasCollateral = collateralToken && collateralToken.toLowerCase() !== ADDRESS_ZERO\n if (hasIrm) {\n const r = irmAndDecimalResults[resultIdx++]\n const rate = r.status === 'success' ? (r.result as bigint) : 0n\n ratesAtTarget.push(rate < 0n ? 0n : rate)\n } else {\n ratesAtTarget.push(0n)\n }\n const d = irmAndDecimalResults[resultIdx++]\n decimals.push(d.status === 'success' ? Number(d.result) : 18)\n const ls = irmAndDecimalResults[resultIdx++]\n loanSymbols.push(ls.status === 'success' ? (ls.result as string) : '???')\n if (hasCollateral) {\n const cs = irmAndDecimalResults[resultIdx++]\n collateralSymbols.push(cs.status === 'success' ? (cs.result as string) : '???')\n } else {\n collateralSymbols.push('—')\n }\n }\n\n // 6. Assemble\n const markets: MarketForApy[] = allIds.map((marketId, i) => {\n const [totalSupplyAssets, totalSupplyShares, totalBorrowAssets, , , fee] = marketStates[i]\n const [supplyShares] = positions[i]\n const [cap] = configs[i]\n\n let vaultSupplyAssets = 0n\n if (supplyShares > 0n && totalSupplyShares > 0n) {\n vaultSupplyAssets = (supplyShares * totalSupplyAssets) / totalSupplyShares\n }\n\n const [loanToken, collateralToken] = marketParams[i] as unknown as [string, string, string, string, bigint]\n\n return {\n marketId,\n name: `${collateralSymbols[i]}/${loanSymbols[i]}`,\n loanToken,\n collateralToken,\n totalSupplyAssets,\n totalBorrowAssets,\n fee,\n vaultSupplyAssets,\n rateAtTarget: ratesAtTarget[i],\n cap,\n decimals: decimals[i],\n inSupplyQueue: supplySet.has(marketId as `0x${string}`),\n inWithdrawQueue: withdrawSet.has(marketId as `0x${string}`),\n withdrawQueueIndex: withdrawQueueOrder.get(marketId) ?? -1,\n }\n })\n\n const sumVaultSupply = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n)\n const idleAssets = totalAssets > sumVaultSupply ? totalAssets - sumVaultSupply : 0n\n\n return { markets, idleAssets }\n}\n\n/**\n * Fetch vault markets and compute the weighted APY in one call.\n * Returns null if the vault has no markets yet (not yet deployed).\n * Returns `apy`, `markets`, and `idleAssets` so callers can reuse for simulation.\n */\nexport async function fetchVaultApyViem(\n client: PublicClient,\n vaultAddress: string,\n morphoAddress: string,\n options?: { includeMarkets?: boolean },\n): Promise<VaultApyResult | null> {\n const { markets, idleAssets } = await fetchVaultMarketsViem(client, vaultAddress, morphoAddress)\n if (markets.length === 0) return null\n const detailed = weightedVaultApyDetailed(markets)\n return { apy: detailed.apy, markets, idleAssets, marketDetails: options?.includeMarkets ? detailed.markets : [] }\n}\n","/** Well-known Morpho Blue singleton addresses per chain. */\nexport const MORPHO_BLUE_ADDRESSES: Record<number, `0x${string}`> = {\n 1: '0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb', // Ethereum\n 8453: '0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb', // Base\n 999: '0x68e37de8d93d3496ae143f2e900490f6280c57cd', // HyperEVM\n}\n\n/** Look up Morpho Blue singleton address by chain ID. Throws if unknown. */\nexport function getMorphoBlueAddress(chainId: number): `0x${string}` {\n const addr = MORPHO_BLUE_ADDRESSES[chainId]\n if (!addr) {\n throw new Error(\n `No Morpho Blue address configured for chainId ${chainId}. ` +\n `Known chains: ${Object.keys(MORPHO_BLUE_ADDRESSES).join(', ')}`,\n )\n }\n return addr\n}\n","import type { PublicClient } from 'viem'\n\nimport { getMorphoBlueAddress } from './addresses.js'\nimport { fetchVaultApyViem } from './fetch.js'\nimport { mergeMarketDetails, simulateDeposit, weightedVaultApyDetailed } from './math.js'\nimport type { DepositImpactResult } from './types.js'\n\n/**\n * Compute the APY impact of a deposit into a MetaMorpho vault.\n * Makes live on-chain RPC calls via the provided viem PublicClient.\n *\n * @param client viem PublicClient for the target chain\n * @param chainId Chain ID (used to resolve the Morpho Blue singleton address)\n * @param vaultAddress MetaMorpho V1.1 vault address\n * @param depositAmount Deposit size in loan-token base units (e.g. 1e18 for 1 ETH)\n * @param morphoAddress Optional override for the Morpho Blue singleton address\n */\nexport async function computeDepositImpact(\n client: PublicClient,\n chainId: number,\n vaultAddress: string,\n depositAmount: bigint,\n options?: { morphoAddress?: string; includeMarkets?: boolean },\n): Promise<DepositImpactResult> {\n const morpho = options?.morphoAddress ?? getMorphoBlueAddress(chainId)\n const incMkts = options?.includeMarkets ?? false\n\n const result = await fetchVaultApyViem(client, vaultAddress, morpho)\n if (!result) {\n return { currentApy: 0, newApy: 0, impact: 0, impactBps: 0, markets: [] }\n }\n\n const { markets } = result\n const current = weightedVaultApyDetailed(markets)\n const sim = simulateDeposit(markets, depositAmount)\n const simulated = weightedVaultApyDetailed(markets, sim)\n const impact = simulated.apy - current.apy\n\n return {\n currentApy: current.apy,\n newApy: simulated.apy,\n impact,\n impactBps: Math.round(impact * 10000),\n markets: incMkts ? mergeMarketDetails(current.markets, simulated.markets) : [],\n }\n}\n","import type { PublicClient } from 'viem'\n\nimport { getMorphoBlueAddress } from './addresses.js'\nimport { fetchVaultApyViem } from './fetch.js'\nimport { mergeMarketDetails, simulateWithdrawal, weightedVaultApyDetailed } from './math.js'\nimport type { WithdrawalImpactResult } from './types.js'\n\n/**\n * Compute the APY impact of a withdrawal from a MetaMorpho vault.\n * Makes live on-chain RPC calls via the provided viem PublicClient.\n *\n * @param client viem PublicClient for the target chain\n * @param chainId Chain ID (used to resolve the Morpho Blue singleton address)\n * @param vaultAddress MetaMorpho V1.1 vault address\n * @param withdrawAmount Withdrawal size in loan-token base units (e.g. 1e18 for 1 ETH)\n * @param morphoAddress Optional override for the Morpho Blue singleton address\n */\nexport async function computeWithdrawalImpact(\n client: PublicClient,\n chainId: number,\n vaultAddress: string,\n withdrawAmount: bigint,\n options?: { morphoAddress?: string; includeMarkets?: boolean },\n): Promise<WithdrawalImpactResult> {\n const morpho = options?.morphoAddress ?? getMorphoBlueAddress(chainId)\n const incMkts = options?.includeMarkets ?? false\n\n const result = await fetchVaultApyViem(client, vaultAddress, morpho)\n if (!result) {\n return {\n currentApy: 0,\n newApy: 0,\n impact: 0,\n impactBps: 0,\n isPartial: false,\n withdrawableAmount: '0',\n markets: [],\n }\n }\n\n const { markets, idleAssets } = result\n const current = weightedVaultApyDetailed(markets)\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, withdrawAmount, idleAssets)\n\n // Edge case: full withdrawal drains all vault positions\n const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets\n const simulated =\n withdrawable >= totalVaultAssets ? { apy: 0, markets: [] } : weightedVaultApyDetailed(markets, {}, sim)\n const impact = simulated.apy - current.apy\n\n return {\n currentApy: current.apy,\n newApy: simulated.apy,\n impact,\n impactBps: Math.round(impact * 10000),\n isPartial: remaining > 0n,\n withdrawableAmount: withdrawable.toString(),\n markets: incMkts ? mergeMarketDetails(current.markets, simulated.markets) : [],\n }\n}\n","/**\n * Binary search to find the maximum deposit/withdrawal amount\n * within an APY impact threshold. Pure math — zero external deps.\n */\nimport { mergeMarketDetails, simulateDeposit, simulateWithdrawal, weightedVaultApyDetailed } from './math.js'\nimport type {\n DepositImpactResult,\n FindMaxConstraint,\n FindMaxDepositResult,\n FindMaxWithdrawalResult,\n MarketApyDetail,\n MarketForApy,\n VaultApyDetailed,\n WithdrawalImpactResult,\n} from './types.js'\n\nfunction defaultPrecision(markets: MarketForApy[]): bigint {\n const decimals = markets.length > 0 ? markets[0].decimals : 18\n return 10n ** BigInt(decimals) // 1 whole token\n}\n\nfunction computeMaxDepositable(markets: MarketForApy[]): bigint {\n return markets\n .filter((m) => m.inSupplyQueue && m.cap > m.vaultSupplyAssets)\n .reduce((sum, m) => sum + (m.cap - m.vaultSupplyAssets), 0n)\n}\n\nfunction computeMaxWithdrawable(markets: MarketForApy[], idleAssets: bigint): bigint {\n const fromMarkets = markets\n .filter((m) => m.inWithdrawQueue && m.withdrawQueueIndex >= 0)\n .reduce((sum, m) => {\n const liquidity = m.totalSupplyAssets > m.totalBorrowAssets ? m.totalSupplyAssets - m.totalBorrowAssets : 0n\n const available = m.vaultSupplyAssets < liquidity ? m.vaultSupplyAssets : liquidity\n return sum + (available > 0n ? available : 0n)\n }, 0n)\n return idleAssets + fromMarkets\n}\n\nfunction buildDepositImpact(\n current: VaultApyDetailed,\n simulated: VaultApyDetailed,\n includeMarkets: boolean,\n): DepositImpactResult {\n const impact = simulated.apy - current.apy\n return {\n currentApy: current.apy,\n newApy: simulated.apy,\n impact,\n impactBps: Math.round(impact * 10000),\n markets: includeMarkets ? mergeMarketDetails(current.markets, simulated.markets) : [],\n }\n}\n\nfunction buildWithdrawalImpact(\n current: VaultApyDetailed,\n simulated: VaultApyDetailed,\n withdrawable: bigint,\n remaining: bigint,\n includeMarkets: boolean,\n): WithdrawalImpactResult {\n const impact = simulated.apy - current.apy\n return {\n currentApy: current.apy,\n newApy: simulated.apy,\n impact,\n impactBps: Math.round(impact * 10000),\n isPartial: remaining > 0n,\n withdrawableAmount: withdrawable.toString(),\n markets: includeMarkets ? mergeMarketDetails(current.markets, simulated.markets) : [],\n }\n}\n\nfunction zeroDepositImpact(markets: MarketForApy[], includeMarkets: boolean): DepositImpactResult {\n if (markets.length === 0) {\n return { currentApy: 0, newApy: 0, impact: 0, impactBps: 0, markets: [] }\n }\n const current = weightedVaultApyDetailed(markets)\n return buildDepositImpact(current, current, includeMarkets)\n}\n\nfunction zeroWithdrawalImpact(\n markets: MarketForApy[],\n idleAssets: bigint,\n includeMarkets: boolean,\n): WithdrawalImpactResult {\n if (markets.length === 0) {\n return { currentApy: 0, newApy: 0, impact: 0, impactBps: 0, isPartial: false, withdrawableAmount: '0', markets: [] }\n }\n const current = weightedVaultApyDetailed(markets)\n return buildWithdrawalImpact(current, current, 0n, 0n, includeMarkets)\n}\n\n/**\n * Find the maximum deposit amount within an APY impact threshold using binary search.\n *\n * @param markets - Market data from fetchVaultMarkets\n * @param maxAmount - Ceiling amount in base units\n * @param maxImpactBps - Maximum allowed APY impact in basis points (e.g. 50 = 0.5%)\n * @param options.precision - Search granularity in base units. Default: 1 whole token.\n * @param options.includeMarkets - Include per-market details in impact result. Default: false.\n * @param options.constraint - Custom constraint callback. Must be monotonic: if amount X fails, all X+n must also fail.\n */\nexport async function findMaxDepositAmount(\n markets: MarketForApy[],\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxDepositResult> {\n const raw = options?.precision ?? defaultPrecision(markets)\n const step = raw > 0n ? raw : 1n\n const maxImpact = maxImpactBps / 10000\n const incMkts = options?.includeMarkets ?? false\n const constraint = options?.constraint\n\n if (markets.length === 0 || maxAmount === 0n) {\n return {\n amount: 0n,\n maxPossibleAmount: 0n,\n isMaxAmount: maxAmount === 0n,\n isCapped: false,\n impact: zeroDepositImpact(markets, incMkts),\n }\n }\n\n const maxPossible = computeMaxDepositable(markets)\n const effectiveMax = maxAmount < maxPossible ? maxAmount : maxPossible\n\n if (effectiveMax === 0n) {\n return {\n amount: 0n,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: maxPossible === 0n,\n impact: zeroDepositImpact(markets, incMkts),\n }\n }\n\n const current = weightedVaultApyDetailed(markets)\n\n // Helper: is this amount acceptable (APY impact + custom constraint)?\n async function isAcceptable(amount: bigint): Promise<boolean> {\n const sim = simulateDeposit(markets, amount)\n const simulated = weightedVaultApyDetailed(markets, sim)\n const impact = Math.abs(simulated.apy - current.apy)\n if (impact > maxImpact) return false\n if (!constraint) return true\n return constraint({\n amount,\n currentApy: current.apy,\n simulatedApy: simulated.apy,\n impactBps: Math.round((simulated.apy - current.apy) * 10000),\n markets: mergeMarketDetails(current.markets, simulated.markets),\n })\n }\n\n // Fast path: full effectiveMax within threshold\n if (await isAcceptable(effectiveMax)) {\n const sim = simulateDeposit(markets, effectiveMax)\n const simulated = weightedVaultApyDetailed(markets, sim)\n return {\n amount: effectiveMax,\n maxPossibleAmount: maxPossible,\n isMaxAmount: effectiveMax === maxAmount,\n isCapped: effectiveMax < maxAmount,\n impact: buildDepositImpact(current, simulated, incMkts),\n }\n }\n\n // Fast path: even smallest step exceeds threshold\n if (step >= effectiveMax || !(await isAcceptable(step))) {\n return {\n amount: 0n,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: false,\n impact: zeroDepositImpact(markets, incMkts),\n }\n }\n\n // Binary search: lo is within threshold, hi exceeds it\n let lo = step\n let hi = effectiveMax\n\n while (hi - lo > step) {\n let mid = lo + (hi - lo) / 2n\n mid = (mid / step) * step // snap to precision grid\n if (mid === lo) break\n\n if (await isAcceptable(mid)) {\n lo = mid\n } else {\n hi = mid\n }\n }\n\n const sim = simulateDeposit(markets, lo)\n const simulated = weightedVaultApyDetailed(markets, sim)\n return {\n amount: lo,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: false,\n impact: buildDepositImpact(current, simulated, incMkts),\n }\n}\n\n/**\n * Find the maximum withdrawal amount within an APY impact threshold using binary search.\n *\n * @param markets - Market data from fetchVaultMarkets\n * @param idleAssets - Vault idle assets (undeployed)\n * @param maxAmount - Ceiling amount in base units\n * @param maxImpactBps - Maximum allowed APY impact in basis points (e.g. 50 = 0.5%)\n * @param options.precision - Search granularity in base units. Default: 1 whole token.\n * @param options.includeMarkets - Include per-market details in impact result. Default: false.\n * @param options.constraint - Custom constraint callback. Must be monotonic: if amount X fails, all X+n must also fail.\n */\nexport async function findMaxWithdrawalAmount(\n markets: MarketForApy[],\n idleAssets: bigint,\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxWithdrawalResult> {\n const raw = options?.precision ?? defaultPrecision(markets)\n const step = raw > 0n ? raw : 1n\n const maxImpact = maxImpactBps / 10000\n const incMkts = options?.includeMarkets ?? false\n const constraint = options?.constraint\n\n if (markets.length === 0 || maxAmount === 0n) {\n return {\n amount: 0n,\n maxPossibleAmount: 0n,\n isMaxAmount: maxAmount === 0n,\n isCapped: false,\n impact: zeroWithdrawalImpact(markets, idleAssets, incMkts),\n }\n }\n\n const maxPossible = computeMaxWithdrawable(markets, idleAssets)\n const effectiveMax = maxAmount < maxPossible ? maxAmount : maxPossible\n\n if (effectiveMax === 0n) {\n return {\n amount: 0n,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: maxPossible === 0n,\n impact: zeroWithdrawalImpact(markets, idleAssets, incMkts),\n }\n }\n\n const current = weightedVaultApyDetailed(markets)\n const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets\n\n // Helper: is this amount acceptable (APY impact + custom constraint)?\n async function isAcceptable(amount: bigint): Promise<boolean> {\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, amount, idleAssets)\n const isFullDrain = withdrawable >= totalVaultAssets\n const simulated = isFullDrain\n ? { apy: 0, markets: [] as MarketApyDetail[] }\n : weightedVaultApyDetailed(markets, {}, sim)\n const impact = Math.abs(simulated.apy - current.apy)\n if (impact > maxImpact) return false\n if (!constraint) return true\n return constraint({\n amount,\n currentApy: current.apy,\n simulatedApy: simulated.apy,\n impactBps: Math.round((simulated.apy - current.apy) * 10000),\n markets: mergeMarketDetails(current.markets, simulated.markets),\n isPartial: remaining > 0n,\n withdrawableAmount: withdrawable,\n })\n }\n\n // Fast path: idle covers everything (zero APY impact)\n if (effectiveMax <= idleAssets) {\n if (!constraint || (await isAcceptable(effectiveMax))) {\n const { withdrawable, remaining } = simulateWithdrawal(markets, effectiveMax, idleAssets)\n return {\n amount: effectiveMax,\n maxPossibleAmount: maxPossible,\n isMaxAmount: effectiveMax === maxAmount,\n isCapped: effectiveMax < maxAmount,\n impact: buildWithdrawalImpact(current, current, withdrawable, remaining, incMkts),\n }\n }\n // Constraint failed despite zero APY impact — fall through to binary search\n }\n\n // Fast path: full effectiveMax within threshold\n if (await isAcceptable(effectiveMax)) {\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, effectiveMax, idleAssets)\n const simulated =\n withdrawable >= totalVaultAssets\n ? { apy: 0, markets: [] as MarketApyDetail[] }\n : weightedVaultApyDetailed(markets, {}, sim)\n return {\n amount: effectiveMax,\n maxPossibleAmount: maxPossible,\n isMaxAmount: effectiveMax === maxAmount,\n isCapped: effectiveMax < maxAmount,\n impact: buildWithdrawalImpact(current, simulated, withdrawable, remaining, incMkts),\n }\n }\n\n // Fast path: even smallest step exceeds threshold\n if (step >= effectiveMax || !(await isAcceptable(step))) {\n return {\n amount: 0n,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: false,\n impact: zeroWithdrawalImpact(markets, idleAssets, incMkts),\n }\n }\n\n // Binary search\n let lo = step\n let hi = effectiveMax\n\n while (hi - lo > step) {\n let mid = lo + (hi - lo) / 2n\n mid = (mid / step) * step\n if (mid === lo) break\n\n if (await isAcceptable(mid)) {\n lo = mid\n } else {\n hi = mid\n }\n }\n\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, lo, idleAssets)\n const simulated =\n withdrawable >= totalVaultAssets\n ? { apy: 0, markets: [] as MarketApyDetail[] }\n : weightedVaultApyDetailed(markets, {}, sim)\n return {\n amount: lo,\n maxPossibleAmount: maxPossible,\n isMaxAmount: false,\n isCapped: false,\n impact: buildWithdrawalImpact(current, simulated, withdrawable, remaining, incMkts),\n }\n}\n","import type { PublicClient } from 'viem'\n\nimport { getMorphoBlueAddress } from './addresses.js'\nimport { fetchVaultApyViem } from './fetch.js'\nimport { findMaxDepositAmount, findMaxWithdrawalAmount } from './find-max.js'\nimport type { FindMaxConstraint, FindMaxDepositResult, FindMaxWithdrawalResult } from './types.js'\n\nexport async function findMaxDeposit(\n client: PublicClient,\n chainId: number,\n vaultAddress: string,\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; morphoAddress?: string; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxDepositResult> {\n const morpho = options?.morphoAddress ?? getMorphoBlueAddress(chainId)\n const result = await fetchVaultApyViem(client, vaultAddress, morpho)\n if (!result) {\n return {\n amount: 0n,\n maxPossibleAmount: 0n,\n isMaxAmount: false,\n isCapped: false,\n impact: { currentApy: 0, newApy: 0, impact: 0, impactBps: 0, markets: [] },\n }\n }\n return findMaxDepositAmount(result.markets, maxAmount, maxImpactBps, {\n precision: options?.precision,\n includeMarkets: options?.includeMarkets,\n constraint: options?.constraint,\n })\n}\n\nexport async function findMaxWithdrawal(\n client: PublicClient,\n chainId: number,\n vaultAddress: string,\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; morphoAddress?: string; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxWithdrawalResult> {\n const morpho = options?.morphoAddress ?? getMorphoBlueAddress(chainId)\n const result = await fetchVaultApyViem(client, vaultAddress, morpho)\n if (!result) {\n return {\n amount: 0n,\n maxPossibleAmount: 0n,\n isMaxAmount: false,\n isCapped: false,\n impact: {\n currentApy: 0,\n newApy: 0,\n impact: 0,\n impactBps: 0,\n isPartial: false,\n withdrawableAmount: '0',\n markets: [],\n },\n }\n }\n return findMaxWithdrawalAmount(result.markets, result.idleAssets, maxAmount, maxImpactBps, {\n precision: options?.precision,\n includeMarkets: options?.includeMarkets,\n constraint: options?.constraint,\n })\n}\n","/**\n * RPC URL-based convenience functions.\n * These wrap the viem-based functions so consumers don't need to create a PublicClient.\n */\nimport { createPublicClient, defineChain, http } from 'viem'\n\nimport { getMorphoBlueAddress } from './addresses.js'\nimport { computeDepositImpact as computeDepositImpactViem } from './deposit-impact.js'\nimport { fetchVaultApyViem, fetchVaultMarketsViem } from './fetch.js'\nimport { findMaxDeposit as findMaxDepositViem, findMaxWithdrawal as findMaxWithdrawalViem } from './find-max-impact.js'\nimport type {\n DepositImpactResult,\n FindMaxConstraint,\n FindMaxDepositResult,\n FindMaxWithdrawalResult,\n VaultApyResult,\n VaultFetchResult,\n WithdrawalImpactResult,\n} from './types.js'\nimport { computeWithdrawalImpact as computeWithdrawalImpactViem } from './withdrawal-impact.js'\n\nconst MULTICALL3 = '0xcA11bde05977b3631167028862bE2a173976CA11' as const\n\nfunction makeClient(rpcUrl: string, chainId: number) {\n return createPublicClient({\n transport: http(rpcUrl),\n chain: defineChain({\n id: chainId,\n name: `Chain ${chainId}`,\n nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },\n rpcUrls: { default: { http: [rpcUrl] } },\n contracts: { multicall3: { address: MULTICALL3 } },\n }),\n })\n}\n\nexport async function fetchVaultMarkets(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n morphoAddress: string,\n): Promise<VaultFetchResult> {\n return fetchVaultMarketsViem(makeClient(rpcUrl, chainId), vaultAddress, morphoAddress)\n}\n\nexport async function fetchVaultApy(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n options?: { morphoAddress?: string; includeMarkets?: boolean },\n): Promise<VaultApyResult | null> {\n const morpho = options?.morphoAddress ?? getMorphoBlueAddress(chainId)\n return fetchVaultApyViem(makeClient(rpcUrl, chainId), vaultAddress, morpho, {\n includeMarkets: options?.includeMarkets,\n })\n}\n\nexport async function computeDepositImpactRpc(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n depositAmount: bigint,\n options?: { morphoAddress?: string; includeMarkets?: boolean },\n): Promise<DepositImpactResult> {\n return computeDepositImpactViem(makeClient(rpcUrl, chainId), chainId, vaultAddress, depositAmount, options)\n}\n\nexport async function computeWithdrawalImpactRpc(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n withdrawAmount: bigint,\n options?: { morphoAddress?: string; includeMarkets?: boolean },\n): Promise<WithdrawalImpactResult> {\n return computeWithdrawalImpactViem(makeClient(rpcUrl, chainId), chainId, vaultAddress, withdrawAmount, options)\n}\n\nexport async function findMaxDepositRpc(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; morphoAddress?: string; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxDepositResult> {\n return findMaxDepositViem(makeClient(rpcUrl, chainId), chainId, vaultAddress, maxAmount, maxImpactBps, options)\n}\n\nexport async function findMaxWithdrawalRpc(\n rpcUrl: string,\n chainId: number,\n vaultAddress: string,\n maxAmount: bigint,\n maxImpactBps: number,\n options?: { precision?: bigint; morphoAddress?: string; includeMarkets?: boolean; constraint?: FindMaxConstraint },\n): Promise<FindMaxWithdrawalResult> {\n return findMaxWithdrawalViem(makeClient(rpcUrl, chainId), chainId, vaultAddress, maxAmount, maxImpactBps, options)\n}\n"],"mappings":"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","names":["sim","simulated","withdrawable","remaining"]}
package/dist/cli.cjs ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ "use strict"; function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+ var _chunk35AORYVFcjs = require('./chunk-35AORYVF.cjs');
11
+
12
+
13
+
14
+
15
+
16
+ var _chunk37KGZP4Ocjs = require('./chunk-37KGZP4O.cjs');
17
+
18
+ // src/cli.ts
19
+ function jsonReplacer(_key, value) {
20
+ return typeof value === "bigint" ? value.toString() : value;
21
+ }
22
+ function printJson(data) {
23
+ console.log(JSON.stringify(data, jsonReplacer, 2));
24
+ }
25
+ function usage() {
26
+ console.error(`Usage: morpho-utils <command> [options]
27
+
28
+ Commands:
29
+ apy Fetch current vault APY
30
+ deposit-impact Simulate deposit APY impact
31
+ withdraw-impact Simulate withdrawal APY impact
32
+ find-max-deposit Find max deposit within APY impact threshold
33
+ find-max-withdraw Find max withdrawal within APY impact threshold
34
+ compute Compute APY from JSON market data on stdin (offline, no RPC)
35
+
36
+ Options:
37
+ --rpc-url <url> RPC endpoint (required for RPC commands)
38
+ --chain-id <number> Chain ID (required for RPC commands)
39
+ --vault <address> MetaMorpho vault address (required for RPC commands)
40
+ --morpho <address> Morpho Blue address override (optional)
41
+ --amount <string> Amount in base units (required for impact/find-max commands)
42
+ --max-impact <number> Max APY impact in bps (for find-max or compute with --deposit/--withdraw)
43
+ --precision <string> Search precision in base units (optional, default: 1 whole token)
44
+ --deposit <string> Deposit amount for offline compute
45
+ --withdraw <string> Withdrawal amount for offline compute
46
+ --markets Include per-market details in output`);
47
+ process.exit(1);
48
+ }
49
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["markets"]);
50
+ function parseArgs(args) {
51
+ const result = {};
52
+ for (let i = 0; i < args.length; i++) {
53
+ if (!args[i].startsWith("--")) continue;
54
+ const key = args[i].slice(2);
55
+ if (BOOLEAN_FLAGS.has(key)) {
56
+ result[key] = "true";
57
+ } else if (i + 1 < args.length) {
58
+ result[key] = args[i + 1];
59
+ i++;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ function requireArg(opts, key) {
65
+ if (!opts[key]) {
66
+ console.error(`Error: --${key} is required`);
67
+ process.exit(1);
68
+ }
69
+ return opts[key];
70
+ }
71
+ async function readStdin() {
72
+ const chunks = [];
73
+ for await (const chunk of process.stdin) {
74
+ chunks.push(chunk);
75
+ }
76
+ return Buffer.concat(chunks).toString("utf-8");
77
+ }
78
+ function parseMarkets(data) {
79
+ return {
80
+ markets: data.markets.map((m) => ({
81
+ marketId: String(m.marketId),
82
+ name: m.name ? String(m.name) : void 0,
83
+ loanToken: m.loanToken ? String(m.loanToken) : void 0,
84
+ collateralToken: m.collateralToken ? String(m.collateralToken) : void 0,
85
+ totalSupplyAssets: BigInt(m.totalSupplyAssets),
86
+ totalBorrowAssets: BigInt(m.totalBorrowAssets),
87
+ fee: BigInt(m.fee),
88
+ vaultSupplyAssets: BigInt(m.vaultSupplyAssets),
89
+ rateAtTarget: BigInt(m.rateAtTarget),
90
+ decimals: Number(m.decimals),
91
+ inSupplyQueue: Boolean(m.inSupplyQueue),
92
+ inWithdrawQueue: Boolean(m.inWithdrawQueue),
93
+ withdrawQueueIndex: Number(m.withdrawQueueIndex),
94
+ cap: BigInt(m.cap)
95
+ })),
96
+ idleAssets: BigInt(_nullishCoalesce(data.idleAssets, () => ( "0")))
97
+ };
98
+ }
99
+ async function main() {
100
+ const [command, ...rest] = process.argv.slice(2);
101
+ if (!command) usage();
102
+ const opts = parseArgs(rest);
103
+ if (command === "compute") {
104
+ const input = JSON.parse(await readStdin());
105
+ const { markets, idleAssets } = parseMarkets(input);
106
+ const maxImpactBps = opts["max-impact"] ? Number(opts["max-impact"]) : void 0;
107
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
108
+ const includeMarkets2 = opts.markets === "true";
109
+ if (opts.deposit) {
110
+ const amount = BigInt(opts.deposit);
111
+ if (maxImpactBps !== void 0) {
112
+ const result = await _chunk35AORYVFcjs.findMaxDepositAmount.call(void 0, markets, amount, maxImpactBps, { precision, includeMarkets: includeMarkets2 });
113
+ printJson(result);
114
+ } else {
115
+ const sim = _chunk37KGZP4Ocjs.simulateDeposit.call(void 0, markets, amount);
116
+ const currentApy = _chunk37KGZP4Ocjs.weightedVaultApy.call(void 0, markets);
117
+ const newApy = _chunk37KGZP4Ocjs.weightedVaultApy.call(void 0, markets, sim);
118
+ const impact = newApy - currentApy;
119
+ printJson({ currentApy, newApy, impact, impactBps: Math.round(impact * 1e4) });
120
+ }
121
+ } else if (opts.withdraw) {
122
+ const amount = BigInt(opts.withdraw);
123
+ if (maxImpactBps !== void 0) {
124
+ const result = await _chunk35AORYVFcjs.findMaxWithdrawalAmount.call(void 0, markets, idleAssets, amount, maxImpactBps, {
125
+ precision,
126
+ includeMarkets: includeMarkets2
127
+ });
128
+ printJson(result);
129
+ } else {
130
+ const { sim, withdrawable, remaining } = _chunk37KGZP4Ocjs.simulateWithdrawal.call(void 0, markets, amount, idleAssets);
131
+ const currentApy = _chunk37KGZP4Ocjs.weightedVaultApy.call(void 0, markets);
132
+ const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets;
133
+ const newApy = withdrawable >= totalVaultAssets ? 0 : _chunk37KGZP4Ocjs.weightedVaultApy.call(void 0, markets, {}, sim);
134
+ const impact = newApy - currentApy;
135
+ printJson({
136
+ currentApy,
137
+ newApy,
138
+ impact,
139
+ impactBps: Math.round(impact * 1e4),
140
+ isPartial: remaining > 0n,
141
+ withdrawableAmount: withdrawable.toString()
142
+ });
143
+ }
144
+ } else {
145
+ if (includeMarkets2) {
146
+ const detailed = _chunk37KGZP4Ocjs.weightedVaultApyDetailed.call(void 0, markets);
147
+ printJson({ apy: detailed.apy, markets: detailed.markets });
148
+ } else {
149
+ printJson({ apy: _chunk37KGZP4Ocjs.weightedVaultApy.call(void 0, markets) });
150
+ }
151
+ }
152
+ return;
153
+ }
154
+ const rpcUrl = requireArg(opts, "rpc-url");
155
+ const chainId = Number(requireArg(opts, "chain-id"));
156
+ const vaultAddress = requireArg(opts, "vault");
157
+ const morphoAddress = opts.morpho;
158
+ const includeMarkets = opts.markets === "true";
159
+ if (command === "apy") {
160
+ const result = await _chunk35AORYVFcjs.fetchVaultApy.call(void 0, rpcUrl, chainId, vaultAddress, { morphoAddress, includeMarkets });
161
+ printJson(
162
+ result ? { apy: result.apy, idleAssets: result.idleAssets, markets: result.marketDetails } : { apy: 0, markets: [] }
163
+ );
164
+ } else if (command === "deposit-impact") {
165
+ const amount = BigInt(requireArg(opts, "amount"));
166
+ const result = await _chunk35AORYVFcjs.computeDepositImpactRpc.call(void 0, rpcUrl, chainId, vaultAddress, amount, {
167
+ morphoAddress,
168
+ includeMarkets
169
+ });
170
+ printJson(result);
171
+ } else if (command === "withdraw-impact") {
172
+ const amount = BigInt(requireArg(opts, "amount"));
173
+ const result = await _chunk35AORYVFcjs.computeWithdrawalImpactRpc.call(void 0, rpcUrl, chainId, vaultAddress, amount, {
174
+ morphoAddress,
175
+ includeMarkets
176
+ });
177
+ printJson(result);
178
+ } else if (command === "find-max-deposit") {
179
+ const amount = BigInt(requireArg(opts, "amount"));
180
+ const maxImpactBps = Number(requireArg(opts, "max-impact"));
181
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
182
+ const result = await _chunk35AORYVFcjs.findMaxDepositRpc.call(void 0, rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {
183
+ precision,
184
+ morphoAddress,
185
+ includeMarkets
186
+ });
187
+ printJson(result);
188
+ } else if (command === "find-max-withdraw") {
189
+ const amount = BigInt(requireArg(opts, "amount"));
190
+ const maxImpactBps = Number(requireArg(opts, "max-impact"));
191
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
192
+ const result = await _chunk35AORYVFcjs.findMaxWithdrawalRpc.call(void 0, rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {
193
+ precision,
194
+ morphoAddress,
195
+ includeMarkets
196
+ });
197
+ printJson(result);
198
+ } else {
199
+ console.error(`Unknown command: ${command}`);
200
+ usage();
201
+ }
202
+ }
203
+ main().catch((err) => {
204
+ console.error(_nullishCoalesce(err.message, () => ( err)));
205
+ process.exit(1);
206
+ });
207
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Volumes/origin/morpho-utils/dist/cli.cjs","../src/cli.ts"],"names":["includeMarkets"],"mappings":"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","file":"/Volumes/origin/morpho-utils/dist/cli.cjs","sourcesContent":[null,"#!/usr/bin/env node\nimport { findMaxDepositAmount, findMaxWithdrawalAmount } from './find-max.js'\nimport { simulateDeposit, simulateWithdrawal, weightedVaultApy, weightedVaultApyDetailed } from './math.js'\nimport {\n computeDepositImpactRpc,\n computeWithdrawalImpactRpc,\n fetchVaultApy,\n findMaxDepositRpc,\n findMaxWithdrawalRpc,\n} from './rpc.js'\nimport type { MarketForApy } from './types.js'\n\nfunction jsonReplacer(_key: string, value: unknown): unknown {\n return typeof value === 'bigint' ? value.toString() : value\n}\n\nfunction printJson(data: unknown): void {\n console.log(JSON.stringify(data, jsonReplacer, 2))\n}\n\nfunction usage(): never {\n console.error(`Usage: morpho-utils <command> [options]\n\nCommands:\n apy Fetch current vault APY\n deposit-impact Simulate deposit APY impact\n withdraw-impact Simulate withdrawal APY impact\n find-max-deposit Find max deposit within APY impact threshold\n find-max-withdraw Find max withdrawal within APY impact threshold\n compute Compute APY from JSON market data on stdin (offline, no RPC)\n\nOptions:\n --rpc-url <url> RPC endpoint (required for RPC commands)\n --chain-id <number> Chain ID (required for RPC commands)\n --vault <address> MetaMorpho vault address (required for RPC commands)\n --morpho <address> Morpho Blue address override (optional)\n --amount <string> Amount in base units (required for impact/find-max commands)\n --max-impact <number> Max APY impact in bps (for find-max or compute with --deposit/--withdraw)\n --precision <string> Search precision in base units (optional, default: 1 whole token)\n --deposit <string> Deposit amount for offline compute\n --withdraw <string> Withdrawal amount for offline compute\n --markets Include per-market details in output`)\n process.exit(1)\n}\n\nconst BOOLEAN_FLAGS = new Set(['markets'])\n\nfunction parseArgs(args: string[]): Record<string, string> {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i++) {\n if (!args[i].startsWith('--')) continue\n const key = args[i].slice(2)\n if (BOOLEAN_FLAGS.has(key)) {\n result[key] = 'true'\n } else if (i + 1 < args.length) {\n result[key] = args[i + 1]\n i++\n }\n }\n return result\n}\n\nfunction requireArg(opts: Record<string, string>, key: string): string {\n if (!opts[key]) {\n console.error(`Error: --${key} is required`)\n process.exit(1)\n }\n return opts[key]\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n return Buffer.concat(chunks).toString('utf-8')\n}\n\nfunction parseMarkets(data: { markets: Record<string, string | number | boolean>[]; idleAssets?: string }): {\n markets: MarketForApy[]\n idleAssets: bigint\n} {\n return {\n markets: data.markets.map((m) => ({\n marketId: String(m.marketId),\n name: m.name ? String(m.name) : undefined,\n loanToken: m.loanToken ? String(m.loanToken) : undefined,\n collateralToken: m.collateralToken ? String(m.collateralToken) : undefined,\n totalSupplyAssets: BigInt(m.totalSupplyAssets),\n totalBorrowAssets: BigInt(m.totalBorrowAssets),\n fee: BigInt(m.fee),\n vaultSupplyAssets: BigInt(m.vaultSupplyAssets),\n rateAtTarget: BigInt(m.rateAtTarget),\n decimals: Number(m.decimals),\n inSupplyQueue: Boolean(m.inSupplyQueue),\n inWithdrawQueue: Boolean(m.inWithdrawQueue),\n withdrawQueueIndex: Number(m.withdrawQueueIndex),\n cap: BigInt(m.cap),\n })),\n idleAssets: BigInt(data.idleAssets ?? '0'),\n }\n}\n\nasync function main() {\n const [command, ...rest] = process.argv.slice(2)\n if (!command) usage()\n\n const opts = parseArgs(rest)\n\n if (command === 'compute') {\n const input = JSON.parse(await readStdin())\n const { markets, idleAssets } = parseMarkets(input)\n const maxImpactBps = opts['max-impact'] ? Number(opts['max-impact']) : undefined\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n\n const includeMarkets = opts.markets === 'true'\n\n if (opts.deposit) {\n const amount = BigInt(opts.deposit)\n if (maxImpactBps !== undefined) {\n const result = await findMaxDepositAmount(markets, amount, maxImpactBps, { precision, includeMarkets })\n printJson(result)\n } else {\n const sim = simulateDeposit(markets, amount)\n const currentApy = weightedVaultApy(markets)\n const newApy = weightedVaultApy(markets, sim)\n const impact = newApy - currentApy\n printJson({ currentApy, newApy, impact, impactBps: Math.round(impact * 10000) })\n }\n } else if (opts.withdraw) {\n const amount = BigInt(opts.withdraw)\n if (maxImpactBps !== undefined) {\n const result = await findMaxWithdrawalAmount(markets, idleAssets, amount, maxImpactBps, {\n precision,\n includeMarkets,\n })\n printJson(result)\n } else {\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, amount, idleAssets)\n const currentApy = weightedVaultApy(markets)\n const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets\n const newApy = withdrawable >= totalVaultAssets ? 0 : weightedVaultApy(markets, {}, sim)\n const impact = newApy - currentApy\n printJson({\n currentApy,\n newApy,\n impact,\n impactBps: Math.round(impact * 10000),\n isPartial: remaining > 0n,\n withdrawableAmount: withdrawable.toString(),\n })\n }\n } else {\n if (includeMarkets) {\n const detailed = weightedVaultApyDetailed(markets)\n printJson({ apy: detailed.apy, markets: detailed.markets })\n } else {\n printJson({ apy: weightedVaultApy(markets) })\n }\n }\n return\n }\n\n // RPC-based commands\n const rpcUrl = requireArg(opts, 'rpc-url')\n const chainId = Number(requireArg(opts, 'chain-id'))\n const vaultAddress = requireArg(opts, 'vault')\n const morphoAddress = opts.morpho\n const includeMarkets = opts.markets === 'true'\n\n if (command === 'apy') {\n const result = await fetchVaultApy(rpcUrl, chainId, vaultAddress, { morphoAddress, includeMarkets })\n printJson(\n result\n ? { apy: result.apy, idleAssets: result.idleAssets, markets: result.marketDetails }\n : { apy: 0, markets: [] },\n )\n } else if (command === 'deposit-impact') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const result = await computeDepositImpactRpc(rpcUrl, chainId, vaultAddress, amount, {\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'withdraw-impact') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const result = await computeWithdrawalImpactRpc(rpcUrl, chainId, vaultAddress, amount, {\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'find-max-deposit') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const maxImpactBps = Number(requireArg(opts, 'max-impact'))\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n const result = await findMaxDepositRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {\n precision,\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'find-max-withdraw') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const maxImpactBps = Number(requireArg(opts, 'max-impact'))\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n const result = await findMaxWithdrawalRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {\n precision,\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else {\n console.error(`Unknown command: ${command}`)\n usage()\n }\n}\n\nmain().catch((err) => {\n console.error(err.message ?? err)\n process.exit(1)\n})\n"]}
package/dist/cli.d.cts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ computeDepositImpactRpc,
4
+ computeWithdrawalImpactRpc,
5
+ fetchVaultApy,
6
+ findMaxDepositAmount,
7
+ findMaxDepositRpc,
8
+ findMaxWithdrawalAmount,
9
+ findMaxWithdrawalRpc
10
+ } from "./chunk-M43IOBUD.js";
11
+ import {
12
+ simulateDeposit,
13
+ simulateWithdrawal,
14
+ weightedVaultApy,
15
+ weightedVaultApyDetailed
16
+ } from "./chunk-K2ZKPGSF.js";
17
+
18
+ // src/cli.ts
19
+ function jsonReplacer(_key, value) {
20
+ return typeof value === "bigint" ? value.toString() : value;
21
+ }
22
+ function printJson(data) {
23
+ console.log(JSON.stringify(data, jsonReplacer, 2));
24
+ }
25
+ function usage() {
26
+ console.error(`Usage: morpho-utils <command> [options]
27
+
28
+ Commands:
29
+ apy Fetch current vault APY
30
+ deposit-impact Simulate deposit APY impact
31
+ withdraw-impact Simulate withdrawal APY impact
32
+ find-max-deposit Find max deposit within APY impact threshold
33
+ find-max-withdraw Find max withdrawal within APY impact threshold
34
+ compute Compute APY from JSON market data on stdin (offline, no RPC)
35
+
36
+ Options:
37
+ --rpc-url <url> RPC endpoint (required for RPC commands)
38
+ --chain-id <number> Chain ID (required for RPC commands)
39
+ --vault <address> MetaMorpho vault address (required for RPC commands)
40
+ --morpho <address> Morpho Blue address override (optional)
41
+ --amount <string> Amount in base units (required for impact/find-max commands)
42
+ --max-impact <number> Max APY impact in bps (for find-max or compute with --deposit/--withdraw)
43
+ --precision <string> Search precision in base units (optional, default: 1 whole token)
44
+ --deposit <string> Deposit amount for offline compute
45
+ --withdraw <string> Withdrawal amount for offline compute
46
+ --markets Include per-market details in output`);
47
+ process.exit(1);
48
+ }
49
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["markets"]);
50
+ function parseArgs(args) {
51
+ const result = {};
52
+ for (let i = 0; i < args.length; i++) {
53
+ if (!args[i].startsWith("--")) continue;
54
+ const key = args[i].slice(2);
55
+ if (BOOLEAN_FLAGS.has(key)) {
56
+ result[key] = "true";
57
+ } else if (i + 1 < args.length) {
58
+ result[key] = args[i + 1];
59
+ i++;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ function requireArg(opts, key) {
65
+ if (!opts[key]) {
66
+ console.error(`Error: --${key} is required`);
67
+ process.exit(1);
68
+ }
69
+ return opts[key];
70
+ }
71
+ async function readStdin() {
72
+ const chunks = [];
73
+ for await (const chunk of process.stdin) {
74
+ chunks.push(chunk);
75
+ }
76
+ return Buffer.concat(chunks).toString("utf-8");
77
+ }
78
+ function parseMarkets(data) {
79
+ return {
80
+ markets: data.markets.map((m) => ({
81
+ marketId: String(m.marketId),
82
+ name: m.name ? String(m.name) : void 0,
83
+ loanToken: m.loanToken ? String(m.loanToken) : void 0,
84
+ collateralToken: m.collateralToken ? String(m.collateralToken) : void 0,
85
+ totalSupplyAssets: BigInt(m.totalSupplyAssets),
86
+ totalBorrowAssets: BigInt(m.totalBorrowAssets),
87
+ fee: BigInt(m.fee),
88
+ vaultSupplyAssets: BigInt(m.vaultSupplyAssets),
89
+ rateAtTarget: BigInt(m.rateAtTarget),
90
+ decimals: Number(m.decimals),
91
+ inSupplyQueue: Boolean(m.inSupplyQueue),
92
+ inWithdrawQueue: Boolean(m.inWithdrawQueue),
93
+ withdrawQueueIndex: Number(m.withdrawQueueIndex),
94
+ cap: BigInt(m.cap)
95
+ })),
96
+ idleAssets: BigInt(data.idleAssets ?? "0")
97
+ };
98
+ }
99
+ async function main() {
100
+ const [command, ...rest] = process.argv.slice(2);
101
+ if (!command) usage();
102
+ const opts = parseArgs(rest);
103
+ if (command === "compute") {
104
+ const input = JSON.parse(await readStdin());
105
+ const { markets, idleAssets } = parseMarkets(input);
106
+ const maxImpactBps = opts["max-impact"] ? Number(opts["max-impact"]) : void 0;
107
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
108
+ const includeMarkets2 = opts.markets === "true";
109
+ if (opts.deposit) {
110
+ const amount = BigInt(opts.deposit);
111
+ if (maxImpactBps !== void 0) {
112
+ const result = await findMaxDepositAmount(markets, amount, maxImpactBps, { precision, includeMarkets: includeMarkets2 });
113
+ printJson(result);
114
+ } else {
115
+ const sim = simulateDeposit(markets, amount);
116
+ const currentApy = weightedVaultApy(markets);
117
+ const newApy = weightedVaultApy(markets, sim);
118
+ const impact = newApy - currentApy;
119
+ printJson({ currentApy, newApy, impact, impactBps: Math.round(impact * 1e4) });
120
+ }
121
+ } else if (opts.withdraw) {
122
+ const amount = BigInt(opts.withdraw);
123
+ if (maxImpactBps !== void 0) {
124
+ const result = await findMaxWithdrawalAmount(markets, idleAssets, amount, maxImpactBps, {
125
+ precision,
126
+ includeMarkets: includeMarkets2
127
+ });
128
+ printJson(result);
129
+ } else {
130
+ const { sim, withdrawable, remaining } = simulateWithdrawal(markets, amount, idleAssets);
131
+ const currentApy = weightedVaultApy(markets);
132
+ const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets;
133
+ const newApy = withdrawable >= totalVaultAssets ? 0 : weightedVaultApy(markets, {}, sim);
134
+ const impact = newApy - currentApy;
135
+ printJson({
136
+ currentApy,
137
+ newApy,
138
+ impact,
139
+ impactBps: Math.round(impact * 1e4),
140
+ isPartial: remaining > 0n,
141
+ withdrawableAmount: withdrawable.toString()
142
+ });
143
+ }
144
+ } else {
145
+ if (includeMarkets2) {
146
+ const detailed = weightedVaultApyDetailed(markets);
147
+ printJson({ apy: detailed.apy, markets: detailed.markets });
148
+ } else {
149
+ printJson({ apy: weightedVaultApy(markets) });
150
+ }
151
+ }
152
+ return;
153
+ }
154
+ const rpcUrl = requireArg(opts, "rpc-url");
155
+ const chainId = Number(requireArg(opts, "chain-id"));
156
+ const vaultAddress = requireArg(opts, "vault");
157
+ const morphoAddress = opts.morpho;
158
+ const includeMarkets = opts.markets === "true";
159
+ if (command === "apy") {
160
+ const result = await fetchVaultApy(rpcUrl, chainId, vaultAddress, { morphoAddress, includeMarkets });
161
+ printJson(
162
+ result ? { apy: result.apy, idleAssets: result.idleAssets, markets: result.marketDetails } : { apy: 0, markets: [] }
163
+ );
164
+ } else if (command === "deposit-impact") {
165
+ const amount = BigInt(requireArg(opts, "amount"));
166
+ const result = await computeDepositImpactRpc(rpcUrl, chainId, vaultAddress, amount, {
167
+ morphoAddress,
168
+ includeMarkets
169
+ });
170
+ printJson(result);
171
+ } else if (command === "withdraw-impact") {
172
+ const amount = BigInt(requireArg(opts, "amount"));
173
+ const result = await computeWithdrawalImpactRpc(rpcUrl, chainId, vaultAddress, amount, {
174
+ morphoAddress,
175
+ includeMarkets
176
+ });
177
+ printJson(result);
178
+ } else if (command === "find-max-deposit") {
179
+ const amount = BigInt(requireArg(opts, "amount"));
180
+ const maxImpactBps = Number(requireArg(opts, "max-impact"));
181
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
182
+ const result = await findMaxDepositRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {
183
+ precision,
184
+ morphoAddress,
185
+ includeMarkets
186
+ });
187
+ printJson(result);
188
+ } else if (command === "find-max-withdraw") {
189
+ const amount = BigInt(requireArg(opts, "amount"));
190
+ const maxImpactBps = Number(requireArg(opts, "max-impact"));
191
+ const precision = opts.precision ? BigInt(opts.precision) : void 0;
192
+ const result = await findMaxWithdrawalRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {
193
+ precision,
194
+ morphoAddress,
195
+ includeMarkets
196
+ });
197
+ printJson(result);
198
+ } else {
199
+ console.error(`Unknown command: ${command}`);
200
+ usage();
201
+ }
202
+ }
203
+ main().catch((err) => {
204
+ console.error(err.message ?? err);
205
+ process.exit(1);
206
+ });
207
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { findMaxDepositAmount, findMaxWithdrawalAmount } from './find-max.js'\nimport { simulateDeposit, simulateWithdrawal, weightedVaultApy, weightedVaultApyDetailed } from './math.js'\nimport {\n computeDepositImpactRpc,\n computeWithdrawalImpactRpc,\n fetchVaultApy,\n findMaxDepositRpc,\n findMaxWithdrawalRpc,\n} from './rpc.js'\nimport type { MarketForApy } from './types.js'\n\nfunction jsonReplacer(_key: string, value: unknown): unknown {\n return typeof value === 'bigint' ? value.toString() : value\n}\n\nfunction printJson(data: unknown): void {\n console.log(JSON.stringify(data, jsonReplacer, 2))\n}\n\nfunction usage(): never {\n console.error(`Usage: morpho-utils <command> [options]\n\nCommands:\n apy Fetch current vault APY\n deposit-impact Simulate deposit APY impact\n withdraw-impact Simulate withdrawal APY impact\n find-max-deposit Find max deposit within APY impact threshold\n find-max-withdraw Find max withdrawal within APY impact threshold\n compute Compute APY from JSON market data on stdin (offline, no RPC)\n\nOptions:\n --rpc-url <url> RPC endpoint (required for RPC commands)\n --chain-id <number> Chain ID (required for RPC commands)\n --vault <address> MetaMorpho vault address (required for RPC commands)\n --morpho <address> Morpho Blue address override (optional)\n --amount <string> Amount in base units (required for impact/find-max commands)\n --max-impact <number> Max APY impact in bps (for find-max or compute with --deposit/--withdraw)\n --precision <string> Search precision in base units (optional, default: 1 whole token)\n --deposit <string> Deposit amount for offline compute\n --withdraw <string> Withdrawal amount for offline compute\n --markets Include per-market details in output`)\n process.exit(1)\n}\n\nconst BOOLEAN_FLAGS = new Set(['markets'])\n\nfunction parseArgs(args: string[]): Record<string, string> {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i++) {\n if (!args[i].startsWith('--')) continue\n const key = args[i].slice(2)\n if (BOOLEAN_FLAGS.has(key)) {\n result[key] = 'true'\n } else if (i + 1 < args.length) {\n result[key] = args[i + 1]\n i++\n }\n }\n return result\n}\n\nfunction requireArg(opts: Record<string, string>, key: string): string {\n if (!opts[key]) {\n console.error(`Error: --${key} is required`)\n process.exit(1)\n }\n return opts[key]\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n return Buffer.concat(chunks).toString('utf-8')\n}\n\nfunction parseMarkets(data: { markets: Record<string, string | number | boolean>[]; idleAssets?: string }): {\n markets: MarketForApy[]\n idleAssets: bigint\n} {\n return {\n markets: data.markets.map((m) => ({\n marketId: String(m.marketId),\n name: m.name ? String(m.name) : undefined,\n loanToken: m.loanToken ? String(m.loanToken) : undefined,\n collateralToken: m.collateralToken ? String(m.collateralToken) : undefined,\n totalSupplyAssets: BigInt(m.totalSupplyAssets),\n totalBorrowAssets: BigInt(m.totalBorrowAssets),\n fee: BigInt(m.fee),\n vaultSupplyAssets: BigInt(m.vaultSupplyAssets),\n rateAtTarget: BigInt(m.rateAtTarget),\n decimals: Number(m.decimals),\n inSupplyQueue: Boolean(m.inSupplyQueue),\n inWithdrawQueue: Boolean(m.inWithdrawQueue),\n withdrawQueueIndex: Number(m.withdrawQueueIndex),\n cap: BigInt(m.cap),\n })),\n idleAssets: BigInt(data.idleAssets ?? '0'),\n }\n}\n\nasync function main() {\n const [command, ...rest] = process.argv.slice(2)\n if (!command) usage()\n\n const opts = parseArgs(rest)\n\n if (command === 'compute') {\n const input = JSON.parse(await readStdin())\n const { markets, idleAssets } = parseMarkets(input)\n const maxImpactBps = opts['max-impact'] ? Number(opts['max-impact']) : undefined\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n\n const includeMarkets = opts.markets === 'true'\n\n if (opts.deposit) {\n const amount = BigInt(opts.deposit)\n if (maxImpactBps !== undefined) {\n const result = await findMaxDepositAmount(markets, amount, maxImpactBps, { precision, includeMarkets })\n printJson(result)\n } else {\n const sim = simulateDeposit(markets, amount)\n const currentApy = weightedVaultApy(markets)\n const newApy = weightedVaultApy(markets, sim)\n const impact = newApy - currentApy\n printJson({ currentApy, newApy, impact, impactBps: Math.round(impact * 10000) })\n }\n } else if (opts.withdraw) {\n const amount = BigInt(opts.withdraw)\n if (maxImpactBps !== undefined) {\n const result = await findMaxWithdrawalAmount(markets, idleAssets, amount, maxImpactBps, {\n precision,\n includeMarkets,\n })\n printJson(result)\n } else {\n const { sim, withdrawable, remaining } = simulateWithdrawal(markets, amount, idleAssets)\n const currentApy = weightedVaultApy(markets)\n const totalVaultAssets = markets.reduce((acc, m) => acc + m.vaultSupplyAssets, 0n) + idleAssets\n const newApy = withdrawable >= totalVaultAssets ? 0 : weightedVaultApy(markets, {}, sim)\n const impact = newApy - currentApy\n printJson({\n currentApy,\n newApy,\n impact,\n impactBps: Math.round(impact * 10000),\n isPartial: remaining > 0n,\n withdrawableAmount: withdrawable.toString(),\n })\n }\n } else {\n if (includeMarkets) {\n const detailed = weightedVaultApyDetailed(markets)\n printJson({ apy: detailed.apy, markets: detailed.markets })\n } else {\n printJson({ apy: weightedVaultApy(markets) })\n }\n }\n return\n }\n\n // RPC-based commands\n const rpcUrl = requireArg(opts, 'rpc-url')\n const chainId = Number(requireArg(opts, 'chain-id'))\n const vaultAddress = requireArg(opts, 'vault')\n const morphoAddress = opts.morpho\n const includeMarkets = opts.markets === 'true'\n\n if (command === 'apy') {\n const result = await fetchVaultApy(rpcUrl, chainId, vaultAddress, { morphoAddress, includeMarkets })\n printJson(\n result\n ? { apy: result.apy, idleAssets: result.idleAssets, markets: result.marketDetails }\n : { apy: 0, markets: [] },\n )\n } else if (command === 'deposit-impact') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const result = await computeDepositImpactRpc(rpcUrl, chainId, vaultAddress, amount, {\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'withdraw-impact') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const result = await computeWithdrawalImpactRpc(rpcUrl, chainId, vaultAddress, amount, {\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'find-max-deposit') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const maxImpactBps = Number(requireArg(opts, 'max-impact'))\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n const result = await findMaxDepositRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {\n precision,\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else if (command === 'find-max-withdraw') {\n const amount = BigInt(requireArg(opts, 'amount'))\n const maxImpactBps = Number(requireArg(opts, 'max-impact'))\n const precision = opts.precision ? BigInt(opts.precision) : undefined\n const result = await findMaxWithdrawalRpc(rpcUrl, chainId, vaultAddress, amount, maxImpactBps, {\n precision,\n morphoAddress,\n includeMarkets,\n })\n printJson(result)\n } else {\n console.error(`Unknown command: ${command}`)\n usage()\n }\n}\n\nmain().catch((err) => {\n console.error(err.message ?? err)\n process.exit(1)\n})\n"],"mappings":"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","names":["includeMarkets"]}