UNPKG

@molecularlabs/nucleus-frontend

Version:
1 lines 19.4 kB
{"version":3,"sources":["../../../src/vaults/withdraw/withdraw.ts"],"sourcesContent":["/**\n * @file Withdraw functionality for Nucleus vaults\n * @module vaults/withdraw\n */\n\nimport { type Address, parseUnits } from \"viem\";\nimport { getRateInQuoteAndSharesAndWantAssetDecimals } from \"../../api/accountant\";\nimport { getErc20Decimals } from \"../../api/erc20\";\nimport { getAssetSlippage } from \"../../api/solver-slippage\";\nimport { type ChainId, toChainId } from \"../../api/vault-config\";\nimport {\n ATOMIC_QUEUE_CONTRACT_ADDRESS,\n DEFAULT_APPROVAL_AMOUNT,\n DEFAULT_DEADLINE,\n DEFAULT_WITHDRAW_SLIPPAGE,\n} from \"../../constants\";\nimport { AtomicQueueAbi } from \"../../contracts/atomic-queue-abi\";\nimport { BoringVaultAbi } from \"../../contracts/boring-vault-abi\";\nimport { tryCatch } from \"../../utils/try-catch\";\nimport {\n type BridgeTransactionData,\n prepareBridgeTransactionData,\n} from \"../bridge\";\nimport type { VaultKey } from \"../config\";\nimport { getVaultByKey } from \"../index\";\nimport type { TokenKey } from \"../tokens\";\nimport { calculateAtomicPrice, prepareUserRequest } from \"./utils\";\n\n/**\n * Arguments for an atomic request to the AtomicQueue contract\n * @typedef {Object} AtomicRequestArgs\n * @property {Address} offer - Address of the token being offered (shares token address)\n * @property {Address} want - Address of the token being requested (want token address)\n * @property {UserRequest} userRequest - User request details\n */\nexport type AtomicRequestArgs = {\n offer: Address; // sharesTokenAddress\n want: Address; // wantTokenAddress\n userRequest: UserRequest; // userRequest\n};\n\n/**\n * Options for an atomic request\n * @typedef {Object} AtomicRequestOptions\n * @property {Address} atomicQueueContractAddress - Address of the AtomicQueue contract\n * @property {number} chainId - ID of the destination chain\n */\nexport type AtomicRequestOptions = {\n atomicQueueContractAddress: Address; // atomicQueueContractAddress\n chainId: ChainId; // destinationChainId\n};\n\n/**\n * User request details for an atomic swap\n * @typedef {Object} UserRequest\n * @property {number} deadline - Unix timestamp when the request expires\n * @property {bigint} atomicPrice - Rate in quote with fee applied\n * @property {bigint} offerAmount - Amount of shares token to redeem\n * @property {boolean} inSolve - Whether the request is in solve mode (always false for new requests)\n */\nexport type UserRequest = {\n deadline: bigint; // deadline\n atomicPrice: bigint; // rateInQuoteWithFee\n offerAmount: bigint; // redeemAmount\n inSolve: boolean; // false\n};\n\n/**\n * Parameters for preparing a withdrawal transaction from a vault\n * @interface PrepareWithdrawTransactionDataParams\n * @property {ChainId} chainId - ID of the chain where the withdrawal will occur\n * @property {number} [deadline] - Unix timestamp when the request expires (optional)\n * @property {number} offerAmount - Amount of vault shares to withdraw\n * @property {number} [slippage] - Maximum acceptable slippage percentage (optional)\n * @property {Address} userAddress - Ethereum address of the user making the withdrawal\n * @property {VaultKey} vaultKey - Unique identifier for the vault\n * @property {TokenKey} wantTokenSymbol - Symbol of the token the user wants to receive\n */\ninterface PrepareWithdrawTransactionDataParams {\n chainId: ChainId;\n deadline?: number;\n offerAmount: string;\n slippage?: number;\n userAddress: Address;\n vaultKey: VaultKey;\n wantTokenSymbol: TokenKey;\n}\n\n/**\n * Result object containing transaction data for a withdrawal operation\n * @interface WithdrawTransactionData\n * @property {typeof AtomicQueueAbi} abi - ABI for the AtomicQueue contract\n * @property {Address} address - Address of the AtomicQueue contract\n * @property {'updateAtomicRequest'} functionName - Name of the function to call\n * @property {[Address, Address, UserRequest]} args - Arguments for the updateAtomicRequest function:\n * [offer, want, userRequest]\n * @property {number} chainId - ID of the chain where the transaction should be executed\n */\ninterface WithdrawTransactionData {\n abi: typeof AtomicQueueAbi;\n address: Address;\n functionName: \"updateAtomicRequest\";\n args: [offer: Address, want: Address, userRequest: UserRequest];\n chainId: number;\n}\n\n/**\n * Prepares the transaction data needed to withdraw assets from a vault\n *\n * This function calculates the appropriate exchange rate with slippage protection\n * and prepares a user request for the AtomicQueue contract. It returns a transaction\n * object that can be used with viem or ethers.js to execute the withdrawal.\n *\n * @example\n * ```typescript\n * const withdrawData = await prepareWithdrawData({\n * vaultKey: 'bobaeth',\n * chainId: 1, // Ethereum mainnet\n * userAddress: '0x1234...',\n * wantAsset: TokenKey.WETH,\n * redeemAmount: BigInt('1000000000000000000'), // 1 token\n * deadline: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now\n * slippage: 0.01, // 1% slippage\n * });\n * ```\n *\n * @param {PrepareWithdrawTransactionParams} params - Parameters for the withdraw operation\n * @param {VaultKey} params.vaultKey - Unique identifier for the vault\n * @param {ChainId} params.chainId - ID of the chain where the withdrawal will occur\n * @param {Address} params.userAddress - Ethereum address of the user making the withdrawal\n * @param {TokenKey} params.wantAsset - Key of the asset the user wants to receive\n * @param {bigint} params.offerAmount - Amount of vault tokens to redeem (in base units)\n * @param {number} [params.deadline] - Unix timestamp when the request expires\n * @param {number} [params.slippage] - Maximum acceptable slippage in basis points (bps).\n * e.g., 20 for 0.2%, 50 for 0.5%\n *\n * @returns {Promise<WithdrawTransactionData>} Promise resolving to the prepared transaction data\n * @throws {Error} If the vault key is invalid, if contracts are not properly configured,\n * or if the specified chain or token is not supported\n */\nconst prepareWithdrawTransactionData = async ({\n vaultKey,\n chainId,\n wantTokenSymbol,\n offerAmount,\n deadline = DEFAULT_DEADLINE,\n slippage,\n}: PrepareWithdrawTransactionDataParams): Promise<WithdrawTransactionData> => {\n const vault = await getVaultByKey(vaultKey);\n const normalizedChainId = toChainId(chainId);\n if (!vault) {\n throw new Error(`Invalid vault key: ${vaultKey}`);\n }\n\n if (!vault.contracts) {\n throw new Error(`Contracts not configured for vault ${vaultKey}`);\n }\n\n // Check if contracts exist\n if (!vault.contracts.boringVault) {\n throw new Error(\n `BoringVault contract not configured for vault ${vaultKey}`\n );\n }\n const boringVaultAddress = vault.contracts.boringVault;\n\n if (!vault.contracts.accountant) {\n throw new Error(`Accountant contract not configured for vault ${vaultKey}`);\n }\n const accountantAddress = vault.contracts.accountant;\n\n // Updated chain validation\n const destinationChain =\n vault.withdraw.sourceChains[normalizedChainId].destinationChains[\n normalizedChainId\n ];\n const wantTokens = destinationChain.wantTokens;\n if (!!wantTokens && !wantTokens[wantTokenSymbol]) {\n throw new Error(\n `Invalid want token \"${wantTokenSymbol}\" for vault ${vaultKey}. ` +\n `Allowed tokens are: ${Object.keys(wantTokens).join(\", \")}`\n );\n }\n\n const wantAssetAddress = wantTokens[wantTokenSymbol].address;\n if (!wantAssetAddress) {\n throw new Error(`Invalid want token symbol: ${wantTokenSymbol}`);\n }\n\n const [wantAssetDecimals, sharesAssetDecimals, rateInQuote] =\n await getRateInQuoteAndSharesAndWantAssetDecimals({\n sharesAssetAddress: boringVaultAddress,\n wantAssetAddress: wantAssetAddress,\n accountantAddress: accountantAddress,\n chainId: normalizedChainId,\n });\n\n if (wantAssetDecimals.status === \"failure\") {\n throw new Error(wantAssetDecimals.error.message);\n }\n\n if (rateInQuote.status === \"failure\") {\n throw new Error(rateInQuote.error.message);\n }\n\n if (sharesAssetDecimals.status === \"failure\") {\n throw new Error(sharesAssetDecimals.error.message);\n }\n\n const formattedOfferAmount = parseUnits(\n offerAmount,\n sharesAssetDecimals.result\n );\n\n const {\n data: solverSlippage,\n error: solverSlippageError,\n success: solverSlippageSuccess,\n } = await tryCatch(\n getAssetSlippage(normalizedChainId, boringVaultAddress, wantAssetAddress)\n );\n if (!solverSlippageSuccess) {\n throw new Error(\n `Error getting solver slippage: ${solverSlippageError.message}`\n );\n }\n const effectiveSlippage = slippage ?? solverSlippage;\n\n const atomicPrice = calculateAtomicPrice(\n rateInQuote.result,\n wantAssetDecimals.result,\n effectiveSlippage\n );\n\n const userRequest = prepareUserRequest(\n formattedOfferAmount,\n atomicPrice,\n deadline\n );\n\n return {\n abi: AtomicQueueAbi,\n address: ATOMIC_QUEUE_CONTRACT_ADDRESS,\n functionName: \"updateAtomicRequest\",\n args: [boringVaultAddress, wantAssetAddress, userRequest],\n chainId: normalizedChainId,\n };\n};\n\n/**\n * Parameters for preparing a cross-chain bridge and withdrawal transaction\n * @interface PrepareBridgeAndWithdrawTransactionDataParams\n * @extends {Omit<PrepareWithdrawTransactionDataParams, 'chainId'>}\n * @property {ChainId} sourceChainId - Chain ID where shares currently exist\n * @property {ChainId} destinationChainId - Chain ID where assets will be withdrawn\n * @property {number} offerAmount - Amount of vault shares to withdraw\n * @property {number} [deadline] - Unix timestamp when the request expires (optional)\n * @property {number} [slippage] - Maximum acceptable slippage percentage (optional)\n * @property {Address} userAddress - Ethereum address of the user making the withdrawal\n * @property {VaultKey} vaultKey - Unique identifier for the vault\n * @property {TokenKey} wantTokenSymbol - Symbol of the token the user wants to receive\n */\ninterface PrepareBridgeAndWithdrawTransactionDataParams\n extends Omit<PrepareWithdrawTransactionDataParams, \"chainId\"> {\n sourceChainId: ChainId;\n destinationChainId: ChainId;\n}\n\n/**\n * Combined result containing both bridge and withdrawal transaction data\n * @interface BridgeAndWithdrawResult\n */\ninterface BridgeAndWithdrawTransactionData {\n bridgeTransactionData: BridgeTransactionData; // Data for bridging shares to destination chain\n withdrawTransactionData: WithdrawTransactionData; // Data for withdraw request on destination chain\n}\n\n/**\n * Prepares transaction data for cross-chain withdrawals from a vault\n *\n * This function handles both:\n * 1. Bridging vault shares from source chain to destination chain\n * 2. Creating a withdraw request on the destination chain\n *\n * The withdraw request can be submitted before the bridge completes - the solver\n * will wait for bridged shares to arrive before processing the request.\n *\n * @example\n * ```typescript\n * const result = await prepareBridgeAndWithdrawData({\n * vaultKey: 'bobaeth',\n * sourceChainId: 42161, // Arbitrum\n * destinationChainId: 1, // Ethereum\n * userAddress: '0x1234...',\n * wantTokenSymbol: TokenKey.ETH,\n * offerAmount: 1.5, // 1.5 shares\n * deadline: Math.floor(Date.now() / 1000) + 3600, // 1 hour\n * slippage: 0.005 // 0.5%\n * });\n * ```\n *\n * @param {PrepareBridgeAndWithdrawParams} params - Parameters for the cross-chain withdraw\n * @returns {Promise<BridgeAndWithdrawResult>} Transaction data for both bridge and withdraw operations\n * @throws {Error} If vault configuration is invalid or if preparing either transaction fails\n */\nconst prepareBridgeAndWithdrawTransactionData = async ({\n deadline = DEFAULT_DEADLINE,\n destinationChainId,\n offerAmount,\n slippage = DEFAULT_WITHDRAW_SLIPPAGE,\n sourceChainId,\n userAddress,\n vaultKey,\n wantTokenSymbol,\n}: PrepareBridgeAndWithdrawTransactionDataParams): Promise<BridgeAndWithdrawTransactionData> => {\n const vault = await getVaultByKey(vaultKey);\n if (!vault) {\n throw new Error(`Invalid vault key: ${vaultKey}`);\n }\n\n const {\n data: withdrawTransactionData,\n error: withdrawError,\n success: withdrawSuccess,\n } = await tryCatch(\n prepareWithdrawTransactionData({\n vaultKey,\n chainId: destinationChainId,\n userAddress,\n wantTokenSymbol,\n offerAmount,\n deadline,\n slippage,\n })\n );\n\n if (!withdrawSuccess) {\n throw new Error(withdrawError.message);\n }\n\n const { args } = withdrawTransactionData;\n const [, , userRequest] = args;\n const { offerAmount: bridgeAmount } = userRequest;\n\n const {\n data: bridgeTransactionData,\n error: bridgeError,\n success: bridgeSuccess,\n } = await tryCatch(\n prepareBridgeTransactionData({\n vaultKey,\n bridgeAmount: bridgeAmount,\n sourceChainId,\n destinationChainId,\n userAddress,\n })\n );\n\n if (!bridgeSuccess) {\n throw new Error(bridgeError.message);\n }\n\n return {\n bridgeTransactionData: bridgeTransactionData,\n withdrawTransactionData: withdrawTransactionData,\n };\n};\n\ninterface PrepareApproveWithdrawTokenParams {\n vaultKey: VaultKey;\n withdrawAmount?: string;\n chainId: ChainId;\n}\n\ninterface ApproveWithdrawTokenTransactionData {\n abi: typeof BoringVaultAbi;\n address: Address;\n functionName: \"approve\";\n args: [Address, bigint];\n}\n\nconst prepareApproveWithdrawToken = async ({\n vaultKey,\n withdrawAmount,\n chainId,\n}: PrepareApproveWithdrawTokenParams): Promise<ApproveWithdrawTokenTransactionData> => {\n const normalizedChainId = toChainId(chainId);\n const vault = await getVaultByKey(vaultKey);\n\n if (!vault) {\n throw new Error(`Invalid vault key: ${vaultKey}`);\n }\n\n if (!vault.contracts) {\n throw new Error(`Contracts not configured for vault ${vaultKey}`);\n }\n\n if (!vault.contracts.boringVault) {\n throw new Error(`Teller contract not configured for vault ${vaultKey}`);\n }\n const withdrawAssetAddress = vault.contracts.boringVault;\n\n const {\n data: withdrawAssetDecimals,\n error: withdrawAssetDecimalsError,\n success: withdrawAssetDecimalsSuccess,\n } = await tryCatch(\n getErc20Decimals({\n chainId: normalizedChainId,\n tokenAddress: withdrawAssetAddress,\n })\n );\n\n if (!withdrawAssetDecimalsSuccess) {\n throw new Error(\n `Error getting withdraw asset decimals. ${withdrawAssetDecimalsError.name}: ${withdrawAssetDecimalsError.message}`\n );\n }\n\n if (!withdrawAssetDecimals) {\n throw new Error(`Invalid withdraw token symbol: ${withdrawAssetAddress}`);\n }\n\n const withdrawAmountAsBitInt = withdrawAmount\n ? parseUnits(withdrawAmount, withdrawAssetDecimals)\n : DEFAULT_APPROVAL_AMOUNT;\n\n return {\n abi: BoringVaultAbi,\n address: withdrawAssetAddress,\n functionName: \"approve\",\n args: [ATOMIC_QUEUE_CONTRACT_ADDRESS, withdrawAmountAsBitInt],\n };\n};\n\nexport {\n prepareApproveWithdrawToken,\n prepareBridgeAndWithdrawTransactionData,\n prepareWithdrawTransactionData,\n};\n\nexport type {\n ApproveWithdrawTokenTransactionData,\n BridgeAndWithdrawTransactionData,\n PrepareBridgeAndWithdrawTransactionDataParams,\n PrepareWithdrawTransactionDataParams,\n WithdrawTransactionData,\n};\n"],"mappings":"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","names":[]}