Skip to content

Commit

Permalink
(remove-liquidity) Add liquidity removal
Browse files Browse the repository at this point in the history
  • Loading branch information
lilchizh committed Nov 7, 2023
1 parent eceec1c commit 02a81f1
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 72 deletions.
21 changes: 16 additions & 5 deletions src/components/common/Table/limitOrderColumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,19 @@ const TokenRates = ({ rates }: { rates: Rates }) => <div className="flex flex-co
<div>{`1 ${rates.sell.token.symbol} = ${rates.sell.rate.toSignificant()} ${rates.buy.token.symbol}`}</div>
</div>

const StatusBar = ({ progress, sellToken, buyToken }: { progress: number, sellToken: Token, buyToken: Token }) => <div className="relative flex h-[25px] bg-card-dark rounded-xl">
<div className="relative flex w-full h-full font-semibold text-sm">
<div className={`flex items-center justify-end pl-1 pr-2 h-full bg-[#143e65] border border-[#36f] duration-300 ${Number(progress) === 100 ? 'rounded-2xl' : 'rounded-l-2xl'}`} style={{ width: `${progress}%` }}>
<CurrencyLogo currency={sellToken} size={22} className="absolute left-1" />
</div>
<div className={`flex items-center pr-1 pl-2 h-full bg-[#351d6b] border border-[#996cff] duration-300 ${Number(progress) === 100 ? 'rounded-2xl' : 'rounded-r-2xl'}`} style={{ width: `${100 - progress}%` }}>
<CurrencyLogo currency={buyToken} size={22} className="absolute right-1" />
</div>
<span className="absolute left-1/2 top-1/2 transform -translate-y-1/2 -translate-x-1/2">{`${Number(progress).toFixed()}%`}</span>
</div>
</div>

