diff --git a/src/app/liquidity/__hooks__/useAddLiquidity.ts b/src/app/liquidity/__hooks__/useAddLiquidity.ts index 6ebc016f..458cdbb9 100755 --- a/src/app/liquidity/__hooks__/useAddLiquidity.ts +++ b/src/app/liquidity/__hooks__/useAddLiquidity.ts @@ -138,6 +138,7 @@ function composeV3InitializationBytes( deadline, sqrtPriceX96, }; + console.log(mintParams); return encodeFunctionData({ abi, functionName: "mint", diff --git a/src/app/liquidity/add-liquidity/__components__/main/addLiquidityV3.tsx b/src/app/liquidity/add-liquidity/__components__/main/addLiquidityV3.tsx index adc52d1a..a8d64285 100755 --- a/src/app/liquidity/add-liquidity/__components__/main/addLiquidityV3.tsx +++ b/src/app/liquidity/add-liquidity/__components__/main/addLiquidityV3.tsx @@ -13,6 +13,7 @@ import { useTransactionToastProvider } from "@/contexts/transactionToastProvider import { ETHER, NFT_POSITION_MANAGER, + PRESETS, STRINGS, TICK_SPACINGS, TICKS, @@ -31,9 +32,7 @@ import { convertETHToWETHIfApplicable, convertWETHToPlainETHIfApplicable, nearestUsableTick, - sqrtPriceToTick, - sqrtPriceX96ToPriceReadable, - tickToPrice, + priceToTick, useGetTokenInfo, } from "@/utils"; import { Check, Minus, Plus, RefreshCcw, ZoomIn, ZoomOut } from "lucide-react"; @@ -212,9 +211,10 @@ const SearchParamsSchema = z.object({ // } export default function AddLiquidityV3() { - const [tickSpacing, setTickSpacing] = useState(TICK_SPACINGS.NARROW); - const [minPrice, setMinPrice] = useState(0); - const [maxPrice, setMaxPrice] = useState(0); + const [tickSpacing, setTickSpacing] = useState(TICK_SPACINGS.FIFTY); + const [preset, setPreset] = useState("NARROW"); + const [minPrice, setMinPrice] = useState("0"); + const [maxPrice, setMaxPrice] = useState("0"); const [amount0, setAmount0] = useState("0"); const [amount1, setAmount1] = useState("0"); @@ -305,84 +305,37 @@ export default function AddLiquidityV3() { refetchInterval: 15000, }); - const sqrtPriceX96Min = useMemo(() => { - const value = - Math.sqrt( - minPrice * 10 ** ((asset1?.decimals ?? 18) - (asset0?.decimals ?? 18)) - ) * - 2 ** 96; - return !isNaN(value) ? BigInt(value) : BigInt(0); - }, [asset0?.decimals, asset1?.decimals, minPrice]); - - const sqrtPriceX96Max = useMemo(() => { - const value = - Math.sqrt( - maxPrice * 10 ** ((asset1?.decimals ?? 18) - (asset0?.decimals ?? 18)) - ) * - 2 ** 96; - return !isNaN(value) ? BigInt(value) : BigInt(0); - }, [asset0?.decimals, asset1?.decimals, maxPrice]); - - const sqrtPriceX96Current = useMemo(() => { - const a0 = parseFloat(amount0); - const a1 = parseFloat(amount1); - if (a0 <= 0) return 0n; - const price = a1 / a0; - const value = - Math.sqrt( - price * 10 ** ((asset1?.decimals ?? 18) - (asset0?.decimals ?? 18)) - ) * - 2 ** 96; - return !isNaN(value) ? BigInt(value) : BigInt(0); - }, [amount0, amount1, asset0?.decimals, asset1?.decimals]); - - // const sqrtPriceX96MinAvg = useMemo( - // () => (sqrtPriceX96Min + sqrtPriceX96Max) / 2n, - // [sqrtPriceX96Min, sqrtPriceX96Max] - // ); - - // const currentPrice = useMemo(() => { - // }, []); + const currentPrice = useMemo(() => { + if (!asset0 || !asset1) return 0; + let sqrtPrice = Number(slot0Data[0]) / Math.pow(2, 96); + sqrtPrice = + Math.pow(sqrtPrice, 2) / Math.pow(10, asset1.decimals - asset0.decimals); + return sqrtPrice; + }, [asset0, asset1, slot0Data]); const tickLower = useMemo(() => { - if (!asset0 || !asset1) return 0; - const sqrtPrice = Number(sqrtPriceX96Min) / 2 ** 96; - const currentTick = slot0Data[1]; - if (currentTick) - return nearestUsableTick(tickSpacing, currentTick - tickSpacing); - if (sqrtPrice > 0) { - return nearestUsableTick( - tickSpacing, - sqrtPriceToTick( - sqrtPrice, - BASIS_POINT, - asset0?.decimals, - asset1?.decimals - ) - ); + let min = parseFloat(minPrice); + if (isNaN(min)) { + const _preset = PRESETS.NARROW; + min = (_preset.min * currentPrice) / 100; + min = currentPrice - min; } - return 0; - }, [asset0, asset1, slot0Data, sqrtPriceX96Min, tickSpacing]); + return min > 0 + ? nearestUsableTick(tickSpacing, priceToTick(min, BASIS_POINT)) + : 0; + }, [currentPrice, minPrice, tickSpacing]); const tickUpper = useMemo(() => { - if (!asset0 || !asset1) return 0; - const sqrtPrice = Number(sqrtPriceX96Max) / 2 ** 96; - const currentTick = slot0Data[1]; - if (currentTick > 0) - return nearestUsableTick(tickSpacing, currentTick + tickSpacing); - if (sqrtPrice > 0) { - return nearestUsableTick( - tickSpacing, - sqrtPriceToTick( - sqrtPrice, - BASIS_POINT, - asset0?.decimals, - asset1?.decimals - ) - ); + let max = parseFloat(maxPrice); + if (isNaN(max)) { + const _preset = PRESETS.NARROW; + max = (_preset.max * currentPrice) / 100; + max = currentPrice + max; } - return 0; - }, [asset0, asset1, slot0Data, sqrtPriceX96Max, tickSpacing]); + return max > 0 + ? nearestUsableTick(tickSpacing, priceToTick(max, BASIS_POINT)) + : max; + }, [currentPrice, maxPrice, tickSpacing]); const amount0Parsed = useMemo(() => { if (isNaN(Number(amount0Bounced)) || !isFinite(Number(amount0Bounced))) @@ -400,6 +353,18 @@ export default function AddLiquidityV3() { return parseUnits(roundedBounced, asset1?.decimals ?? 18); }, [amount1Bounced, asset1]); + const matchesPreset = useCallback( + (pr: keyof typeof PRESETS) => { + const _preset = PRESETS[pr]; + return ( + Number(minPrice) === + currentPrice - (_preset.min * currentPrice) / 100 && + Number(maxPrice) === currentPrice + (_preset.max * currentPrice) / 100 + ); + }, + [currentPrice, maxPrice, minPrice] + ); + // Check allowance const token0AllowanceCheck = useCheckAllowance( asset0?.address, @@ -421,7 +386,7 @@ export default function AddLiquidityV3() { tickSpacing, tickLower, tickUpper, - pairExists ? 0n : sqrtPriceX96Current, + 0n, amount0Parsed, amount1Parsed, positionId || undefined, @@ -441,7 +406,7 @@ export default function AddLiquidityV3() { tickUpper, amountADesired: amount0Parsed, amountBDesired: amount1Parsed, - sqrtPriceX96: pairExists ? 0n : sqrtPriceX96Current, + sqrtPriceX96: 0n, onSuccess: (hash) => { setToast({ actionTitle: "Added Liquidity", @@ -606,9 +571,7 @@ export default function AddLiquidityV3() { }, [balance0, balance1, amount0Parsed, amount1Parsed]); const buttonState = useMemo(() => { - if (!token0AllowanceCheck.isAllowed || !token1AllowanceCheck.isAllowed) - return ButtonState.Approve; - else if ( + if ( approval0Pending || approval1Pending || initLiquidityPending || @@ -617,8 +580,6 @@ export default function AddLiquidityV3() { return ButtonState.Loading; else return ButtonState.Default; }, [ - token0AllowanceCheck.isAllowed, - token1AllowanceCheck.isAllowed, approval0Pending, approval1Pending, initLiquidityPending, @@ -718,28 +679,13 @@ export default function AddLiquidityV3() { const { ratio0, ratio1 } = useMemo(() => { if (!QLSP || !QLSP.Pool_by_pk || !token0 || !token1) return { ratio0: 0, ratio1: 0 }; + if (total === 0) return { ratio0: 0, ratio1: 0 }; return QLSP.Pool_by_pk.token0?.address.toLowerCase() === convertETHToWETHIfApplicable(token0.address, chainId).toLowerCase() ? { ratio0: (reserve0 * 100) / total, ratio1: (reserve1 * 100) / total } : { ratio0: (reserve1 * 100) / total, ratio1: (reserve0 * 100) / total }; }, [QLSP, chainId, reserve0, reserve1, token0, token1, total]); - const multiplierPrice = useMemo(() => { - if (!asset0) return 0; - if (!pairExists || slot0Data[1] === 0) { - return sqrtPriceX96ToPriceReadable( - Number(sqrtPriceX96Current), - asset0.decimals, - asset1?.decimals - ); - } - return sqrtPriceX96ToPriceReadable( - Number(slot0Data[0]), - asset0.decimals, - asset1?.decimals - ); - }, [asset0, asset1?.decimals, pairExists, slot0Data, sqrtPriceX96Current]); - const priceYData = useMemo(() => { if (!QLToken0DailyAnalytics && !QLToken1DailyAnalytics) return []; const p0 = QLToken0DailyAnalytics @@ -764,20 +710,18 @@ export default function AddLiquidityV3() { ).map((val) => ({ priceX: BASIS_POINT ** val, matchesCurrentTick: - multiplierPrice > 0 && - Math.sqrt(multiplierPrice) === Math.sqrt(BASIS_POINT ** val) && - pairExists, + Math.sqrt(currentPrice) === Math.sqrt(BASIS_POINT ** val) && pairExists, })); - if (!result.some((i) => i.matchesCurrentTick) && multiplierPrice > 0) { + if (!result.some((i) => i.matchesCurrentTick) && currentPrice > 0) { result.push({ - priceX: multiplierPrice, + priceX: currentPrice, matchesCurrentTick: true, }); } return result; - }, [multiplierPrice, pairExists, tickSpacing]); + }, [currentPrice, pairExists, tickSpacing]); const mergedDataXY = useMemo(() => { return priceXData.map((p, index) => { @@ -829,11 +773,10 @@ export default function AddLiquidityV3() { // logic to set quote amounts to inputs useEffect(() => { - if (Number.isNaN(multiplierPrice) || !pairExists) return; if (selectedInput === "0") - setAmount1(String(Number(amount0Bounced) * multiplierPrice)); + setAmount1(String(Number(amount0Bounced) * currentPrice)); else if (selectedInput === "1") { - setAmount0(String(Number(amount1Bounced) * (1 / multiplierPrice))); + setAmount0(String(Number(amount1Bounced) * (1 / currentPrice))); } }, [ pairExists, @@ -845,8 +788,8 @@ export default function AddLiquidityV3() { amount1Quote, amount0Quote, amount0Bounced, - multiplierPrice, amount1Bounced, + currentPrice, ]); useEffect(() => { @@ -885,36 +828,12 @@ export default function AddLiquidityV3() { ]); useEffect(() => { - if (slot0Data[1] !== 0) { - setMinPrice(tickToPrice(tickLower, BASIS_POINT)); - setMaxPrice(tickToPrice(tickUpper, BASIS_POINT)); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [slot0Data]); - - useEffect(() => { - if (pairExists) return; - if (sqrtPriceX96Current > 0) { - const sqrtPrice = Number(sqrtPriceX96Current) / 2 ** 96; - const tick = sqrtPriceToTick( - sqrtPrice, - BASIS_POINT, - asset0?.decimals, - asset1?.decimals - ); - const nearestUT = nearestUsableTick(tickSpacing, tick); - const tL = nearestUT - tickSpacing; - const tU = nearestUT + tickSpacing; - setMinPrice(tickToPrice(tL, BASIS_POINT)); - setMaxPrice(tickToPrice(tU, BASIS_POINT)); - } - }, [ - asset0?.decimals, - asset1?.decimals, - pairExists, - sqrtPriceX96Current, - tickSpacing, - ]); + const _preset = PRESETS[preset]; + const minValue = currentPrice - (_preset.min * currentPrice) / 100; + const maxValue = currentPrice + (_preset.max * currentPrice) / 100; + setMinPrice(String(minValue)); + setMaxPrice(String(maxValue)); + }, [currentPrice, preset]); useEffect( () => @@ -970,21 +889,25 @@ export default function AddLiquidityV3() { Current price - {!pairExists || pair === zeroAddress || !QLSP ? ( - - Unavailable - - ) : ( - - {baseToken === 0 - ? `1 ${QLSP.Pool_by_pk?.token0?.symbol} = - ${QLSP.Pool_by_pk?.token1Price} - ${QLSP.Pool_by_pk?.token1?.symbol}` - : `1 ${QLSP.Pool_by_pk?.token1?.symbol} = - ${QLSP.Pool_by_pk?.token0Price} - ${QLSP.Pool_by_pk?.token0?.symbol}`} - - )} + + {baseToken === 0 ? ( + + 1 {asset0?.symbol} ={" "} + {" "} + {asset1?.symbol} + + ) : ( + + 1 {asset1?.symbol} ={" "} + {" "} + {asset0?.symbol} + + )} +
@@ -1035,34 +958,26 @@ export default function AddLiquidityV3() {
@@ -1072,17 +987,41 @@ export default function AddLiquidityV3() {
setMinPrice(String(value))} min={0} max={Number.POSITIVE_INFINITY} label={`Min ${asset1?.symbol} per ${asset0?.symbol}`} + onIncrement={() => { + const _preset = PRESETS[preset]; + const percentage = _preset.min - 0.5; + const value = currentPrice - (percentage * currentPrice) / 100; + setMinPrice(String(value)); + }} + onDecrement={() => { + const _preset = PRESETS[preset]; + const percentage = _preset.min + 0.5; + const value = currentPrice - (percentage * currentPrice) / 100; + setMinPrice(String(value)); + }} /> setMaxPrice(String(value))} min={0} max={Number.POSITIVE_INFINITY} label={`Max ${asset1?.symbol} per ${asset0?.symbol}`} + onIncrement={() => { + const _preset = PRESETS[preset]; + const percentage = _preset.max + 0.5; + const value = currentPrice + (percentage * currentPrice) / 100; + setMaxPrice(String(value)); + }} + onDecrement={() => { + const _preset = PRESETS[preset]; + const percentage = _preset.max - 0.5; + const value = currentPrice + (percentage * currentPrice) / 100; + setMaxPrice(String(value)); + }} />
@@ -1207,10 +1146,10 @@ export default function AddLiquidityV3() { width="100%" height={400} priceData={mergedDataXY} - minPrice={minPrice} - maxPrice={maxPrice} - onMinPriceChange={setMinPrice} - onMaxPriceChange={setMaxPrice} + minPrice={Number(minPrice)} + maxPrice={Number(maxPrice)} + onMinPriceChange={(min) => setMinPrice(String(min))} + onMaxPriceChange={(max) => setMaxPrice(String(max))} xDomain={xDomain} />
@@ -1301,8 +1240,10 @@ export default function AddLiquidityV3() { } interface PriceInputProps { - value: number; - onChange: (value: number) => void; + value: number | string; + onChange: (value: number | string) => void; + onIncrement: () => void; + onDecrement: () => void; label?: string; min: number; max: number; @@ -1316,6 +1257,8 @@ function PriceInput({ min, max, disabled, + onIncrement, + onDecrement, }: PriceInputProps) { return (
@@ -1325,7 +1268,7 @@ function PriceInput({
- {balance} + + + diff --git a/src/data/constants.ts b/src/data/constants.ts index bf293a6b..a8696392 100755 --- a/src/data/constants.ts +++ b/src/data/constants.ts @@ -68,16 +68,35 @@ export const STRINGS = { SWAP_PATH_NOT_FOUND: "No Available Routes", }; +export const PRESETS = { + NARROW: { + min: 4.88, + max: 4.7, + }, + COMMON: { + min: 9.88, + max: 9.85, + }, + WIDE: { + min: 20.07, + max: 20.19, + }, + FULL: { + min: 0, + max: 100, + }, +}; + export const TICK_SPACINGS = { - NARROW: 1, - COMMON: 50, - WIDE: 100, - FULL: 200, + ONE: 1, + FIFTY: 50, + HUNDRED: 100, + TWO_HUNDRED: 200, }; export const TICKS = { - MIN: -1000, - MAX: 1000, + MIN: -16000, + MAX: 16000, }; export const TIME = { WEEK: 604800 };