UNPKG

saros-sdk

Version:
900 lines (834 loc) 26 kB
/* eslint-disable new-cap */ import { SarosSwapInstructionService } from './sarosSwapIntructions'; import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js'; import { cloneDeep } from 'lodash'; import BN from 'bn.js'; import { HOST_FEE_DENOMINATOR, HOST_FEE_NUMERATOR, INITIALIZE_MINT_SPAN, INITIALIZE_POOL_SPAN, NATIVE_SOL, OWNER_TRADING_FEE_DENOMINATOR, OWNER_TRADING_FEE_NUMERATOR, OWNER_WITHDRAW_FEE_DENOMINATOR, OWNER_WITHDRAW_FEE_NUMERATOR, SAROS_SWAP_PROGRAM_ADDRESS_V1, TOKEN_PROGRAM_ID, TRADING_FEE_DENOMINATOR, TRADING_FEE_NUMERATOR, } from '../constants'; import { TokenProgramInstructionService } from '../common/tokenProgramInstructionService'; import { closeAccount } from '@project-serum/serum/lib/token-instructions'; import { convertBalanceToWei, convertWeiToBalance, renderAmountSlippage, } from '../functions'; import { findAssociatedTokenAddress, getTokenAccountInfo, getTokenMintInfo, genOwnerSolana, messFail, createAssociatedTokenAccountIfNotExist, deserializeAccount, createTransactions, sendTransaction, isAddressInUse, } from '../common'; export const getPoolInfo = async (connection, poolAddress) => { const accountInfo = await connection.getAccountInfo(poolAddress); if (!accountInfo) return null; return SarosSwapInstructionService.decodePoolData(accountInfo.data); }; export const tradingTokensToPoolTokens = ( sourceAmount, swapSourceAmount, poolAmount ) => { const tradingFee = (sourceAmount / 2) * (TRADING_FEE_NUMERATOR.toNumber() / TRADING_FEE_DENOMINATOR.toNumber()); const sourceAmountPostFee = sourceAmount - tradingFee; const root = Math.sqrt(sourceAmountPostFee / swapSourceAmount + 1); return Math.floor(poolAmount * (root - 1)); }; export const findPoolSeed = (tokenSwapProgramId) => { const newToken = Keypair.generate(); return PublicKey.findProgramAddress( [newToken.publicKey.toBuffer()], tokenSwapProgramId ); }; export const createPoolSeed = (address, tokenSwapProgramId) => { return PublicKey.createProgramAddress( [address.toBuffer()], tokenSwapProgramId ); }; export const findPoolAuthorityAddress = (poolAddress, tokenSwapProgramId) => { return PublicKey.findProgramAddress( [poolAddress.toBuffer()], tokenSwapProgramId ); }; export const createPool = async ( connection, owner, feeOwnerAddress, token0MintAddress, token1MintAddress, token0Address, token1Address, token0Amount, token1Amount, curveType, curveParameters, tokenProgramId, sarosSwapProgramId ) => { const transaction = await createTransactions(connection, owner); const payerAccount = await genOwnerSolana(owner); const [poolAccountSeed] = await findPoolSeed(sarosSwapProgramId); const poolAccount = Keypair.fromSeed(poolAccountSeed.toBuffer()); const [poolAuthorityAddress] = await findPoolAuthorityAddress( poolAccount.publicKey, sarosSwapProgramId ); const poolLpMintAccount = Keypair.fromSeed(poolAuthorityAddress.toBuffer()); if (!(await isAddressInUse(connection, poolLpMintAccount.publicKey))) { const lamportsToInitializeMint = await connection.getMinimumBalanceForRentExemption(INITIALIZE_MINT_SPAN); const initMintTransaction = await TokenProgramInstructionService.createInitializeMintTransaction( payerAccount.publicKey, poolLpMintAccount.publicKey, 2, poolAuthorityAddress, null, lamportsToInitializeMint ); transaction.add(initMintTransaction.instructions[0]); transaction.add(initMintTransaction.instructions[1]); } const poolToken0Address = await findAssociatedTokenAddress( poolAuthorityAddress, token0MintAddress ); if (!(await isAddressInUse(connection, poolToken0Address))) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( payerAccount.publicKey, poolAuthorityAddress, token0MintAddress ); transaction.add(createATPATransaction.instructions[0]); } const poolToken1Address = await findAssociatedTokenAddress( poolAuthorityAddress, token1MintAddress ); if (!(await isAddressInUse(connection, poolToken1Address))) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( payerAccount.publicKey, poolAuthorityAddress, token1MintAddress ); transaction.add(createATPATransaction.instructions[0]); } const poolLpTokenAddress = await findAssociatedTokenAddress( payerAccount.publicKey, poolLpMintAccount.publicKey ); if (!(await isAddressInUse(connection, poolLpTokenAddress))) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( payerAccount.publicKey, payerAccount.publicKey, poolLpMintAccount.publicKey ); transaction.add(createATPATransaction.instructions[0]); } const feeLpTokenAddress = await findAssociatedTokenAddress( feeOwnerAddress, poolLpMintAccount.publicKey ); if ( payerAccount.publicKey.toBase58() !== feeOwnerAddress.toBase58() && !(await isAddressInUse(connection, feeLpTokenAddress)) ) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( payerAccount.publicKey, feeOwnerAddress, poolLpMintAccount.publicKey ); transaction.add(createATPATransaction.instructions[0]); } const transferToken0Transaction = await TokenProgramInstructionService.createTransferTransaction( payerAccount.publicKey, token0Address, poolToken0Address, token0Amount ); transaction.add(transferToken0Transaction.instructions[0]); const transferToken1Transaction = await TokenProgramInstructionService.createTransferTransaction( payerAccount.publicKey, token1Address, poolToken1Address, token1Amount ); transaction.add(transferToken1Transaction.instructions[0]); const lamportsToCreatePool = await connection.getMinimumBalanceForRentExemption(INITIALIZE_POOL_SPAN); transaction.add( SystemProgram.createAccount({ fromPubkey: payerAccount.publicKey, newAccountPubkey: poolAccount.publicKey, lamports: lamportsToCreatePool, space: INITIALIZE_POOL_SPAN, programId: sarosSwapProgramId, }) ); const tokenSwapInstruction = SarosSwapInstructionService.createInitSwapInstruction( poolAccount, poolAuthorityAddress, poolToken0Address, poolToken1Address, poolLpMintAccount.publicKey, feeLpTokenAddress, poolLpTokenAddress, tokenProgramId, sarosSwapProgramId, TRADING_FEE_NUMERATOR, TRADING_FEE_DENOMINATOR, OWNER_TRADING_FEE_NUMERATOR, OWNER_TRADING_FEE_DENOMINATOR, OWNER_WITHDRAW_FEE_NUMERATOR, OWNER_WITHDRAW_FEE_DENOMINATOR, HOST_FEE_NUMERATOR, HOST_FEE_DENOMINATOR, curveType, curveParameters ); transaction.add(tokenSwapInstruction); if (token0MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: token0Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } if (token1MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: token1Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } const result = await sendTransaction(connection, transaction, [ payerAccount.publicKey, poolLpMintAccount, poolAccount, ]); if (messFail.includes(result)) { return { isError: true, mess: result }; } else { return { isError: false, hash: result, poolAccount }; } }; export const withdrawAllTokenTypes = async ( connection, walletAddress, userLpTokenAddress, userToken0Address, userToken1Address, lpTokenAmount, poolAddress, tokenSwapProgramId, token0MintAddress, token1MintAddress, slippage ) => { const payerAccount = await genOwnerSolana(walletAddress); const delegateAccount = await genOwnerSolana(walletAddress); const transaction = await createTransactions(connection, walletAddress); const [poolAuthorityAddress] = await findPoolAuthorityAddress( poolAddress, tokenSwapProgramId ); const poolAccountInfo = await getPoolInfo(connection, poolAddress); const poolLpMintInfo = await getTokenMintInfo( connection, poolAccountInfo.lpTokenMint ); const lpTokenSupply = poolLpMintInfo.supply.toNumber(); let feeAmount = 0; if (OWNER_WITHDRAW_FEE_NUMERATOR.toNumber() !== 0) { feeAmount = Math.floor( (lpTokenAmount * OWNER_WITHDRAW_FEE_NUMERATOR.toNumber()) / OWNER_WITHDRAW_FEE_DENOMINATOR.toNumber() ); } const withdrawLpTokenAmount = lpTokenAmount - feeAmount; const poolToken0AccountInfo = await getTokenAccountInfo( connection, poolAccountInfo.token0Account ); const newAmount0 = Math.floor( (poolToken0AccountInfo.amount.toNumber() * withdrawLpTokenAmount) / lpTokenSupply ); const token0Amount = Math.floor( newAmount0 - renderAmountSlippage(newAmount0, slippage) ); const poolToken1AccountInfo = await getTokenAccountInfo( connection, poolAccountInfo.token1Account ); const newAmount1 = Math.floor( (poolToken1AccountInfo.amount.toNumber() * withdrawLpTokenAmount) / lpTokenSupply ); const token1Amount = Math.floor( newAmount1 - renderAmountSlippage(newAmount1, slippage) ); const withdrawInstruction = SarosSwapInstructionService.withdrawAllTokenTypesInstruction( poolAddress, poolAuthorityAddress, delegateAccount.publicKey, poolAccountInfo.lpTokenMint, poolAccountInfo.feeAccount, userLpTokenAddress, poolAccountInfo.token0Account, poolAccountInfo.token1Account, userToken0Address, userToken1Address, tokenSwapProgramId, TOKEN_PROGRAM_ID, new BN(lpTokenAmount), new BN(token0Amount), new BN(token1Amount) ); transaction.add(withdrawInstruction); if (token0MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: userToken0Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } if (token1MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: userToken1Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } const result = await sendTransaction(connection, transaction, [ delegateAccount, ]); if (messFail.includes(result)) { return { isError: true, mess: result }; } else { return { isError: false, hash: result }; } }; export const depositAllTokenTypes = async ( connection, walletAddress, userAddress, userToken0Address, userToken1Address, lpTokenAmount, poolAddress, tokenSwapProgramId, token0MintAddress, token1MintAddress, slippage ) => { const transaction = await createTransactions(connection, walletAddress); const poolAccountInfo = await getPoolInfo(connection, poolAddress); const payerAccount = await genOwnerSolana(walletAddress); const delegateAccount = await genOwnerSolana(walletAddress); const poolLpMintInfo = await getTokenMintInfo( connection, poolAccountInfo.lpTokenMint ); const lpTokenSupply = poolLpMintInfo.supply.toNumber(); const poolToken0AccountInfo = await getTokenAccountInfo( connection, poolAccountInfo.token0Account ); const newAmount0 = Math.floor( (poolToken0AccountInfo.amount.toNumber() * lpTokenAmount) / lpTokenSupply ); const token0Amount = Math.floor( newAmount0 + renderAmountSlippage(newAmount0, slippage) ); const poolToken1AccountInfo = await getTokenAccountInfo( connection, poolAccountInfo.token1Account ); const newAmount1 = Math.floor( (poolToken1AccountInfo.amount.toNumber() * lpTokenAmount) / lpTokenSupply ); const token1Amount = Math.floor( newAmount1 + renderAmountSlippage(newAmount0, slippage) ); const [poolAuthorityAddress] = await findPoolAuthorityAddress( poolAddress, tokenSwapProgramId ); const userLpTokenAddress = await findAssociatedTokenAddress( userAddress, poolAccountInfo.lpTokenMint ); if (!(await isAddressInUse(connection, userLpTokenAddress))) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( payerAccount.publicKey, userAddress, poolAccountInfo.lpTokenMint ); transaction.add(createATPATransaction.instructions[0]); } const depositInstruction = await SarosSwapInstructionService.depositAllTokenTypesInstruction( poolAddress, poolAuthorityAddress, delegateAccount.publicKey, userToken0Address, userToken1Address, poolAccountInfo.token0Account, poolAccountInfo.token1Account, poolAccountInfo.lpTokenMint, userLpTokenAddress, tokenSwapProgramId, TOKEN_PROGRAM_ID, new BN(lpTokenAmount), new BN(token0Amount), new BN(token1Amount) ); transaction.add(depositInstruction); if (token0MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: userToken0Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } if (token1MintAddress.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: userToken1Address, destination: payerAccount.publicKey, owner: payerAccount.publicKey, }) ); } const result = await sendTransaction(connection, transaction, [ delegateAccount, ]); if (messFail.includes(result)) { return { isError: true, mess: result }; } else { return { isError: false, hash: result }; } }; export const swapSaros = async ( connection, userTokenSourceAddress, userTokenDestinationAddress, amountFrom, minimumAmountTo, hostFeeOwnerAddress, poolAddress, walletAddress, fromCoinMint, toCoinMint ) => { const amountIn = convertBalanceToWei(amountFrom); const minimumAmountOut = convertBalanceToWei(minimumAmountTo); const transaction = await createTransactions(connection, walletAddress); const tokenSwapProgramId = SAROS_SWAP_PROGRAM_ADDRESS_V1; const owner = await genOwnerSolana(walletAddress); const signers = [owner.publicKey]; const [poolAuthorityAddress] = await findPoolAuthorityAddress( poolAddress, tokenSwapProgramId ); const poolAccountInfo = await getPoolInfo(connection, poolAddress); let fromMint = fromCoinMint; let toMint = toCoinMint; if (fromMint === NATIVE_SOL.mintAddress) { fromMint = NATIVE_SOL.mintAddress; } if (toMint === NATIVE_SOL.mintAddress) { toMint = NATIVE_SOL.mintAddress; } const newFromAccount = await createAssociatedTokenAccountIfNotExist( userTokenSourceAddress.toString(), new PublicKey(walletAddress), fromMint, transaction ); const newToAccount = await createAssociatedTokenAccountIfNotExist( userTokenDestinationAddress.toString(), new PublicKey(walletAddress), toMint, transaction ); let poolTokenSourceAddress = null; let poolTokenDestinationAddress = null; if (fromMint === poolAccountInfo.token0Mint.toBase58()) { poolTokenSourceAddress = poolAccountInfo.token0Account; } if (fromMint === poolAccountInfo.token1Mint.toBase58()) { poolTokenSourceAddress = poolAccountInfo.token1Account; } if (toMint === poolAccountInfo.token0Mint.toBase58()) { poolTokenDestinationAddress = poolAccountInfo.token0Account; } if (toMint === poolAccountInfo.token1Mint.toBase58()) { poolTokenDestinationAddress = poolAccountInfo.token1Account; } let hostFeeTokenAddress = null; if (hostFeeOwnerAddress) { hostFeeTokenAddress = await findAssociatedTokenAddress( hostFeeOwnerAddress, poolAccountInfo.lpTokenMint ); if (!(await isAddressInUse(connection, hostFeeTokenAddress))) { const createATPATransaction = await TokenProgramInstructionService.createAssociatedTokenAccountTransaction( owner.publicKey, hostFeeOwnerAddress, poolAccountInfo.lpTokenMint ); transaction.add(createATPATransaction.instructions[0]); } } const swapInstruction = await SarosSwapInstructionService.createSwapInstruction( poolAddress, poolAuthorityAddress, owner.publicKey, newFromAccount, poolTokenSourceAddress, poolTokenDestinationAddress, newToAccount, poolAccountInfo.lpTokenMint, poolAccountInfo.feeAccount, hostFeeTokenAddress, tokenSwapProgramId, TOKEN_PROGRAM_ID, new BN(amountIn), new BN(minimumAmountOut) ); transaction.add(swapInstruction); if (fromMint.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: newFromAccount, destination: owner.publicKey, owner: owner.publicKey, }) ); } if (toMint.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: newToAccount, destination: owner.publicKey, owner: owner.publicKey, }) ); } const hash = await sendTransaction(connection, transaction, signers); if (messFail.includes(hash)) { return { isError: true, mess: hash }; } return { isError: false, mess: hash }; }; export const swapRouteSaros = async ( connection, userTokenSourceAddress, middleTokenAddress, userTokenDestinationAddress, amountIn, minimumAmountOut, middleAmount, hostFeeOwnerAddress, poolAddress, poolAddressB, tokenSwapProgramId, walletAddress, fromCoinMint, toCoinMint, middleCoinMint, toastNotiWait, transaction ) => { const owner = await genOwnerSolana(walletAddress); const signers = [owner.publicKey]; const [poolAuthorityAddress] = await findPoolAuthorityAddress( poolAddress, tokenSwapProgramId ); const [poolAuthorityAddressB] = await findPoolAuthorityAddress( poolAddressB, tokenSwapProgramId ); const poolAccountInfo = await getPoolInfo(connection, poolAddress); const poolAccountInfoB = await getPoolInfo(connection, poolAddressB); let fromMint = fromCoinMint; let toMint = toCoinMint; let middleMint = middleCoinMint; if (fromMint === NATIVE_SOL.mintAddress) { fromMint = NATIVE_SOL.mintAddress; } if (middleMint === NATIVE_SOL.mintAddress) { middleMint = NATIVE_SOL.mintAddress; } if (toMint === NATIVE_SOL.mintAddress) { toMint = NATIVE_SOL.mintAddress; } const newFromAccount = await createAssociatedTokenAccountIfNotExist( userTokenSourceAddress.toString(), new PublicKey(walletAddress), fromMint, transaction ); const newMiddleAccount = await createAssociatedTokenAccountIfNotExist( middleTokenAddress.toString(), new PublicKey(walletAddress), middleMint, transaction ); const newToAccount = await createAssociatedTokenAccountIfNotExist( userTokenDestinationAddress.toString(), new PublicKey(walletAddress), toMint, transaction ); let poolTokenSourceAddress = null; let poolTokenSourceAddressB = null; let poolTokenDestinationAddress = null; let poolTokenMiddleAddress = null; if (fromMint === poolAccountInfo.token0Mint.toBase58()) { poolTokenSourceAddress = poolAccountInfo.token0Account; } if (fromMint === poolAccountInfo.token1Mint.toBase58()) { poolTokenSourceAddress = poolAccountInfo.token1Account; } if (middleMint === poolAccountInfo.token0Mint.toBase58()) { poolTokenMiddleAddress = poolAccountInfo.token0Account; } if (middleMint === poolAccountInfo.token1Mint.toBase58()) { poolTokenMiddleAddress = poolAccountInfo.token1Account; } if (middleMint === poolAccountInfoB.token0Mint.toBase58()) { poolTokenSourceAddressB = poolAccountInfoB.token0Account; } if (middleMint === poolAccountInfoB.token1Mint.toBase58()) { poolTokenSourceAddressB = poolAccountInfoB.token1Account; } if (toMint === poolAccountInfoB.token0Mint.toBase58()) { poolTokenDestinationAddress = poolAccountInfoB.token0Account; } if (toMint === poolAccountInfoB.token1Mint.toBase58()) { poolTokenDestinationAddress = poolAccountInfoB.token1Account; } const swapInstruction = await SarosSwapInstructionService.createSwapInstruction( poolAddress, poolAuthorityAddress, owner.publicKey, newFromAccount, poolTokenSourceAddress, poolTokenMiddleAddress, newMiddleAccount, poolAccountInfo.lpTokenMint, poolAccountInfo.feeAccount, null, tokenSwapProgramId, TOKEN_PROGRAM_ID, new BN(amountIn), new BN(middleAmount) ); const swapInstructionB = await SarosSwapInstructionService.createSwapInstruction( poolAddressB, poolAuthorityAddressB, owner.publicKey, newMiddleAccount, poolTokenSourceAddressB, poolTokenDestinationAddress, newToAccount, poolAccountInfoB.lpTokenMint, poolAccountInfoB.feeAccount, null, tokenSwapProgramId, TOKEN_PROGRAM_ID, new BN(middleAmount), new BN(minimumAmountOut) ); transaction.add(swapInstruction); transaction.add(swapInstructionB); if (fromMint.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: newFromAccount, destination: owner.publicKey, owner: owner.publicKey, }) ); } if (middleMint.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: newMiddleAccount, destination: owner.publicKey, owner: owner.publicKey, }) ); } if (toMint.toString() === NATIVE_SOL.mintAddress) { transaction.add( closeAccount({ source: newToAccount, destination: owner.publicKey, owner: owner.publicKey, }) ); } const hash = await sendTransaction( connection, transaction, signers, toastNotiWait ); if (messFail.includes(hash)) { return { isError: true, mess: hash }; } return hash; }; export const getInfoByMintAddress = (mintAddress, lisToken) => { const info = lisToken.find((item) => item.mintAddress === mintAddress); return info; }; export const getSwapAmountSaros = async ( connection, fromCoinMint, toCoinMint, amount, slippage, poolParams ) => { const { address, tokens } = poolParams; const poolInfo = await getPoolInfo( connection, new PublicKey(address.toString()) ); const { token0Account, token1Account, tradeFeeDenominator, tradeFeeNumerator, } = poolInfo; const fromCoinMintClone = cloneDeep(fromCoinMint); const toCoinMintClone = cloneDeep(toCoinMint); const newSlippage = parseFloat(slippage); const newAmount = parseFloat(amount); const accountInfos = await Promise.all([ connection.getAccountInfo(new PublicKey(token0Account.toString())), connection.getAccountInfo(new PublicKey(token1Account.toString())), ]); const tokenInfos = accountInfos.map((info) => info ? deserializeAccount(info.data) : undefined ); if (!tokenInfos[0] || !tokenInfos[1]) return 0; const inputTokenAccount = tokenInfos[0]; const inputTokenInfo = tokens[inputTokenAccount.mint.toString()]; const outputTokenAccount = tokenInfos[1]; const outputTokenInfo = tokens[outputTokenAccount.mint.toString()]; const convertAmountInputToken = parseFloat( convertWeiToBalance( inputTokenAccount.amount.toNumber(), inputTokenInfo.decimals ) ); const convertAmountOutputToken = parseFloat( convertWeiToBalance( outputTokenAccount.amount.toNumber(), outputTokenInfo.decimals ) ); let fromAmountWithFee = (newAmount * (tradeFeeDenominator.toNumber() - tradeFeeNumerator.toNumber())) / tradeFeeDenominator; if (tradeFeeDenominator.toNumber() === 0 || tradeFeeNumerator.toNumber()) { fromAmountWithFee = newAmount; } const rateEst = convertAmountOutputToken / convertAmountInputToken; if ( fromCoinMintClone === inputTokenInfo.mintAddress && toCoinMintClone === outputTokenInfo.mintAddress ) { const denominator = convertAmountInputToken + fromAmountWithFee; const amountOut = (convertAmountOutputToken * fromAmountWithFee) / denominator; const amountOutWithSlippage = amountOut / (1 + newSlippage / 100); const outBalance = convertAmountOutputToken - amountOut; const beforePrice = convertAmountOutputToken / convertAmountInputToken; const afterPrice = outBalance / denominator; const priceImpact = Math.abs( ((parseFloat(beforePrice) - parseFloat(afterPrice)) / parseFloat(beforePrice)) * 100 ); return { amountOut, amountOutWithSlippage, priceImpact, rate: rateEst, }; } else { const denominator = convertAmountOutputToken + fromAmountWithFee; const amountOut = (convertAmountInputToken * fromAmountWithFee) / denominator; const amountOutWithSlippage = amountOut / (1 + newSlippage / 100); const outBalance = convertAmountInputToken - amountOut; const beforePrice = convertAmountOutputToken / convertAmountInputToken; const afterPrice = denominator / outBalance; const priceImpact = Math.abs( ((parseFloat(afterPrice) - parseFloat(beforePrice)) / parseFloat(beforePrice)) * 100 ); return { amountOut, amountOutWithSlippage, priceImpact, rate: 1 / rateEst, }; } };