Skip to content

Commit

Permalink
feat: [GSW-463] Calculate the Price Range of Liquidity
Browse files Browse the repository at this point in the history
  • Loading branch information
jinoosss committed Dec 4, 2023
1 parent 6d14e8d commit 1286179
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 99 deletions.
27 changes: 7 additions & 20 deletions packages/web/src/components/common/pool-graph/PoolGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const PoolGraph: React.FC<PoolGraphProps> = ({
/** Zoom */
const zoom: d3.ZoomBehavior<any, unknown> = d3
.zoom()
.scaleExtent([1, tickFullRange / 2])
.scaleExtent([0, tickFullRange])
.translateExtent([
[0, 0],
[boundsWidth, boundsHeight]
Expand All @@ -115,10 +115,11 @@ const PoolGraph: React.FC<PoolGraphProps> = ({
const svgElement = d3.select(svgRef.current);
const minXTick = minX || 0;
const maxXTick = maxX || 0;
const distance = Math.abs(centerX - minXTick) > Math.abs(centerX - maxXTick)
? Math.abs(minXTick - centerX)
: Math.abs(maxXTick - centerX);
const scaleRate = (tickFullRange / (distance) / 2);
const tick = currentTick || 0;
const distance = Math.abs(tick - minXTick) > Math.abs(tick - maxXTick)
? Math.abs(tick - minXTick)
: Math.abs(tick - maxXTick);
const scaleRate = (MAX_TICK / distance);
zoom.scaleTo(svgElement, scaleRate, [scaleX(centerX), 0]);
}

Expand All @@ -130,28 +131,14 @@ const PoolGraph: React.FC<PoolGraphProps> = ({
updateChart();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function zoomIn() {
d3.select(svgRef.current)
.transition()
.call(zoom.scaleBy, 4);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function zoomOut() {
d3.select(svgRef.current)
.transition()
.call(zoom.scaleBy, 0.25);
}

function getTickSpacing() {
if (bins.length < 1) {
return 0;
}
if (bins.length === 2) {
return 20;
}
const spacing = scaleX(bins[1].minTick) - scaleX(bins[0].minTick);
const spacing = scaleX(bins[0].maxTick) - scaleX(bins[0].minTick);
if (spacing < 2) {
return spacing;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ const EarnAddLiquidityContainer: React.FC = () => {
currentTick={null}
submitType={submitType}
submit={submit}
isEarnAdd={true}
isEarnAdd={false}
connected={connectedWallet}
slippage={slippage}
changeSlippage={handleChangeSlippage}
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/hooks/pool/use-pool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const usePool = ({
}, [compareToken, tokenA, tokenB]);

async function fetchPoolInfos(pools: PoolModel[]) {
const poolInfos = await (await Promise.all(pools.map(pool => poolRepository.getPoolInfoByPoolPath(pool.path).catch(null)))).filter(info => info !== null);
const poolInfos = await (await Promise.all(pools.map(pool => poolRepository.getPoolDetailRPCByPoolPath(pool.path).catch(null)))).filter(info => info !== null);
return poolInfos;
}

Expand Down
31 changes: 16 additions & 15 deletions packages/web/src/hooks/pool/use-select-pool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { SwapFeeTierInfoMap, SwapFeeTierType } from "@constants/option.constant"
import { TokenModel } from "@models/token/token-model";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useGnoswapContext } from "@hooks/common/use-gnoswap-context";
import { PoolInfoModel } from "@models/pool/pool-info-model";
import { feeBoostRateByPrices, priceToNearTick, tickToPrice } from "@utils/swap-utils";
import { MAX_TICK, MIN_PRICE, MIN_TICK } from "@constants/swap.constant";
import { PoolDetailRPCModel } from "@models/pool/pool-detail-rpc-model";
import { MAX_TICK, MIN_PRICE_X96, MIN_TICK } from "@constants/swap.constant";

type RenderState = "NONE" | "CREATE" | "LOADING" | "DONE";

Expand Down Expand Up @@ -66,7 +66,7 @@ export const useSelectPool = ({
const [minPosition, setMinPosition] = useState<number | null>(null);
const [maxPosition, setMaxPosition] = useState<number | null>(null);
const [compareToken, setCompareToken] = useState<TokenModel | null>(tokenA);
const [poolInfo, setPoolInfo] = useState<PoolInfoModel | null>(null);
const [poolInfo, setPoolInfo] = useState<PoolDetailRPCModel | null>(null);
const [interactionType, setInteractionType] = useState<"NONE" | "INTERACTION" | "TICK_UPDATE" | "FINISH">("NONE");

const renderState: RenderState = useMemo(() => {
Expand Down Expand Up @@ -136,7 +136,7 @@ export const useSelectPool = ({
}

const logMin = minPrice <= 0 ?
Math.log(currentPrice / MIN_PRICE) :
Math.log(currentPrice / Number(MIN_PRICE_X96)) :
Math.log(currentPrice / minPrice);
const logMax = Math.log(maxPrice / currentPrice);
return logMin * 100 / (logMin + logMax);
Expand All @@ -147,7 +147,7 @@ export const useSelectPool = ({
return null;
}
if (minPrice <= 0) {
return feeBoostRateByPrices(MIN_PRICE, maxPrice);
return feeBoostRateByPrices(Number(MIN_PRICE_X96), maxPrice);
}
return feeBoostRateByPrices(minPrice, maxPrice);
}, [maxPrice, minPrice]);
Expand Down Expand Up @@ -273,32 +273,33 @@ export const useSelectPool = ({
setPoolInfo(null);
return;
}
const poolInfo: PoolInfoModel = {
const poolInfo: PoolDetailRPCModel = {
poolPath: "",
tokenABalance: 0,
tokenBBalance: 0,
tokenAPath: "",
tokenBPath: "",
fee: 0,
tokenABalance: 0n,
tokenBBalance: 0n,
tickSpacing: SwapFeeTierInfoMap[feeTier].tickSpacing,
maxLiquidityPerTick: 0,
price: startPrice,
sqrtPriceX96: 0,
sqrtPriceX96: 0n,
tick: 0,
feeProtocol: 0,
feeGrowthGlobal0X128: 0,
feeGrowthGlobal1X128: 0,
tokenAProtocolFee: 0,
tokenBProtocolFee: 0,
liquidity: 0,
liquidity: 0n,
ticks: [],
tickBitmaps: [],
positions: []
};
setPoolInfo(poolInfo);
} else {
const tokenPair = [tokenA.symbol.toLowerCase(), tokenB.symbol.toLowerCase()].sort();
const poolPath = `${tokenPair.join("_")}_${SwapFeeTierInfoMap[feeTier].fee}`;
const tokenPair = [tokenA.path, tokenB.path].sort();
const poolPath = `${tokenPair.join(":")}:${SwapFeeTierInfoMap[feeTier].fee}`;
const reverse = [tokenA?.path, tokenB?.path].sort().findIndex(path => path === compareToken?.path) === 1;

poolRepository.getPoolInfoByPoolPath(poolPath).then(poolInfo => {
poolRepository.getPoolDetailRPCByPoolPath(poolPath).then(poolInfo => {
const changedPoolInfo = reverse === false ? poolInfo : {
...poolInfo,
price: 1 / poolInfo.price,
Expand Down
51 changes: 50 additions & 1 deletion packages/web/src/models/pool/mapper/pool-rpc-mapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PoolRPCResponse } from "@repositories/pool/response/pool-rpc-response";
import { rawBySqrtX96 } from "@utils/swap-utils";
import { PoolDetailRPCModel } from "../pool-detail-rpc-model";
import { PoolRPCModel } from "../pool-rpc-model";

export class PoolRPCMapper {
Expand Down Expand Up @@ -32,16 +33,64 @@ export class PoolRPCMapper {
return acc;
}, {}),
positions: responseData.positions.map(position => ({
liquidity: BigInt(position.liquidity),
owner: position.owner,
tickLower: position.tick_lower,
tickUpper: position.tick_upper,
liquidity: BigInt(position.liquidity),
tokenAOwed: BigInt(position.token0_owed),
tokenBOwed: BigInt(position.token1_owed),
})),
};
}

public static detailFrom(data: PoolRPCResponse): PoolDetailRPCModel {
const responseData = data;
const sqrtPriceX96 = BigInt(responseData.sqrt_price_x96);
const price = rawBySqrtX96(sqrtPriceX96);
const tickSpacing = responseData.tick_spacing;
console.log(data);

return {
poolPath: responseData.pool_path,
tokenAPath: responseData.token0_path,
tokenBPath: responseData.token1_path,
fee: responseData.fee,
tokenABalance: BigInt(responseData.token0_balance),
tokenBBalance: BigInt(responseData.token1_balance),
tickSpacing,
maxLiquidityPerTick: responseData.max_liquidity_per_tick,
price,
sqrtPriceX96,
tick: responseData.tick,
feeProtocol: responseData.fee_protocol,
tokenAProtocolFee: responseData.token0_protocol_fee,
tokenBProtocolFee: responseData.token1_protocol_fee,
liquidity: BigInt(responseData.liquidity),
ticks: responseData.ticks,
tickBitmaps: Object.entries(responseData.tick_bitmaps).reduce<
{ [key in string]: string }
>((acc, [key, value]) => {
acc[key] = value.toString();
return acc;
}, {}),
positions: data.positions.map(position => {
const tickLower = position.tick_lower;
const tickUpper = position.tick_upper;
const tickCount = 1 + (tickUpper - tickLower) / tickSpacing;
const liquidityOfTick = Number(position.liquidity) / tickCount;
return {
owner: position.owner,
tickLower,
tickUpper,
liquidityOfTick,
liquidity: BigInt(position.liquidity),
tokenAOwed: BigInt(position.token0_owed),
tokenBOwed: BigInt(position.token1_owed),
};
}),
};
}

public static fromList(response: PoolRPCResponse[] | null): PoolRPCModel[] {
if (!response) {
throw new Error("mapper error");
Expand Down
6 changes: 6 additions & 0 deletions packages/web/src/models/pool/pool-detail-rpc-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PoolRPCModel } from "./pool-rpc-model";
import { PositionDetailRPCModel } from "./position-detail-rpc-model";

export interface PoolDetailRPCModel extends PoolRPCModel {
positions: PositionDetailRPCModel[];
}
5 changes: 5 additions & 0 deletions packages/web/src/models/pool/position-detail-rpc-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PositionRPCModel } from "./position-rpc-model";

export interface PositionDetailRPCModel extends PositionRPCModel {
liquidityOfTick: number;
}
7 changes: 4 additions & 3 deletions packages/web/src/repositories/pool/pool-repository-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { AddLiquidityRequest } from "./request/add-liquidity-request";
import BigNumber from "bignumber.js";
import { priceToNearTick, tickToPrice } from "@utils/swap-utils";
import { X96 } from "@constants/swap.constant";
import { PoolDetailRPCModel } from "@models/pool/pool-detail-rpc-model";

const POOL_PATH = process.env.NEXT_PUBLIC_PACKAGE_POOL_PATH || "";
const POSITION_PATH = process.env.NEXT_PUBLIC_PACKAGE_POSITION_PATH || "";
Expand Down Expand Up @@ -84,9 +85,9 @@ export class PoolRepositoryImpl implements PoolRepository {
return response.data;
};

getPoolInfoByPoolPath = async (
getPoolDetailRPCByPoolPath = async (
poolPath: string,
): Promise<PoolRPCModel> => {
): Promise<PoolDetailRPCModel> => {
const poolPackagePath = process.env.NEXT_PUBLIC_PACKAGE_POOL_PATH;
if (!poolPackagePath || !this.rpcProvider) {
throw new CommonError("FAILED_INITIALIZE_ENVIRONMENT");
Expand All @@ -105,7 +106,7 @@ export class PoolRepositoryImpl implements PoolRepository {
if (!response?.response?.data) {
return null;
}
return PoolRPCMapper.from(response?.response?.data);
return PoolRPCMapper.detailFrom(response?.response?.data);
})
.catch(e => {
console.error(e);
Expand Down
3 changes: 2 additions & 1 deletion packages/web/src/repositories/pool/pool-repository-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PoolError } from "@common/errors/pool";
import rpcPools from "./mock/rpc-pools.json";
import { PoolRPCMapper } from "@models/pool/mapper/pool-rpc-mapper";
import { PoolModel } from "@models/pool/pool-model";
import { PoolDetailRPCModel } from "@models/pool/pool-detail-rpc-model";
export class PoolRepositoryMock implements PoolRepository {
getPools = async (): Promise<PoolModel[]> => {
return [];
Expand All @@ -15,7 +16,7 @@ export class PoolRepositoryMock implements PoolRepository {
return rpcPools.map(pool => PoolRPCMapper.from(pool as any));
};

getPoolInfoByPoolPath = async (): Promise<PoolRPCModel> => {
getPoolDetailRPCByPoolPath = async (): Promise<PoolDetailRPCModel> => {
throw new PoolError("NOT_FOUND_POOL");
};

Expand Down
3 changes: 2 additions & 1 deletion packages/web/src/repositories/pool/pool-repository.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PoolDetailRPCModel } from "@models/pool/pool-detail-rpc-model";
import { PoolModel } from "@models/pool/pool-model";
import { PoolRPCModel } from "@models/pool/pool-rpc-model";
import { AddLiquidityRequest } from "./request/add-liquidity-request";
Expand All @@ -9,7 +10,7 @@ export interface PoolRepository {

getRPCPools: () => Promise<PoolRPCModel[]>;

getPoolInfoByPoolPath: (poolPath: string) => Promise<PoolRPCModel>;
getPoolDetailRPCByPoolPath: (poolPath: string) => Promise<PoolDetailRPCModel>;

getPoolDetailByPoolId: (poolId: string) => Promise<PoolDetailResponse>;

Expand Down
Loading

0 comments on commit 1286179

Please sign in to comment.