const LimitOrderStatus = ({ ticks }: { ticks: Ticks }) => {
const LimitOrderStatus = ({ ticks, amounts }: { ticks: Ticks, amounts: Amounts }) => {

if (ticks.killed) return <div className="flex items-center gap-4 text-left">
<XCircleIcon className="text-red-500" />
Expand All @@ -90,14 +101,14 @@ const LimitOrderStatus = ({ ticks }: { ticks: Ticks }) => {

const progress = (100 * (ticks.tickCurrent - ticks.tickLower) / (ticks.tickUpper - ticks.tickLower))

if (ticks.zeroToOne ? (progress < 0) : (progress > 0) ) return <div className="text-left">0%</div>
if (ticks.zeroToOne ? (progress < 0) : (progress > 0)) return <StatusBar progress={0} sellToken={amounts.sell.token} buyToken={amounts.buy.token} />

if (ticks.zeroToOne ? (progress >= 100) : (progress <= -100) ) return <div className="flex items-center gap-4 text-left">
if (ticks.zeroToOne ? (progress >= 100) : (progress <= -100)) return <div className="flex items-center gap-4 text-left">
<CheckCircle2Icon className={'text-green-500'} />
<span>Completed</span>
</div>

return <div className="text-left">{`${progress.toFixed(1)}%`}</div>
return <StatusBar progress={progress} sellToken={amounts.sell.token} buyToken={amounts.buy.token} />

}

Expand Down Expand Up @@ -155,7 +166,7 @@ export const limitOrderColumns: ColumnDef<LimitOrder>[] = [
{
accessorKey: 'ticks',
header: () => <HeaderItem>Status</HeaderItem>,
cell: ({ getValue }) => <LimitOrderStatus ticks={getValue() as Ticks} />
cell: ({ getValue, row }) => <LimitOrderStatus ticks={getValue() as Ticks} amounts={row.original.amounts} />
},
{
id: 'action',
Expand Down
3 changes: 1 addition & 2 deletions src/components/common/Table/myPositionsColumns.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ColumnDef } from '@tanstack/react-table'
import { HeaderItem } from "./common";
import { formatUSD } from "@/utils/common/formatUSD";
import { formatPercent } from "@/utils/common/formatPercent";

interface MyPosition {
id: number;
Expand Down Expand Up @@ -40,6 +39,6 @@ export const myPositionsColumns: ColumnDef<MyPosition>[] = [
{
accessorKey: 'apr',
header: ({column}) => <HeaderItem sort={() => column.toggleSorting(column.getIsSorted() === "asc")} isAsc={column.getIsSorted() === "asc"}>APR</HeaderItem>,
cell: ({ getValue }) => formatPercent.format((getValue() as number) / 10_000)
cell: ({ getValue }) => `${(getValue() as number)?.toFixed(2)}%`
}
]
18 changes: 18 additions & 0 deletions src/components/modals/RemoveLiquidityModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const RemoveLiquidityModal = ({ positionId }: RemoveLiquidityModalProps) => {

const derivedInfo = useDerivedBurnInfo(position, false);

useEffect(() => {
console.log('derivedInfo 113', derivedInfo)
}, [derivedInfo])

const {
position: positionSDK,
liquidityPercentage,
Expand All @@ -49,6 +53,18 @@ const RemoveLiquidityModal = ({ positionId }: RemoveLiquidityModalProps) => {
)
return { calldata: undefined, value: undefined };

console.log({
tokenId: String(positionId),
liquidityPercentage,
slippageTolerance: new Percent(1, 100),
deadline: Date.now() + txDeadline * 1000,
collectOptions: {
expectedCurrencyOwed0: feeValue0,
expectedCurrencyOwed1: feeValue1,
recipient: account,
}
})

return NonfungiblePositionManager.removeCallParameters(positionSDK, {
tokenId: String(positionId),
liquidityPercentage,
Expand Down Expand Up @@ -146,4 +162,6 @@ const RemoveLiquidityModal = ({ positionId }: RemoveLiquidityModalProps) => {

}

RemoveLiquidityModal.whyDidYouRender = true

export default RemoveLiquidityModal;
2 changes: 1 addition & 1 deletion src/components/position/CollectFees/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const CollectFees = ({ mintInfo, positionFeesUSD, positionId }: CollectFeesProps
const { data: collectData, write: collect } = useContractWrite(collectConfig)

const { isLoading } = useTransitionAwait(collectData?.hash, 'Collect fees')

const collectedFees = positionFeesUSD === '$0' && !zeroRewards ? '< $0.001' : positionFeesUSD

return <div className="flex w-full justify-between bg-card-dark p-4 rounded-xl">
Expand Down
9 changes: 4 additions & 5 deletions src/components/position/PositionCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import PositionNFT from "../PositionNFT";
import { FormattedPosition } from "@/types/formatted-position";
import { formatUSD } from "@/utils/common/formatUSD";
import { Skeleton } from "@/components/ui/skeleton";
import { formatPercent } from "@/utils/common/formatPercent";
import PositionRangeChart from "../PositionRangeChart";
import TokenRatio from "@/components/create-position/TokenRatio";
import { useDerivedMintInfo } from "@/state/mintStore";
import CollectFees from "../CollectFees";
// import RemoveLiquidityModal from "@/components/modals/RemoveLiquidityModal";
import RemoveLiquidityModal from "@/components/modals/RemoveLiquidityModal";

interface PositionCardProps {
selectedPosition: FormattedPosition | undefined
Expand Down Expand Up @@ -41,7 +40,7 @@ const PositionCard = ({ selectedPosition }: PositionCardProps) => {
const [positionLiquidityUSD, positionFeesUSD, positionAPR] = selectedPosition ? [
formatUSD.format(selectedPosition.liquidityUSD),
formatUSD.format(selectedPosition.feesUSD),
formatPercent.format(selectedPosition.apr)
`${selectedPosition.apr.toFixed(2)}%`
] : []

if (!selectedPosition || loading) return
Expand Down Expand Up @@ -76,9 +75,9 @@ const PositionCard = ({ selectedPosition }: PositionCardProps) => {
pool && positionEntity &&
<PositionRangeChart pool={pool} position={positionEntity} />
}
{/* <div className="flex gap-4 w-full whitespace-nowrap">
<div className="flex gap-4 w-full whitespace-nowrap">
<RemoveLiquidityModal positionId={selectedPosition.id}/>
</div> */}
</div>
</div>

}
Expand Down
28 changes: 13 additions & 15 deletions src/components/position/PositionNFT/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import { ALGEBRA_POSITION_MANAGER } from "@/constants/addresses";
import { useAlgebraPositionManagerTokenUri } from "@/generated";
import { ExternalLinkIcon } from "lucide-react";
import { useEffect, useRef } from "react";

interface PositionNFTProps {
Expand All @@ -16,6 +17,8 @@ const PositionNFT = ({ positionId }: PositionNFTProps) => {

const json = uri && JSON.parse(atob(uri.slice('data:application/json;base64,'.length)))

const openSeaLink = `https://testnets.opensea.io/assets/goerli/${ALGEBRA_POSITION_MANAGER}/${positionId}`

useEffect(() => {

if (!imgRef?.current || !json) return
Expand All @@ -24,20 +27,15 @@ const PositionNFT = ({ positionId }: PositionNFTProps) => {

}, [imgRef, json])

return <Dialog>
<DialogTrigger asChild>
<div className="inline-block relative w-[155px] h-[155px] overflow-hidden rounded-full pointer-events-none">
<img ref={imgRef} style={{ transform: 'scale(2)' }} className="mt-4" />
</div>
</DialogTrigger>
<DialogContent className="min-w-[500px] min-h-[250px] rounded-3xl p-0" style={{ borderRadius: '32px' }}>
<div className="relative flex w-full h-full">
<img ref={imgRef} className="rounded-3xl"></img>
<div className="absolute bottom-4 right-4 bg-card-light p-2 rounded-xl">View on OpenSea</div>
</div>
</DialogContent>
</Dialog>

return <div className="inline-block relative w-[160px] h-[160px] overflow-hidden rounded-full">
<img ref={imgRef} style={{ transform: 'scale(2)' }} className="mt-4 absolute" />
<div className="absolute w-full h-full flex items-center justify-center duration-200 bg-black/40 opacity-0 hover:opacity-100">
<a href={openSeaLink} target={'_blank'} rel={'noreferrer noopener'} className="inline-flex gap-2 p-2 hover:bg-gray-600/60 rounded-xl">
<span className="font-semibold">OpenSea</span>
<ExternalLinkIcon />
</a>
</div>
</div>
}

export default PositionNFT;
2 changes: 1 addition & 1 deletion src/graphql/queries/pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const MULTIPLE_POOLS = gql`

export const POOL_FEE_DATA = gql`
query PoolFeeData($poolId: String) {
poolDayDatas(where: { pool: $poolId }, orderBy: date, orderDirection: desc, first: 1) {
poolDayDatas(where: { pool: $poolId }, orderBy: date, orderDirection: desc) {
...PoolDayDataFields
}
}
Expand Down
23 changes: 17 additions & 6 deletions src/hooks/positions/usePositionAPR.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { useAlgebraPoolLiquidity } from "@/generated"
import { usePoolFeeDataQuery, useSinglePoolQuery } from "@/graphql/generated/graphql"
import { useNativePriceQuery, usePoolFeeDataQuery, useSinglePoolQuery } from "@/graphql/generated/graphql"
import { Position } from "@cryptoalgebra/integral-sdk"
import { Address } from "wagmi"

export function usePositionAPR(poolId: Address | undefined, position: Position | undefined) {
export function usePositionAPR(
poolId: Address | undefined,
position: Position | undefined,
positionId?: string | undefined
) {

const { data: liquidity } = useAlgebraPoolLiquidity({
address: poolId
Expand All @@ -21,16 +25,23 @@ export function usePositionAPR(poolId: Address | undefined, position: Position |
}
})

const poolDayFees = poolFeeData && Boolean(poolFeeData.poolDayDatas.length) && poolFeeData.poolDayDatas[0].feesUSD
const { data: bundles } = useNativePriceQuery()

const nativePrice = bundles?.bundles[0] && Number(bundles.bundles[0].maticPriceUSD)

const poolDayFees = poolFeeData && Boolean(poolFeeData.poolDayDatas.length) && poolFeeData.poolDayDatas.reduce((acc, v) => acc + Number(v.feesUSD), 0)

const yearFee = poolDayFees && poolDayFees * 365

const liquidityRelation = position && liquidity && Number(position.liquidity.toString()) / Number(liquidity)
const liquidityRelation = position && liquidity && Number(position.liquidity.toString()) / (Number(liquidity) + (positionId ? 0 : Number(position.liquidity.toString())))

const [amount0, amount1] = position ? [position.amount0.toSignificant(), position.amount1.toSignificant()] : [0, 0]

const tvl = pool && pool.pool && (Number(pool.pool.token0Price) * Number(amount0) + Number(pool.pool.token1Price) * Number(amount1))
const tvl =
pool?.pool && nativePrice &&
(Number(pool.pool.token0.derivedMatic) * nativePrice * Number(amount0) +
Number(pool.pool.token1.derivedMatic) * nativePrice * Number(amount1))

return liquidityRelation && yearFee && tvl && (yearFee * liquidityRelation / tvl)
return liquidityRelation && yearFee && tvl && ((yearFee * liquidityRelation) / tvl) * 100

}
8 changes: 7 additions & 1 deletion src/hooks/positions/usePositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ export function usePositions() {
}

export function usePosition(tokenId: string | number | undefined): { loading: boolean; position: PositionFromTokenId | undefined } {
const { isLoading, positions } = usePositionsFromTokenIds(tokenId ? [tokenId] : undefined)

const tokenIdArr = useMemo(() => {
if (!tokenId) return
return [tokenId]
}, [tokenId])

const { isLoading, positions } = usePositionsFromTokenIds(tokenIdArr)

return useMemo(() => {
return {
Expand Down
3 changes: 1 addition & 2 deletions src/pages/NewPosition/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { Bound, INITIAL_POOL_FEE } from "@cryptoalgebra/integral-sdk"
import { useEffect, useMemo, useState } from "react"
import { useParams } from "react-router-dom"
import { Address } from "wagmi"
import { formatPercent } from "@/utils/common/formatPercent"
import { usePositionAPR } from "@/hooks/positions/usePositionAPR"
import { getPoolAPR } from "@/utils/pool/getPoolAPR"

Expand Down Expand Up @@ -184,7 +183,7 @@ const NewPositionPage = () => {
<div className="flex justify-between bg-card-dark p-2 px-3 rounded-xl">
<div>
<div className="text-xs font-bold">ESTIMATED POSITION APR</div>
<div className="text-lg font-bold text-green-300">{apr ? formatPercent.format(apr) : 0}</div>
<div className="text-lg font-bold text-green-300">{apr ? `${apr.toFixed(2)}%` : 0}</div>
</div>
<div className="text-right">
<div className="text-xs font-bold">POOL APR</div>
Expand Down
14 changes: 9 additions & 5 deletions src/pages/Pool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PoolHeader from "@/components/pool/PoolHeader"
import PositionCard from "@/components/position/PositionCard"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import { usePoolFeeDataQuery, useSinglePoolQuery } from "@/graphql/generated/graphql"
import { useNativePriceQuery, usePoolFeeDataQuery, useSinglePoolQuery } from "@/graphql/generated/graphql"
import { usePool } from "@/hooks/pools/usePool"
import { usePositions } from "@/hooks/positions/usePositions"
import { FormattedPosition } from "@/types/formatted-position"
Expand Down Expand Up @@ -41,6 +41,8 @@ const PoolPage = () => {
}
})

const { data: bundles } = useNativePriceQuery()

const [positionsFees, setPositionsFees] = useState<any>()
const [positionsAPRs, setPositionsAPRs] = useState<any>()

Expand All @@ -51,7 +53,8 @@ const PoolPage = () => {
if (!positions || !poolEntity) return []

return positions.filter(({ pool }) => pool.toLowerCase() === poolId.toLowerCase()).map(position => ({
positionId: position.tokenId, position: new Position({
positionId: position.tokenId,
position: new Position({
pool: poolEntity,
liquidity: position.liquidity.toString(),
tickLower: Number(position.tickLower),
Expand All @@ -75,13 +78,14 @@ const PoolPage = () => {
useEffect(() => {

async function getPositionsAPRs() {
const aprs = await Promise.all(filteredPositions.map(({ position }) => getPositionAPR(poolId, position, poolInfo?.pool, poolFeeData?.poolDayDatas)))
const nativePrice = bundles?.bundles[0].maticPriceUSD
const aprs = await Promise.all(filteredPositions.map(({ position }) => getPositionAPR(poolId, position, poolInfo?.pool, poolFeeData?.poolDayDatas, nativePrice)))
setPositionsAPRs(aprs)
}

if (filteredPositions && poolInfo?.pool && poolFeeData?.poolDayDatas && poolId) getPositionsAPRs()
if (filteredPositions && poolInfo?.pool && poolFeeData?.poolDayDatas && bundles?.bundles && poolId) getPositionsAPRs()

}, [filteredPositions, poolInfo, poolId, poolFeeData])
}, [filteredPositions, poolInfo, poolId, poolFeeData, bundles])

const formatLiquidityUSD = (position: Position) => {
if (!poolInfo?.pool) return 0
Expand Down
Loading

0 comments on commit 02a81f1

Please sign in to comment.