From 0701f4feb73dedb10581a8919be0ba285a159aa7 Mon Sep 17 00:00:00 2001 From: eli-d <64763513+eli-d@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:48:39 +0930 Subject: [PATCH] use liquidity math to adjust display of second token based on range --- web/src/components/StakeForm.tsx | 49 +++++++++++++++++++------------- web/src/lib/amounts.ts | 12 -------- web/src/lib/math.ts | 20 +++++++++++++ 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/web/src/components/StakeForm.tsx b/web/src/components/StakeForm.tsx index 8dd5723b..d35ba843 100644 --- a/web/src/components/StakeForm.tsx +++ b/web/src/components/StakeForm.tsx @@ -15,7 +15,7 @@ import CurrentPrice from "@/assets/icons/legend/current-price.svg"; import LiquidityDistribution from "@/assets/icons/legend/liquidity-distribution.svg"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import { MAX_TICK, getSqrtRatioAtTick, sqrtPriceX96ToPrice } from "@/lib/math"; +import { MAX_TICK, calculateX, calculateY, getLiquidityForAmount0, getLiquidityForAmount1, getSqrtRatioAtTick, sqrtPriceX96ToPrice } from "@/lib/math"; import { ammAddress } from "@/lib/addresses"; import { createChartData } from "@/lib/chartData"; import { output as seawaterContract } from "@/lib/abi/ISeawaterAMM"; @@ -44,7 +44,7 @@ import { graphql, useFragment } from "@/gql"; import { useGraphqlGlobal, useGraphqlUser } from "@/hooks/useGraphql"; import { usdFormat } from "@/lib/usdFormat"; import { Token as TokenType, fUSDC, getTokenFromAddress } from "@/config/tokens"; -import { getFormattedPriceFromAmount, getTokenAmountFromRawAmountAndPrice } from "@/lib/amounts"; +import { getFormattedPriceFromAmount } from "@/lib/amounts"; const colorGradient = new echarts.graphic.LinearGradient( 0, @@ -111,6 +111,8 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { priceUpper, setPriceLower, setPriceUpper, + tickLower, + tickUpper, setTickLower, setTickUpper, } = useStakeStore(); @@ -148,11 +150,16 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { const positionData_ = useFragment(PositionsFragment, userData?.getWallet) const positionData = positionData_?.positions.positions.find(p => p.positionId === positionId) - const { upper: upperTick, lower: lowerTick } = positionData || { + const { upper: upperTickPosition, lower: lowerTickPosition } = positionData || { upper: 0, lower: 0 }; + useEffect(() => { + setTickLower(lowerTickPosition) + setTickUpper(upperTickPosition) + }, [positionData]) + const showMockData = useFeatureFlag("ui show demo data"); const router = useRouter(); @@ -212,26 +219,26 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { } useEffect(() => { + if (!token0AmountRaw || !curTick || tickLower === undefined || tickUpper === undefined) + return + const lower = BigInt(tickLower) + const upper = BigInt(tickUpper) + + const cur = curTick.result + const sqp = getSqrtRatioAtTick(cur) + const sqa = getSqrtRatioAtTick(lower) + const sqb = getSqrtRatioAtTick(upper) + if (quotedToken === 'token0') { - const newToken1Amount = getTokenAmountFromRawAmountAndPrice( - BigInt(token0AmountRaw), - tokenPrice, - BigInt(token0.decimals), - BigInt(token1.decimals), - 'mul' - ) + const liq = getLiquidityForAmount0(cur, upper, BigInt(token0AmountRaw)) + const newToken1Amount = calculateY(liq, sqa, sqp) if (token1Balance?.value && newToken1Amount > token1Balance.value) return setToken1AmountRaw(newToken1Amount.toString()) } else { - const newToken0Amount = getTokenAmountFromRawAmountAndPrice( - BigInt(token1AmountRaw), - tokenPrice, - BigInt(token0.decimals), - BigInt(token1.decimals), - 'div' - ); + const liq = getLiquidityForAmount1(cur, lower, BigInt(token1AmountRaw)) + const newToken0Amount = calculateX(liq, sqb, sqp) if (token0Balance?.value && newToken0Amount > token0Balance.value) return setToken0AmountRaw(newToken0Amount.toString()) @@ -244,7 +251,9 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { token0AmountRaw, token1AmountRaw, tokenPrice, - quotedToken + quotedToken, + tickUpper, + tickLower, ]); const setMaxBalance = (token: TokenType) => { @@ -271,8 +280,8 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { // set the ticks to the existing ticks of the pool if (mode === "existing") { const scale = token0.decimals - fUSDC.decimals - const priceLower = (1.0001 ** lowerTick * 10 ** scale).toFixed(fUSDC.decimals) - const priceHigher = (1.0001 ** upperTick * 10 ** scale).toFixed(fUSDC.decimals) + const priceLower = (1.0001 ** (tickLower ?? 0) * 10 ** scale).toFixed(fUSDC.decimals) + const priceHigher = (1.0001 ** (tickUpper ?? 0) * 10 ** scale).toFixed(fUSDC.decimals) setPriceLower(priceLower, token0.decimals) setPriceUpper(priceHigher, token0.decimals) return diff --git a/web/src/lib/amounts.ts b/web/src/lib/amounts.ts index 85421ff0..985f08f0 100644 --- a/web/src/lib/amounts.ts +++ b/web/src/lib/amounts.ts @@ -100,23 +100,11 @@ const getFormattedPriceFromTick = (tick: number, decimals0: number, decimals1: n return formattedPrice.length > 20 ? '∞ ' : formattedPrice } -// get the amount of token1Unscaled, given the price and amount of token0Unscaled. -// mul sets the operation to scale up token0Unscaled by tokenPrice18 (assumes token0Unscaled is the base token) -// div sets the operation to divide token0Unscaled by tokenPrice18 (assumes token0Unscaled is the other token) -const getTokenAmountFromRawAmountAndPrice = (token0Unscaled: bigint, tokenPrice18: bigint, dec0: bigint, dec1: bigint, op: 'mul' | 'div'): bigint => { - const num = token0Unscaled * 10n ** dec0; - const dec = dec1 <= dec0 ? (dec0 - dec1) + dec0 : dec0; - return op === 'mul' ? - num * tokenPrice18 / 10n ** (dec + dec1) : - num / tokenPrice18 -} - export { getFormattedStringFromTokenAmount, snapAmountToDecimals, getTokenAmountFromFormattedString, getFormattedPriceFromAmount, getFormattedPriceFromTick, - getTokenAmountFromRawAmountAndPrice, } diff --git a/web/src/lib/math.ts b/web/src/lib/math.ts index bbc35605..028fe8c3 100644 --- a/web/src/lib/math.ts +++ b/web/src/lib/math.ts @@ -328,6 +328,26 @@ export const getAmountsForLiquidity = ( } }; +export const calculateY = (liq: bigint, sqrtPriceAX96: bigint, sqrtPriceBX96: bigint) => { + let sqrtPrice0X96 = sqrtPriceAX96; + let sqrtPrice1X96 = sqrtPriceBX96; + if (sqrtPriceAX96 > sqrtPriceBX96) { + sqrtPrice0X96 = sqrtPriceBX96 + sqrtPrice1X96 = sqrtPriceAX96 + } + return liq * (sqrtPrice1X96 - sqrtPrice0X96) / Q96 +} +export const calculateX = (liq: bigint, sqrtPriceAX96: bigint, sqrtPriceBX96: bigint) => { + let sqrtPrice0X96 = sqrtPriceAX96; + let sqrtPrice1X96 = sqrtPriceBX96; + if (sqrtPriceAX96 > sqrtPriceBX96) { + sqrtPrice0X96 = sqrtPriceBX96 + sqrtPrice1X96 = sqrtPriceAX96 + } + return liq * Q96 * (sqrtPrice1X96 - sqrtPrice0X96) / sqrtPrice0X96 / sqrtPrice1X96 +} + + const getTickAtSqrtPrice = (sqrtPriceX96: number) => Math.floor(Math.log((sqrtPriceX96 / Number(Q96)) ** 2) / Math.log(1.0001));