diff --git a/packages/frontend/src/state/lp/hooks.ts b/packages/frontend/src/state/lp/hooks.ts index ceaf48c9e..e82e2efce 100644 --- a/packages/frontend/src/state/lp/hooks.ts +++ b/packages/frontend/src/state/lp/hooks.ts @@ -19,6 +19,7 @@ import { ethers } from 'ethers' import { useCallback } from 'react' import { useGetDebtAmount, useGetVault } from '../controller/hooks' import { indexAtom, normFactorAtom } from '../controller/atoms' +import { getPoolState } from '../squeethPool/hooks' /*** CONSTANTS ***/ const COLLAT_RATIO_FLASHLOAN = 2 @@ -77,8 +78,8 @@ export const useOpenPositionDeposit = () => { const amount0Min = amount0New.times(new BigNumber(1).minus(slippage)).toFixed(0) const amount1Min = amount1New.times(new BigNumber(1).minus(slippage)).toFixed(0) - const collateralToWithdraw = fromTokenAmount(withdrawAmount, OSQUEETH_DECIMALS) - const ethIndexPrice = toTokenAmount(index, 18).sqrt() + const collateralToWithdraw = fromTokenAmount(withdrawAmount, WETH_DECIMALS) + const ethIndexPrice = toTokenAmount(index, WETH_DECIMALS).sqrt() const vaultShortAmt = fromTokenAmount(vaultBefore.shortAmount, OSQUEETH_DECIMALS) const vaultCollateralAmt = fromTokenAmount(vaultBefore.collateralAmount, WETH_DECIMALS) @@ -135,6 +136,130 @@ export const useOpenPositionDeposit = () => { return openPositionDeposit } +// Close position with flashloan +export const useFlashClosePosition = () => { + const address = useAtomValue(addressAtom) + const controllerHelperContract = useAtomValue(controllerHelperHelperContractAtom) + const controllerContract = useAtomValue(controllerContractAtom) + const handleTransaction = useHandleTransaction() + const getDebtAmount = useGetDebtAmount() + const getVault = useGetVault() + const getPosition = useGetPosition() + const getExactIn = useGetExactIn() + const getExactOut = useGetExactOut() + const getDecreaseLiquidity = useGetDecreaseLiquidity() + const isWethToken0 = useAtomValue(isWethToken0Atom) + const index = useAtomValue(indexAtom) + const normFactor = useAtomValue(normFactorAtom) + const flashClosePosition = useAppCallback( + async ( + vaultId: number, + liquidityPercentage: number, + burnPercentage: number, + withdrawAmount: number, + burnExactRemoved: boolean, + slippage: number, + onTxConfirmed?: () => void, + ) => { + const vaultBefore = await getVault(vaultId) + const uniTokenId = vaultBefore?.NFTCollateralId + const position = await getPosition(uniTokenId) + + if ( + !controllerContract || + !controllerHelperContract || + !address || + !position || + !vaultBefore || + !vaultBefore.shortAmount + ) + return + + const shortAmount = fromTokenAmount(vaultBefore.shortAmount, OSQUEETH_DECIMALS) + + // Get current LP positions + const { amount0, amount1 } = await getDecreaseLiquidity( + uniTokenId, + position.liquidity, + 0, + 0, + Math.floor(Date.now() / 1000 + 86400), + ) + const wPowerPerpAmountInLP = isWethToken0 ? amount1 : amount0 + + const amountToLiquidate = new BigNumber(wPowerPerpAmountInLP).times(liquidityPercentage) + const amountToBurn = shortAmount.times(burnPercentage) + const limitEth = await (amountToLiquidate.gt(amountToBurn) + ? getExactIn(amountToLiquidate.minus(amountToBurn), true) + : getExactOut(amountToBurn.minus(amountToLiquidate), true)) + + const collateralToWithdraw = fromTokenAmount(withdrawAmount, WETH_DECIMALS) + const ethIndexPrice = toTokenAmount(index, WETH_DECIMALS).sqrt() + const vaultShortAmt = fromTokenAmount(vaultBefore.shortAmount, OSQUEETH_DECIMALS) + const vaultCollateralAmt = fromTokenAmount(vaultBefore.collateralAmount, WETH_DECIMALS) + + const flashLoanAmount = new BigNumber(COLLAT_RATIO_FLASHLOAN + FLASHLOAN_BUFFER) + .times(vaultShortAmt) + .times(normFactor) + .times(ethIndexPrice) + .div(INDEX_SCALE) + .minus(vaultCollateralAmt) + const flashLoanAmountPos = BigNumber.max(flashLoanAmount, 0) + + const limitPrice = new BigNumber(limitEth) + .div(amountToLiquidate.minus(amountToBurn).abs()) + .times(new BigNumber(1).minus(slippage)) + + const amount0Min = new BigNumber(amount0) + .times(liquidityPercentage) + .times(new BigNumber(1).minus(slippage)) + .toFixed(0) + const amount1Min = new BigNumber(amount1) + .times(liquidityPercentage) + .times(new BigNumber(1).minus(slippage)) + .toFixed(0) + + const flashloanCloseVaultLpNftParam = { + vaultId: vaultId, + tokenId: uniTokenId, + liquidity: position.liquidity, + liquidityPercentage: fromTokenAmount(liquidityPercentage, 18).toFixed(0), + wPowerPerpAmountToBurn: amountToBurn.toFixed(0), + collateralToFlashloan: flashLoanAmountPos.toFixed(0), + collateralToWithdraw: collateralToWithdraw.toFixed(0), + limitPriceEthPerPowerPerp: fromTokenAmount(limitPrice, 18).toFixed(0), + amount0Min, + amount1Min, + poolFee: POOL_FEE, + burnExactRemoved, + } + + return handleTransaction( + await controllerHelperContract.methods.flashloanCloseVaultLpNft(flashloanCloseVaultLpNftParam).send({ + from: address, + }), + onTxConfirmed, + ) + }, + [ + address, + controllerHelperContract, + controllerContract, + handleTransaction, + getDebtAmount, + getVault, + getPosition, + getExactIn, + getExactOut, + getDecreaseLiquidity, + isWethToken0, + index, + normFactor, + ], + ) + return flashClosePosition +} + /*** GETTERS ***/ export const useGetPosition = () => { @@ -326,24 +451,27 @@ export const useGetExactOut = () => { return getExactOut } -async function getPoolState(poolContract: Contract) { - const [slot, liquidity, tickSpacing] = await Promise.all([ - poolContract?.methods.slot0().call(), - poolContract?.methods.liquidity().call(), - poolContract.methods.tickSpacing().call(), - ]) - - const PoolState = { - liquidity, - sqrtPriceX96: slot[0], - tick: slot[1], - observationIndex: slot[2], - observationCardinality: slot[3], - observationCardinalityNext: slot[4], - feeProtocol: slot[5], - unlocked: slot[6], - tickSpacing, - } - - return PoolState -} \ No newline at end of file +export const useGetQuote = () => { + const contract = useAtomValue(quoterContractAtom) + const { weth, oSqueeth } = useAtomValue(addressesAtom) + + const getQuote = useCallback( + async (amount: BigNumber, squeethIn: boolean) => { + if (!contract) return null + + const QuoteExactInputSingleParams = { + tokenIn: squeethIn ? oSqueeth : weth, + tokenOut: squeethIn ? weth : oSqueeth, + amountIn: amount.toFixed(0), + fee: 3000, + sqrtPriceLimitX96: 0, + } + + const quote = await contract.methods.quoteExactInputSingle(QuoteExactInputSingleParams).call() + return quote.amountOut + }, + [contract, weth, oSqueeth], + ) + + return getQuote +} diff --git a/packages/frontend/src/state/positions/atoms.ts b/packages/frontend/src/state/positions/atoms.ts index 9ce484913..c429d5fa0 100644 --- a/packages/frontend/src/state/positions/atoms.ts +++ b/packages/frontend/src/state/positions/atoms.ts @@ -95,4 +95,4 @@ export const vaultHistoryUpdatingAtom = atom(false) export const isToHidePnLAtom = atom(false) export const swapsAtom = atom({ swaps: [] }) export const longPositionValueAtom = atom(BIG_ZERO) -export const shortPositionValueAtom = atom(BIG_ZERO) +export const shortPositionValueAtom = atom(BIG_ZERO) \ No newline at end of file