UNPKG

@molecularlabs/nucleus-frontend

Version:
211 lines 6.47 kB
import { parseUnits } from "viem"; import { getRateInQuoteAndSharesAndWantAssetDecimals } from "../../api/accountant"; import { getErc20Decimals } from "../../api/erc20"; import { getAssetSlippage } from "../../api/solver-slippage"; import { toChainId } from "../../api/vault-config"; import { ATOMIC_QUEUE_CONTRACT_ADDRESS, DEFAULT_APPROVAL_AMOUNT, DEFAULT_DEADLINE, DEFAULT_WITHDRAW_SLIPPAGE } from "../../constants"; import { AtomicQueueAbi } from "../../contracts/atomic-queue-abi"; import { BoringVaultAbi } from "../../contracts/boring-vault-abi"; import { tryCatch } from "../../utils/try-catch"; import { prepareBridgeTransactionData } from "../bridge"; import { getVaultByKey } from "../index"; import { calculateAtomicPrice, prepareUserRequest } from "./utils"; const prepareWithdrawTransactionData = async ({ vaultKey, chainId, wantTokenSymbol, offerAmount, deadline = DEFAULT_DEADLINE, slippage }) => { const vault = await getVaultByKey(vaultKey); const normalizedChainId = toChainId(chainId); if (!vault) { throw new Error(`Invalid vault key: ${vaultKey}`); } if (!vault.contracts) { throw new Error(`Contracts not configured for vault ${vaultKey}`); } if (!vault.contracts.boringVault) { throw new Error( `BoringVault contract not configured for vault ${vaultKey}` ); } const boringVaultAddress = vault.contracts.boringVault; if (!vault.contracts.accountant) { throw new Error(`Accountant contract not configured for vault ${vaultKey}`); } const accountantAddress = vault.contracts.accountant; const destinationChain = vault.withdraw.sourceChains[normalizedChainId].destinationChains[normalizedChainId]; const wantTokens = destinationChain.wantTokens; if (!!wantTokens && !wantTokens[wantTokenSymbol]) { throw new Error( `Invalid want token "${wantTokenSymbol}" for vault ${vaultKey}. Allowed tokens are: ${Object.keys(wantTokens).join(", ")}` ); } const wantAssetAddress = wantTokens[wantTokenSymbol].address; if (!wantAssetAddress) { throw new Error(`Invalid want token symbol: ${wantTokenSymbol}`); } const [wantAssetDecimals, sharesAssetDecimals, rateInQuote] = await getRateInQuoteAndSharesAndWantAssetDecimals({ sharesAssetAddress: boringVaultAddress, wantAssetAddress, accountantAddress, chainId: normalizedChainId }); if (wantAssetDecimals.status === "failure") { throw new Error(wantAssetDecimals.error.message); } if (rateInQuote.status === "failure") { throw new Error(rateInQuote.error.message); } if (sharesAssetDecimals.status === "failure") { throw new Error(sharesAssetDecimals.error.message); } const formattedOfferAmount = parseUnits( offerAmount, sharesAssetDecimals.result ); const { data: solverSlippage, error: solverSlippageError, success: solverSlippageSuccess } = await tryCatch( getAssetSlippage(normalizedChainId, boringVaultAddress, wantAssetAddress) ); if (!solverSlippageSuccess) { throw new Error( `Error getting solver slippage: ${solverSlippageError.message}` ); } const effectiveSlippage = slippage ?? solverSlippage; const atomicPrice = calculateAtomicPrice( rateInQuote.result, wantAssetDecimals.result, effectiveSlippage ); const userRequest = prepareUserRequest( formattedOfferAmount, atomicPrice, deadline ); return { abi: AtomicQueueAbi, address: ATOMIC_QUEUE_CONTRACT_ADDRESS, functionName: "updateAtomicRequest", args: [boringVaultAddress, wantAssetAddress, userRequest], chainId: normalizedChainId }; }; const prepareBridgeAndWithdrawTransactionData = async ({ deadline = DEFAULT_DEADLINE, destinationChainId, offerAmount, slippage = DEFAULT_WITHDRAW_SLIPPAGE, sourceChainId, userAddress, vaultKey, wantTokenSymbol }) => { const vault = await getVaultByKey(vaultKey); if (!vault) { throw new Error(`Invalid vault key: ${vaultKey}`); } const { data: withdrawTransactionData, error: withdrawError, success: withdrawSuccess } = await tryCatch( prepareWithdrawTransactionData({ vaultKey, chainId: destinationChainId, userAddress, wantTokenSymbol, offerAmount, deadline, slippage }) ); if (!withdrawSuccess) { throw new Error(withdrawError.message); } const { args } = withdrawTransactionData; const [, , userRequest] = args; const { offerAmount: bridgeAmount } = userRequest; const { data: bridgeTransactionData, error: bridgeError, success: bridgeSuccess } = await tryCatch( prepareBridgeTransactionData({ vaultKey, bridgeAmount, sourceChainId, destinationChainId, userAddress }) ); if (!bridgeSuccess) { throw new Error(bridgeError.message); } return { bridgeTransactionData, withdrawTransactionData }; }; const prepareApproveWithdrawToken = async ({ vaultKey, withdrawAmount, chainId }) => { const normalizedChainId = toChainId(chainId); const vault = await getVaultByKey(vaultKey); if (!vault) { throw new Error(`Invalid vault key: ${vaultKey}`); } if (!vault.contracts) { throw new Error(`Contracts not configured for vault ${vaultKey}`); } if (!vault.contracts.boringVault) { throw new Error(`Teller contract not configured for vault ${vaultKey}`); } const withdrawAssetAddress = vault.contracts.boringVault; const { data: withdrawAssetDecimals, error: withdrawAssetDecimalsError, success: withdrawAssetDecimalsSuccess } = await tryCatch( getErc20Decimals({ chainId: normalizedChainId, tokenAddress: withdrawAssetAddress }) ); if (!withdrawAssetDecimalsSuccess) { throw new Error( `Error getting withdraw asset decimals. ${withdrawAssetDecimalsError.name}: ${withdrawAssetDecimalsError.message}` ); } if (!withdrawAssetDecimals) { throw new Error(`Invalid withdraw token symbol: ${withdrawAssetAddress}`); } const withdrawAmountAsBitInt = withdrawAmount ? parseUnits(withdrawAmount, withdrawAssetDecimals) : DEFAULT_APPROVAL_AMOUNT; return { abi: BoringVaultAbi, address: withdrawAssetAddress, functionName: "approve", args: [ATOMIC_QUEUE_CONTRACT_ADDRESS, withdrawAmountAsBitInt] }; }; export { prepareApproveWithdrawToken, prepareBridgeAndWithdrawTransactionData, prepareWithdrawTransactionData }; //# sourceMappingURL=withdraw.mjs.map