From 200dd20fdd90bbc4c69bd0d26a8baec1d03c9b50 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 8 Nov 2023 17:35:22 +0900 Subject: [PATCH 1/3] feat: Add custom errors --- .../src/common/errors/common/common-error.ts | 4 ++++ packages/web/src/common/errors/pool/index.ts | 1 + .../web/src/common/errors/pool/pool-error.ts | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 packages/web/src/common/errors/pool/index.ts create mode 100644 packages/web/src/common/errors/pool/pool-error.ts diff --git a/packages/web/src/common/errors/common/common-error.ts b/packages/web/src/common/errors/common/common-error.ts index 65916b209..183bf10ec 100644 --- a/packages/web/src/common/errors/common/common-error.ts +++ b/packages/web/src/common/errors/common/common-error.ts @@ -13,6 +13,10 @@ const ERROR_VALUE = { status: 402, type: "Failed to initialize Wallet", }, + FAILED_INITIALIZE_ENVIRONMENT: { + status: 403, + type: "Failed to initialize environment value", + }, }; type ErrorType = keyof typeof ERROR_VALUE; diff --git a/packages/web/src/common/errors/pool/index.ts b/packages/web/src/common/errors/pool/index.ts new file mode 100644 index 000000000..4df3116eb --- /dev/null +++ b/packages/web/src/common/errors/pool/index.ts @@ -0,0 +1 @@ +export * from "./pool-error"; diff --git a/packages/web/src/common/errors/pool/pool-error.ts b/packages/web/src/common/errors/pool/pool-error.ts new file mode 100644 index 000000000..c62fd19d2 --- /dev/null +++ b/packages/web/src/common/errors/pool/pool-error.ts @@ -0,0 +1,17 @@ +import { BaseError } from "@common/errors"; + +const ERROR_VALUE = { + NOT_FOUND_POOL: { + status: 404, + type: "Not found pool", + }, +}; + +type ErrorType = keyof typeof ERROR_VALUE; + +export class PoolError extends BaseError { + constructor(errorType: ErrorType) { + super(ERROR_VALUE[errorType]); + Object.setPrototypeOf(this, PoolError.prototype); + } +} From ed6c1c555dbffa8faebeb28fc1a73ab6ad3a79b2 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 8 Nov 2023 17:36:25 +0900 Subject: [PATCH 2/3] feat: Add an RPC call function --- .../models/pool/mapper/pool-info-mapper.ts | 33 ++++++++++++ .../web/src/models/pool/pool-info-model.ts | 49 ++++++++++++++++++ .../GnoswapServiceProvider.tsx | 4 +- .../repositories/pool/pool-repository-impl.ts | 50 +++++++++++++++---- .../repositories/pool/pool-repository-mock.ts | 8 ++- .../src/repositories/pool/pool-repository.ts | 3 ++ .../pool/response/pool-info-response.ts | 35 +++++++++++++ 7 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 packages/web/src/models/pool/mapper/pool-info-mapper.ts create mode 100644 packages/web/src/models/pool/pool-info-model.ts create mode 100644 packages/web/src/repositories/pool/response/pool-info-response.ts diff --git a/packages/web/src/models/pool/mapper/pool-info-mapper.ts b/packages/web/src/models/pool/mapper/pool-info-mapper.ts new file mode 100644 index 000000000..04887bd7d --- /dev/null +++ b/packages/web/src/models/pool/mapper/pool-info-mapper.ts @@ -0,0 +1,33 @@ +import { PoolInfoResponse } from "@repositories/pool/response/pool-info-response"; +import { rawByX96 } from "@utils/swap-utils"; +import { PoolInfoModel } from "../pool-info-model"; + +export class PoolInfoMapper { + public static fromResponse(response: PoolInfoResponse | null): PoolInfoModel { + if (!response) { + throw new Error("mapper error"); + } + const sqrtPriceX96 = response.response.data.sqrt_price_x96; + const price = rawByX96(sqrtPriceX96); + + return { + poolPath: response.response.data.pool_path, + tokenABalance: response.response.data.token0_balance, + tokenBBalance: response.response.data.token1_balance, + tickSpacing: response.response.data.tick_spacing, + maxLiquidityPerTick: response.response.data.max_liquidity_per_tick, + price, + sqrtPriceX96, + tick: response.response.data.tick, + feeProtocol: response.response.data.fee_protocol, + feeGrowthGlobal0X128: response.response.data.fee_growth_global0_x128, + feeGrowthGlobal1X128: response.response.data.fee_growth_global1_x128, + tokenAProtocolFee: response.response.data.token0_protocol_fee, + tokenBProtocolFee: response.response.data.token1_protocol_fee, + liquidity: response.response.data.liquidity, + ticks: response.response.data.ticks, + tickBitmaps: response.response.data.tick_bitmaps, + positions: [] as any, + }; + } +} diff --git a/packages/web/src/models/pool/pool-info-model.ts b/packages/web/src/models/pool/pool-info-model.ts new file mode 100644 index 000000000..fbf5214c4 --- /dev/null +++ b/packages/web/src/models/pool/pool-info-model.ts @@ -0,0 +1,49 @@ +export interface PoolInfoModel { + poolPath: string; + + tokenABalance: number; + + tokenBBalance: number; + + tickSpacing: number; + + maxLiquidityPerTick: number; + + price: number; + + sqrtPriceX96: number; + + tick: number; + + feeProtocol: number; + + feeGrowthGlobal0X128: number; + + feeGrowthGlobal1X128: number; + + tokenAProtocolFee: number; + + tokenBProtocolFee: number; + + liquidity: number; + + ticks: number[]; + + tickBitmaps: number[]; + + positions: [ + { + owner: string; + + tickLower: number; + + tickUpper: number; + + liquidity: number; + + tokenAOwed: number; + + tokenBOwed: number; + }, + ]; +} diff --git a/packages/web/src/providers/gnoswap-service-provider/GnoswapServiceProvider.tsx b/packages/web/src/providers/gnoswap-service-provider/GnoswapServiceProvider.tsx index bfff38f5d..720c938db 100644 --- a/packages/web/src/providers/gnoswap-service-provider/GnoswapServiceProvider.tsx +++ b/packages/web/src/providers/gnoswap-service-provider/GnoswapServiceProvider.tsx @@ -69,8 +69,8 @@ const GnoswapServiceProvider: React.FC = ({ }, []); const poolRepository = useMemo(() => { - return new PoolRepositoryImpl(networkClient, walletClient); - }, [networkClient, walletClient]); + return new PoolRepositoryImpl(networkClient, rpcProvider, walletClient); + }, [networkClient, rpcProvider, walletClient]); const stakingRepository = useMemo(() => { return new StakingRepositoryMock(); diff --git a/packages/web/src/repositories/pool/pool-repository-impl.ts b/packages/web/src/repositories/pool/pool-repository-impl.ts index 402623317..c4f91c08d 100644 --- a/packages/web/src/repositories/pool/pool-repository-impl.ts +++ b/packages/web/src/repositories/pool/pool-repository-impl.ts @@ -1,6 +1,5 @@ import { NetworkClient } from "@common/clients/network-client"; import { - MOCK_BINS, PoolDetailResponse, PoolListResponse, PoolRepository, @@ -14,6 +13,15 @@ import { } from "@constants/option.constant"; import { SendTransactionSuccessResponse } from "@common/clients/wallet-client/protocols"; import { CommonError } from "@common/errors"; +import { GnoProvider } from "@gnolang/gno-js-client"; +import { + evaluateExpressionToObject, + makeABCIParams, +} from "@utils/rpc-utils"; +import { PoolInfoResponse } from "./response/pool-info-response"; +import { PoolInfoModel } from "@models/pool/pool-info-model"; +import { PoolInfoMapper } from "@models/pool/mapper/pool-info-mapper"; +import { PoolError } from "@common/errors/pool"; const POOL_PATH = process.env.NEXT_PUBLIC_PACKAGE_POOL_PATH || ""; const POSITION_PATH = process.env.NEXT_PUBLIC_PACKAGE_POSITION_PATH || ""; @@ -21,10 +29,16 @@ const POOL_ADDRESS = process.env.NEXT_PUBLIC_PACKAGE_POOL_ADDRESS || ""; export class PoolRepositoryImpl implements PoolRepository { private networkClient: NetworkClient; + private rpcProvider: GnoProvider | null; private walletClient: WalletClient | null; - constructor(networkClient: NetworkClient, walletClient: WalletClient | null) { + constructor( + networkClient: NetworkClient, + rpcProvider: GnoProvider | null, + walletClient: WalletClient | null, + ) { this.networkClient = networkClient; + this.rpcProvider = rpcProvider; this.walletClient = walletClient; } @@ -32,16 +46,8 @@ export class PoolRepositoryImpl implements PoolRepository { const response = await this.networkClient.get({ url: "/pools", }); - // TODO: This will change after the API is finished. - const pools = response.data.pools.map(pool => ({ - ...pool, - currentTick: MOCK_BINS[3].currentTick, - topBin: MOCK_BINS[3], - bins: MOCK_BINS, - })); return { ...response.data, - pools, }; }; @@ -54,6 +60,30 @@ export class PoolRepositoryImpl implements PoolRepository { return response.data; }; + getPoolInfoByPoolPath = async ( + poolPath: string, + ): Promise => { + const poolPackagePath = process.env.NEXT_PUBLIC_PACKAGE_POOL_PATH; + if (!poolPackagePath || !this.rpcProvider) { + throw new CommonError("FAILED_INITIALIZE_ENVIRONMENT"); + } + const param = makeABCIParams("ApiGetPool", [poolPath]); + + const response = await this.rpcProvider.evaluateExpression( + poolPackagePath, + param, + ).then(evaluateExpressionToObject) + .then(PoolInfoMapper.fromResponse) + .catch(e => { + console.error(e); + return null; + }); + if (response === null) { + throw new PoolError("NOT_FOUND_POOL"); + } + return response; + }; + createPool = async (request: CreatePoolRequest): Promise => { if (this.walletClient === null) { throw new CommonError("FAILED_INITIALIZE_WALLET"); diff --git a/packages/web/src/repositories/pool/pool-repository-mock.ts b/packages/web/src/repositories/pool/pool-repository-mock.ts index 0a2e04c08..70b6909c5 100644 --- a/packages/web/src/repositories/pool/pool-repository-mock.ts +++ b/packages/web/src/repositories/pool/pool-repository-mock.ts @@ -2,14 +2,18 @@ import { PoolDetailResponse, PoolListResponse, PoolRepository } from "."; import PoolListData from "./mock/pools.json"; import PoolDetailData from "./mock/pool-detail.json"; - -export const MOCK_BINS = PoolListData.pools[0].bins; +import { PoolInfoModel } from "@models/pool/pool-info-model"; +import { PoolError } from "@common/errors/pool"; export class PoolRepositoryMock implements PoolRepository { getPools = async (): Promise => { return PoolListData as PoolListResponse; }; + getPoolInfoByPoolPath = async (): Promise => { + throw new PoolError("NOT_FOUND_POOL"); + }; + getPoolDetailByPoolId = async (): Promise => { return PoolDetailData as PoolDetailResponse; }; diff --git a/packages/web/src/repositories/pool/pool-repository.ts b/packages/web/src/repositories/pool/pool-repository.ts index 9eec8b297..6b65da72b 100644 --- a/packages/web/src/repositories/pool/pool-repository.ts +++ b/packages/web/src/repositories/pool/pool-repository.ts @@ -1,9 +1,12 @@ +import { PoolInfoModel } from "@models/pool/pool-info-model"; import { CreatePoolRequest } from "./request/create-pool-request"; import { PoolDetailResponse, PoolListResponse } from "./response"; export interface PoolRepository { getPools: () => Promise; + getPoolInfoByPoolPath: (poolPath: string) => Promise; + getPoolDetailByPoolId: (poolId: string) => Promise; createPool: (request: CreatePoolRequest) => Promise; diff --git a/packages/web/src/repositories/pool/response/pool-info-response.ts b/packages/web/src/repositories/pool/response/pool-info-response.ts new file mode 100644 index 000000000..9553a86b7 --- /dev/null +++ b/packages/web/src/repositories/pool/response/pool-info-response.ts @@ -0,0 +1,35 @@ +export interface PoolInfoResponse { + stat: { + height: number; + timestamp: number; + }; + response: { + data: { + pool_path: string; + token0_balance: number; + token1_balance: number; + tick_spacing: number; + max_liquidity_per_tick: number; + sqrt_price_x96: number; + tick: number; + fee_protocol: number; + fee_growth_global0_x128: number; + fee_growth_global1_x128: number; + token0_protocol_fee: number; + token1_protocol_fee: number; + liquidity: number; + ticks: number[]; + tick_bitmaps: number[]; + positions: [ + { + owner: string; + tick_lower: number; + tick_upper: number; + liquidity: number; + token0_owed: number; + token1_owed: number; + }, + ]; + }; + }; +} From a43e68ec492b54286cab2d131f584472f3d7faa5 Mon Sep 17 00:00:00 2001 From: jinoosss Date: Wed, 8 Nov 2023 17:36:45 +0900 Subject: [PATCH 3/3] [GSW-551] feat: Integrate pool data with RPC --- .../common/select-fee-tier/SelectFeeTier.tsx | 20 +++--- .../earn-add-liquidity/EarnAddLiquidity.tsx | 3 + packages/web/src/constants/swap.constant.ts | 2 + .../EarnAddLiquidityContainer.tsx | 11 +++- packages/web/src/hooks/pool/use-pool.tsx | 61 ++++++++++++++++--- .../use-earn-add-liquidity-confirm-modal.tsx | 15 +++-- packages/web/src/utils/swap-utils.ts | 11 +++- 7 files changed, 96 insertions(+), 27 deletions(-) diff --git a/packages/web/src/components/common/select-fee-tier/SelectFeeTier.tsx b/packages/web/src/components/common/select-fee-tier/SelectFeeTier.tsx index 3fc24dcf7..5b581e97d 100644 --- a/packages/web/src/components/common/select-fee-tier/SelectFeeTier.tsx +++ b/packages/web/src/components/common/select-fee-tier/SelectFeeTier.tsx @@ -2,9 +2,9 @@ import React, { useCallback, useMemo } from "react"; import { SelectFeeTierItemWrapper, SelectFeeTierWrapper } from "./SelectFeeTier.styles"; import { SwapFeeTierInfoMap, SwapFeeTierType } from "@constants/option.constant"; import { PoolModel } from "@models/pool/pool-model"; -import BigNumber from "bignumber.js"; interface SelectFeeTierProps { + feetierOfLiquidityMap: { [key in string]: number }; feeTiers: SwapFeeTierType[]; feeTier: SwapFeeTierType | null; pools: PoolModel[], @@ -12,6 +12,7 @@ interface SelectFeeTierProps { } const SelectFeeTier: React.FC = ({ + feetierOfLiquidityMap, feeTiers, feeTier, pools, @@ -30,6 +31,7 @@ const SelectFeeTier: React.FC = ({ selected={feeTier === item} feeTier={item} pools={pools} + liquidityRange={feetierOfLiquidityMap[SwapFeeTierInfoMap[item].fee] || null} onClick={() => onClickFeeTierItem(item)} /> ))} @@ -41,13 +43,14 @@ interface SelectFeeTierItemProps { selected: boolean; feeTier: SwapFeeTierType; pools: PoolModel[]; + liquidityRange: number | null; onClick: () => void; } const SelectFeeTierItem: React.FC = ({ selected, feeTier, - pools, + liquidityRange, onClick, }) => { const feeRateStr = useMemo(() => { @@ -55,18 +58,11 @@ const SelectFeeTierItem: React.FC = ({ }, [feeTier]); const rangeStr = useMemo(() => { - const pool = pools.find(pool => pool.fee === feeTier); - if (!pool || pool.bins.length < 2) { + if (liquidityRange === null) { return "Not created"; } - const sortedBins = pool.bins.sort((p1, p2) => p1.currentTick - p2.currentTick); - const fullTickRange = 1774545; - const currentTickGap = sortedBins[0].currentTick - sortedBins[sortedBins.length - 1].currentTick; - return BigNumber(currentTickGap) - .dividedBy(fullTickRange) - .multipliedBy(100) - .toFixed(); - }, [feeTier, pools]); + return `${liquidityRange}% Select`; + }, [liquidityRange]); const description = useMemo(() => { return SwapFeeTierInfoMap[feeTier].description; diff --git a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx index 9d6c9dd72..e8d30bdc8 100644 --- a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx +++ b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx @@ -23,6 +23,7 @@ interface EarnAddLiquidityProps { changeTokenB: (token: TokenModel) => void; tokenAInput: TokenAmountInputModel; tokenBInput: TokenAmountInputModel; + feetierOfLiquidityMap: { [key in string]: number }; feeTiers: SwapFeeTierType[]; feeTier: SwapFeeTierType | null; pools: PoolModel[]; @@ -44,6 +45,7 @@ const EarnAddLiquidity: React.FC = ({ changeTokenB, tokenAInput, tokenBInput, + feetierOfLiquidityMap, feeTiers, feeTier, pools, @@ -169,6 +171,7 @@ const EarnAddLiquidity: React.FC = ({ {openedFeeTier && ( { const [priceRange, setPriceRange] = useState( null ); - const [pools] = useState([]); + const { connected: connectedWallet, account, @@ -83,6 +83,7 @@ const EarnAddLiquidityContainer: React.FC = () => { } = useWallet(); const { slippage } = useSlippage(); const { updateTokenPrices } = useTokenData(); + const { pools, feetierOfLiquidityMap, createPool } = usePool({ tokenA, tokenB }); const { openModal: openConfirmModal } = useEarnAddLiquidityConfirmModal({ tokenA, tokenB, @@ -92,8 +93,13 @@ const EarnAddLiquidityContainer: React.FC = () => { priceRange, slippage, swapFeeTier, + createPool }); + useEffect(() => { + console.log(feetierOfLiquidityMap); + }, [feetierOfLiquidityMap]); + useEffect(() => { if (query?.feeTier) { setSwapFeeTier(query?.feeTier as SwapFeeTierType); @@ -209,6 +215,7 @@ const EarnAddLiquidityContainer: React.FC = () => { changeTokenA={changeTokenA} changeTokenB={changeTokenB} feeTiers={SWAP_FEE_TIERS} + feetierOfLiquidityMap={feetierOfLiquidityMap} feeTier={swapFeeTier} selectFeeTier={selectSwapFeeTier} priceRanges={priceRanges} diff --git a/packages/web/src/hooks/pool/use-pool.tsx b/packages/web/src/hooks/pool/use-pool.tsx index 671e8e52e..299d3b912 100644 --- a/packages/web/src/hooks/pool/use-pool.tsx +++ b/packages/web/src/hooks/pool/use-pool.tsx @@ -2,16 +2,40 @@ import { SwapFeeTierType } from "@constants/option.constant"; import { AddLiquidityPriceRage } from "@containers/earn-add-liquidity-container/EarnAddLiquidityContainer"; import { useGnoswapContext } from "@hooks/common/use-gnoswap-context"; import { useWallet } from "@hooks/wallet/use-wallet"; +import { PoolModel } from "@models/pool/pool-model"; import { TokenModel } from "@models/token/token-model"; -import { useCallback } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { usePoolData } from "./use-pool-data"; -export const usePool = () => { +interface Props { + tokenA: TokenModel | null; + tokenB: TokenModel | null; +} + +export const usePool = ({ + tokenA, + tokenB +}: Props) => { const { account } = useWallet(); const { poolRepository } = useGnoswapContext(); + const { pools, updatePools } = usePoolData(); + const [feetierOfLiquidityMap, setFeetierOfLiquidityMap] = useState<{ [key in string]: number }>({}); + + const currentPools: PoolModel[] = useMemo(() => { + if (!tokenA || !tokenB) { + return []; + } + + const tokenPairOfPaths = [tokenA.path, tokenB.path]; // [tokenA.path, tokenB.path]; + return pools.filter(pool => tokenPairOfPaths.includes(pool.tokenA.path) && tokenPairOfPaths.includes(pool.tokenB.path)); + }, [pools, tokenA, tokenB]); + + async function fetchPoolInfos(pools: PoolModel[]) { + const poolInfos = await (await Promise.all(pools.map(pool => poolRepository.getPoolInfoByPoolPath(pool.id).catch(null)))).filter(info => info !== null); + return poolInfos; + } const createPool = useCallback(async ({ - tokenA, - tokenB, tokenAAmount, tokenBAmount, swapFeeTier, @@ -19,8 +43,6 @@ export const usePool = () => { priceRange, slippage, }: { - tokenA: TokenModel; - tokenB: TokenModel; tokenAAmount: string; tokenBAmount: string; swapFeeTier: SwapFeeTierType; @@ -28,7 +50,7 @@ export const usePool = () => { priceRange: AddLiquidityPriceRage; slippage: number; }) => { - if (!account) { + if (!tokenA || !tokenB || !account) { return null; } const hash = await poolRepository.createPool({ @@ -47,9 +69,32 @@ export const usePool = () => { return null; }); return hash; - }, [account, poolRepository]); + }, [account, poolRepository, tokenA, tokenB]); + + useEffect(() => { + updatePools(); + }, []); + + useEffect(() => { + fetchPoolInfos(currentPools) + .then(infos => { + const feetierOfLiquidityMap: { [key in string]: number } = {}; + const totalLiquidities = infos.map(info => info.liquidity).reduce((total, cur) => total + cur, 0); + for (const info of infos) { + const liquidityRate = Math.round((info.liquidity / totalLiquidities) * 100); + const feeTier = currentPools.find(pool => pool.id === info.poolPath)?.fee; + if (feeTier) { + feetierOfLiquidityMap[`${feeTier}`] = liquidityRate; + } + } + return feetierOfLiquidityMap; + }) + .then(setFeetierOfLiquidityMap); + }, [currentPools]); return { + pools: currentPools, + feetierOfLiquidityMap, createPool }; }; \ No newline at end of file diff --git a/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx b/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx index fab6ba33b..1dc24bc8e 100644 --- a/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx +++ b/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx @@ -2,7 +2,6 @@ import EarnAddConfirm from "@components/earn-add/earn-add-confirm/EarnAddConfirm import { SwapFeeTierInfoMap, SwapFeeTierType } from "@constants/option.constant"; import { AddLiquidityPriceRage } from "@containers/earn-add-liquidity-container/EarnAddLiquidityContainer"; import useNavigate from "@hooks/common/use-navigate"; -import { usePool } from "@hooks/pool/use-pool"; import { TokenModel } from "@models/token/token-model"; import { CommonState } from "@states/index"; import { useAtom } from "jotai"; @@ -19,6 +18,16 @@ export interface EarnAddLiquidityConfirmModalProps { priceRange: AddLiquidityPriceRage | null; slippage: number; swapFeeTier: SwapFeeTierType | null; + createPool: ( + params: { + tokenAAmount: string; + tokenBAmount: string; + swapFeeTier: SwapFeeTierType; + startPrice: string; + priceRange: AddLiquidityPriceRage; + slippage: number; + } + ) => Promise; } export interface SelectTokenModalModel { openModal: () => void; @@ -33,10 +42,10 @@ export const useEarnAddLiquidityConfirmModal = ({ currentPrice, slippage, swapFeeTier, + createPool, }: EarnAddLiquidityConfirmModalProps): SelectTokenModalModel => { const [, setOpenedModal] = useAtom(CommonState.openedModal); const [, setModalContent] = useAtom(CommonState.modalContent); - const { createPool } = usePool(); const navigator = useNavigate(); const amountInfo = useMemo(() => { @@ -106,8 +115,6 @@ export const useEarnAddLiquidityConfirmModal = ({ return; } createPool({ - tokenA, - tokenB, tokenAAmount: tokenAAmountInput.amount, tokenBAmount: tokenBAmountInput.amount, priceRange, diff --git a/packages/web/src/utils/swap-utils.ts b/packages/web/src/utils/swap-utils.ts index 59be164fc..fa4334d4b 100644 --- a/packages/web/src/utils/swap-utils.ts +++ b/packages/web/src/utils/swap-utils.ts @@ -2,7 +2,7 @@ import { SwapFeeTierInfoMap, SwapFeeTierType, } from "@constants/option.constant"; -import { MAX_TICK, MIN_TICK } from "@constants/swap.constant"; +import { MAX_TICK, MIN_TICK, X96 } from "@constants/swap.constant"; import BigNumber from "bignumber.js"; export function getCurrentPriceByRaw(raw: string) { @@ -40,6 +40,15 @@ export function priceToNearTick(price: number, tickSpacing: number) { return tickRaw - mod; } +export function rawByX96(value: number) { + return BigNumber(value).dividedBy(X96).toNumber(); +} + +export function priceX96ToNearTick(priceX96: number, tickSpacing: number) { + const price = rawByX96(priceX96); + return priceToNearTick(price, tickSpacing); +} + export function tickToPriceStr(tick: number, decimals?: number) { if (tick === MIN_TICK + 1) { return "0.00";