Skip to content

Commit

Permalink
Merge pull request #231 from gnoswap-labs/GSW-551-integrate-pool-data…
Browse files Browse the repository at this point in the history
…-with-rpc

[GSW-551] feat: Integrate pool data with RPC
  • Loading branch information
jinoosss authored Nov 8, 2023
2 parents 31ccfcf + a43e68e commit ac52797
Show file tree
Hide file tree
Showing 17 changed files with 286 additions and 41 deletions.
4 changes: 4 additions & 0 deletions packages/web/src/common/errors/common/common-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions packages/web/src/common/errors/pool/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./pool-error";
17 changes: 17 additions & 0 deletions packages/web/src/common/errors/pool/pool-error.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ 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[],
selectFeeTier: (feeTier: SwapFeeTierType) => void;
}

const SelectFeeTier: React.FC<SelectFeeTierProps> = ({
feetierOfLiquidityMap,
feeTiers,
feeTier,
pools,
Expand All @@ -30,6 +31,7 @@ const SelectFeeTier: React.FC<SelectFeeTierProps> = ({
selected={feeTier === item}
feeTier={item}
pools={pools}
liquidityRange={feetierOfLiquidityMap[SwapFeeTierInfoMap[item].fee] || null}
onClick={() => onClickFeeTierItem(item)}
/>
))}
Expand All @@ -41,32 +43,26 @@ interface SelectFeeTierItemProps {
selected: boolean;
feeTier: SwapFeeTierType;
pools: PoolModel[];
liquidityRange: number | null;
onClick: () => void;
}

const SelectFeeTierItem: React.FC<SelectFeeTierItemProps> = ({
selected,
feeTier,
pools,
liquidityRange,
onClick,
}) => {
const feeRateStr = useMemo(() => {
return SwapFeeTierInfoMap[feeTier].rateStr;
}, [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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand All @@ -44,6 +45,7 @@ const EarnAddLiquidity: React.FC<EarnAddLiquidityProps> = ({
changeTokenB,
tokenAInput,
tokenBInput,
feetierOfLiquidityMap,
feeTiers,
feeTier,
pools,
Expand Down Expand Up @@ -169,6 +171,7 @@ const EarnAddLiquidity: React.FC<EarnAddLiquidityProps> = ({

{openedFeeTier && (
<SelectFeeTier
feetierOfLiquidityMap={feetierOfLiquidityMap}
feeTiers={feeTiers}
feeTier={feeTier}
pools={pools}
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/constants/swap.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const MAX_PRICE_X96 =

export const MIN_TICK = -887272 as const;
export const MAX_TICK = 887272 as const;

export const X96 = 7.9228163e28 as const;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "@constants/option.constant";
import { useTokenAmountInput } from "@hooks/token/use-token-amount-input";
import { TokenModel } from "@models/token/token-model";
import { PoolModel } from "@models/pool/pool-model";
import { useWallet } from "@hooks/wallet/use-wallet";
import BigNumber from "bignumber.js";
import { useSlippage } from "@hooks/common/use-slippage";
Expand All @@ -16,6 +15,7 @@ import { useEarnAddLiquidityConfirmModal } from "@hooks/token/use-earn-add-liqui
import { useAtom } from "jotai";
import { SwapState } from "@states/index";
import { useRouter } from "next/router";
import { usePool } from "@hooks/pool/use-pool";

export interface AddLiquidityPriceRage {
type: PriceRangeType;
Expand Down Expand Up @@ -73,7 +73,7 @@ const EarnAddLiquidityContainer: React.FC = () => {
const [priceRange, setPriceRange] = useState<AddLiquidityPriceRage | null>(
null
);
const [pools] = useState<PoolModel[]>([]);

const {
connected: connectedWallet,
account,
Expand All @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -209,6 +215,7 @@ const EarnAddLiquidityContainer: React.FC = () => {
changeTokenA={changeTokenA}
changeTokenB={changeTokenB}
feeTiers={SWAP_FEE_TIERS}
feetierOfLiquidityMap={feetierOfLiquidityMap}
feeTier={swapFeeTier}
selectFeeTier={selectSwapFeeTier}
priceRanges={priceRanges}
Expand Down
61 changes: 53 additions & 8 deletions packages/web/src/hooks/pool/use-pool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,55 @@ 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,
startPrice,
priceRange,
slippage,
}: {
tokenA: TokenModel;
tokenB: TokenModel;
tokenAAmount: string;
tokenBAmount: string;
swapFeeTier: SwapFeeTierType;
startPrice: string;
priceRange: AddLiquidityPriceRage;
slippage: number;
}) => {
if (!account) {
if (!tokenA || !tokenB || !account) {
return null;
}
const hash = await poolRepository.createPool({
Expand All @@ -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
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<string | null>;
}
export interface SelectTokenModalModel {
openModal: () => void;
Expand All @@ -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(() => {
Expand Down Expand Up @@ -106,8 +115,6 @@ export const useEarnAddLiquidityConfirmModal = ({
return;
}
createPool({
tokenA,
tokenB,
tokenAAmount: tokenAAmountInput.amount,
tokenBAmount: tokenBAmountInput.amount,
priceRange,
Expand Down
33 changes: 33 additions & 0 deletions packages/web/src/models/pool/mapper/pool-info-mapper.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
}
Loading

0 comments on commit ac52797

Please sign in to comment.