diff --git a/web/src/app/stake/MyPositions.tsx b/web/src/app/stake/MyPositions.tsx index 9e7aa1ba..60005d80 100644 --- a/web/src/app/stake/MyPositions.tsx +++ b/web/src/app/stake/MyPositions.tsx @@ -69,14 +69,14 @@ export const MyPositions = () => { const showDemoData = useFeatureFlag("ui show demo data"); const showClaimAllYield = useFeatureFlag("ui show claim all yield"); - const { positions: walletData } = usePositions() + const { positions: walletData } = usePositions(); // this is every position, with their respective pools const pools = useMemo((): Pool[] | undefined => { if (showDemoData && address) return mockMyPositions; - return walletData. - map((position) => ({ + return walletData + .map((position) => ({ positionId: position.positionId, id: position.pool.token.address, duration: 0, diff --git a/web/src/app/stake/pool/confirm-withdraw/page.tsx b/web/src/app/stake/pool/confirm-withdraw/page.tsx index 7bc68f78..18537389 100644 --- a/web/src/app/stake/pool/confirm-withdraw/page.tsx +++ b/web/src/app/stake/pool/confirm-withdraw/page.tsx @@ -15,7 +15,10 @@ import { } from "wagmi"; import { fUSDC } from "@/config/tokens"; import { sqrtPriceX96ToPrice } from "@/lib/math"; -import { getFormattedPriceFromAmount, getUsdTokenAmountsForPosition } from "@/lib/amounts"; +import { + getFormattedPriceFromAmount, + getUsdTokenAmountsForPosition, +} from "@/lib/amounts"; import Confirm from "@/components/sequence/Confirm"; import { Success } from "@/components/sequence/Success"; import { Fail } from "@/components/sequence/Fail"; @@ -38,7 +41,7 @@ export default function ConfirmWithdrawLiquidity() { const { token0, token0Amount, token0AmountRaw, token1, token1Amount, delta } = useStakeStore(); - const { positions, updatePositionLocal } = usePositions() + const { positions, updatePositionLocal } = usePositions(); // Current liquidity of the position const { data: positionLiquidity } = useSimulateContract({ @@ -136,13 +139,19 @@ export default function ConfirmWithdrawLiquidity() { useEffect(() => { if (updatePositionResult.isSuccess) { - const position = positions.find(p => p.positionId === Number(positionId)) + const position = positions.find( + (p) => p.positionId === Number(positionId), + ); if (position) { - getUsdTokenAmountsForPosition(position, token0, Number(tokenPrice)).then(([amount0, amount1]) => + getUsdTokenAmountsForPosition( + position, + token0, + Number(tokenPrice), + ).then(([amount0, amount1]) => updatePositionLocal({ ...position, served: { - timestamp: Math.round(new Date().getTime() / 1000) + timestamp: Math.round(new Date().getTime() / 1000), }, liquidity: { fusdc: { @@ -150,14 +159,13 @@ export default function ConfirmWithdrawLiquidity() { }, token1: { valueUsd: String(amount0), - } - } - }) - ) + }, + }, + }), + ); } } - }, [updatePositionResult.isSuccess]) - + }, [updatePositionResult.isSuccess]); // step 1 - collect yield from position if emptying entire balance if ( @@ -239,10 +247,10 @@ export default function ConfirmWithdrawLiquidity() { {token0.address === fUSDC.address ? token0Amount : getFormattedPriceFromAmount( - token0Amount, - tokenPrice, - fUSDC.decimals, - )} + token0Amount, + tokenPrice, + fUSDC.decimals, + )} @@ -257,10 +265,10 @@ export default function ConfirmWithdrawLiquidity() { {token1.address === fUSDC.address ? token1Amount : getFormattedPriceFromAmount( - token1Amount, - tokenPrice, - fUSDC.decimals, - )} + token1Amount, + tokenPrice, + fUSDC.decimals, + )} diff --git a/web/src/app/stake/pool/page.tsx b/web/src/app/stake/pool/page.tsx index 9646c0c6..30a24cc3 100644 --- a/web/src/app/stake/pool/page.tsx +++ b/web/src/app/stake/pool/page.tsx @@ -25,9 +25,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { - getFormattedPriceFromTick, -} from "@/lib/amounts"; +import { getFormattedPriceFromTick } from "@/lib/amounts"; import { useStakeStore } from "@/stores/useStakeStore"; import { useSwapStore } from "@/stores/useSwapStore"; import { ammAddress } from "@/lib/addresses"; @@ -68,15 +66,15 @@ export default function PoolPage() { const { data: globalData } = useGraphqlGlobal(); const allPoolsData = useFragment(ManagePoolFragment, globalData?.pools); - const { positions: positionsData_, updatePositionLocal } = usePositions() + const { positions: positionsData_, updatePositionLocal } = usePositions(); const positionsData = useMemo( () => positionsData_.filter( (p) => p.pool.token.address === id && parseFloat(p.liquidity.fusdc.valueUsd) + - parseFloat(p.liquidity.token1.valueUsd) > - 0, + parseFloat(p.liquidity.token1.valueUsd) > + 0, ), [id, positionsData_], ); @@ -119,12 +117,12 @@ export default function PoolPage() { usdFormat( positionsData ? positionsData.reduce( - (total, { liquidity: { fusdc, token1 } }) => - total + - parseFloat(fusdc.valueUsd) + - parseFloat(token1.valueUsd), - 0, - ) + (total, { liquidity: { fusdc, token1 } }) => + total + + parseFloat(fusdc.valueUsd) + + parseFloat(token1.valueUsd), + 0, + ) : 0, ), [poolData], @@ -207,8 +205,8 @@ export default function PoolPage() { ); return usdFormat( (amount0 * Number(tokenPrice)) / - 10 ** (token0.decimals + fUSDC.decimals) + - amount1 / 10 ** token1.decimals, + 10 ** (token0.decimals + fUSDC.decimals) + + amount1 / 10 ** token1.decimals, ); }, [position, positionLiquidity, tokenPrice, token0, token1, curTick]); @@ -370,18 +368,18 @@ export default function PoolPage() {
{lowerTick ? getFormattedPriceFromTick( - lowerTick, - token0.decimals, - token1.decimals, - ) + lowerTick, + token0.decimals, + token1.decimals, + ) : usdFormat(0)} - {upperTick ? getFormattedPriceFromTick( - upperTick, - token0.decimals, - token1.decimals, - ) + upperTick, + token0.decimals, + token1.decimals, + ) : usdFormat(0)}
diff --git a/web/src/components/ConfirmStake.tsx b/web/src/components/ConfirmStake.tsx index 1b8802a0..7fb168fa 100644 --- a/web/src/components/ConfirmStake.tsx +++ b/web/src/components/ConfirmStake.tsx @@ -27,7 +27,10 @@ import Confirm from "@/components/sequence/Confirm"; import { EnableSpending } from "@/components/sequence/EnableSpending"; import { Fail } from "@/components/sequence/Fail"; import { Success } from "@/components/sequence/Success"; -import { getFormattedPriceFromAmount, getUsdTokenAmountsForPosition } from "@/lib/amounts"; +import { + getFormattedPriceFromAmount, + getUsdTokenAmountsForPosition, +} from "@/lib/amounts"; import { fUSDC } from "@/config/tokens"; import { TokenIcon } from "./TokenIcon"; import { useFeatureFlag } from "@/hooks/useFeatureFlag"; @@ -35,13 +38,13 @@ import { usePositions } from "@/hooks/usePostions"; type ConfirmStakeProps = | { - mode: "new"; - positionId?: never; - } + mode: "new"; + positionId?: never; + } | { - mode: "existing"; - positionId: number; - }; + mode: "existing"; + positionId: number; + }; export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { const router = useRouter(); @@ -67,7 +70,7 @@ export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { multiSingleToken, } = useStakeStore(); - const { positions, updatePositionLocal } = usePositions() + const { positions, updatePositionLocal } = usePositions(); // Price of the current pool const { data: poolSqrtPriceX96 } = useSimulateContract({ @@ -169,12 +172,12 @@ export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { !curTick || tickLower === undefined || tickUpper === undefined ? 0n : getLiquidityForAmounts( - curTick.result, - BigInt(tickLower), - BigInt(tickUpper), - BigInt(token0AmountRaw), - BigInt(token1AmountRaw), - ), + curTick.result, + BigInt(tickLower), + BigInt(tickUpper), + BigInt(token0AmountRaw), + BigInt(token1AmountRaw), + ), [curTick, tickLower, tickUpper, token0AmountRaw, token1AmountRaw], ); @@ -312,21 +315,25 @@ export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { }); useEffect(() => { if (updatePositionResult.isSuccess) { - const id = positionId ?? Number(mintPositionId) + const id = positionId ?? Number(mintPositionId); if (id && tickLower && tickUpper) { - const position = positions.find(p => p.positionId === id) ?? { + const position = positions.find((p) => p.positionId === id) ?? { positionId: id, pool: { token: token0, }, lower: tickLower, upper: tickUpper, - } - getUsdTokenAmountsForPosition(position, token0, Number(tokenPrice)).then(([amount0, amount1]) => + }; + getUsdTokenAmountsForPosition( + position, + token0, + Number(tokenPrice), + ).then(([amount0, amount1]) => updatePositionLocal({ ...position, served: { - timestamp: Math.round(new Date().getTime() / 1000) + timestamp: Math.round(new Date().getTime() / 1000), }, liquidity: { fusdc: { @@ -334,14 +341,13 @@ export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { }, token1: { valueUsd: String(amount0), - } - } - }) - ) + }, + }, + }), + ); } - } - }, [updatePositionResult.isSuccess]) + }, [updatePositionResult.isSuccess]); // step 1 pending if (isMintPending || (mintData && result?.isPending)) { @@ -386,27 +392,30 @@ export const ConfirmStake = ({ mode, positionId }: ConfirmStakeProps) => { isUpdatePositionPending || (updatePositionData && updatePositionResult?.isPending) ) { - return ; + return ( + + ); } - // success if (updatePositionResult.data) { - return { - resetUpdatePosition() - resetApproveToken0() - resetApproveToken1() - updatePositionResult.refetch() - router.push("/stake") - }} - />; + return ( + { + resetUpdatePosition(); + resetApproveToken0(); + resetApproveToken1(); + updatePositionResult.refetch(); + router.push("/stake"); + }} + /> + ); } // error diff --git a/web/src/components/StakeForm.tsx b/web/src/components/StakeForm.tsx index 8af64a58..808e4619 100644 --- a/web/src/components/StakeForm.tsx +++ b/web/src/components/StakeForm.tsx @@ -426,7 +426,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { selected={multiSingleToken === "multi"} onClick={() => setMultiSingleToken("multi")} > -
+
Multi-Token
@@ -436,7 +436,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { onClick={() => setMultiSingleToken("single")} variant={"iridescent"} > -
+
Single-Token
@@ -499,7 +499,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { )}
-
+
{token0Balance && ( <>
Balance: {token0Balance.formatted}
@@ -563,7 +563,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { fUSDC.decimals, )}
-
+
{token1Balance && ( <>
Balance: {token1Balance.formatted}
@@ -641,13 +641,13 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => {
Fee Percentage
-
+
The protocol automatically adjust your fees in order to maximise rewards and reduce impermanent loss
@@ -666,52 +666,52 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { >
0.01%
-
+
Best for Very
Stable Pairs
-
+
(0% popularity)
0.05%
-
+
Best for
Stable Pairs
-
+
(99% popularity)
0.10%
-
+
Best for
Stable Pairs
-
+
(0% popularity)
0.15%
-
+
Best for
Stable Pairs
-
+
(0% popularity)
@@ -771,7 +771,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { value={liquidityRangeType === "full-range" ? "-∞" : priceLower} onChange={(e) => setPriceLower(e.target.value, token0.decimals)} /> -
+
fUSDC per{" "} {token0.name}
@@ -795,7 +795,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { onChange={(e) => setPriceUpper(e.target.value, token0.decimals)} /> -
+
fUSDC per{" "} {token0.name}
@@ -819,12 +819,12 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { {breakdownHidden ? ( <>
Show Breakdown
-
{"<-"}
+
{"<-"}
) : ( <>
Hide breakdown
-
{"->"}
+
{"->"}
)}
@@ -893,7 +893,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { - + @@ -963,7 +963,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { >
Yield Breakdown
-
+
Pool Fees
@@ -1004,7 +1004,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { @@ -1015,7 +1015,7 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => { {showBoostIncentives && ( <> -
+
3%
diff --git a/web/src/hooks/usePostions.ts b/web/src/hooks/usePostions.ts index 86a859ec..7940c182 100644 --- a/web/src/hooks/usePostions.ts +++ b/web/src/hooks/usePostions.ts @@ -9,36 +9,36 @@ import { persist } from "zustand/middleware"; export type Position = { // the age at which this was created, not received served: { - timestamp: number - } - positionId: number + timestamp: number; + }; + positionId: number; pool: { - token: Token - } - lower: number - upper: number + token: Token; + }; + lower: number; + upper: number; liquidity: { fusdc: { - valueUsd: string - } + valueUsd: string; + }; token1: { - valueUsd: string - } - } -} + valueUsd: string; + }; + }; +}; interface PositionStore { // positionsLocal is a list of positions modified by local actions - positionsLocal: { [positionId: number]: Position } + positionsLocal: { [positionId: number]: Position }; // positions is a key value store of the most up to date positions // from the remote server and local - positions: { [positionId: number]: Position } + positions: { [positionId: number]: Position }; // receive new positions, preferring the newest version of each position - updatePositionsFromGraph: (newPositions: Array) => void + updatePositionsFromGraph: (newPositions: Array) => void; // store a local position update after depositing or withdrawing a stake // it is assumed this is always newer/more accurate than the remote data // the first time it is stored - updatePositionLocal: (newPosition: Position) => void + updatePositionLocal: (newPosition: Position) => void; } const usePositionStore = create()( @@ -47,33 +47,36 @@ const usePositionStore = create()( return { positions: [], positionsLocal: {}, - updatePositionsFromGraph: (newPositions) => set(({ positionsLocal }) => { - const positionsUpdated = newPositions.map(newPosition => { - const local = positionsLocal[newPosition.positionId] - if (local?.served.timestamp < newPosition.served.timestamp) - return local - return newPosition - }) - return { - positionsLocal, - positions: positionsUpdated - } - }), - updatePositionLocal: (newPosition) => set(({ positionsLocal, positions }) => ({ - positionsLocal: { - ...positionsLocal, - [newPosition.positionId]: newPosition - }, - positions: { - ...positions, - [newPosition.positionId]: newPosition - } - })) - } - }, { - name: 'position-store', - } - ) + updatePositionsFromGraph: (newPositions) => + set(({ positionsLocal }) => { + const positionsUpdated = newPositions.map((newPosition) => { + const local = positionsLocal[newPosition.positionId]; + if (local?.served.timestamp < newPosition.served.timestamp) + return local; + return newPosition; + }); + return { + positionsLocal, + positions: positionsUpdated, + }; + }), + updatePositionLocal: (newPosition) => + set(({ positionsLocal, positions }) => ({ + positionsLocal: { + ...positionsLocal, + [newPosition.positionId]: newPosition, + }, + positions: { + ...positions, + [newPosition.positionId]: newPosition, + }, + })), + }; + }, + { + name: "position-store", + }, + ), ); const PositionsFragment = graphql(` @@ -106,33 +109,35 @@ const PositionsFragment = graphql(` } } } -`) +`); export const usePositions = () => { const { data: userData } = useGraphqlUser(); const positionsData = useFragment(PositionsFragment, userData?.getWallet); - const { - positions, - updatePositionLocal, - updatePositionsFromGraph - } = usePositionStore(); + const { positions, updatePositionLocal, updatePositionsFromGraph } = + usePositionStore(); - useMemo(() => positionsData && updatePositionsFromGraph( - // postprocess this to assert that the nested address type is correct - positionsData.positions.positions.map(p => ({ - ...p, - pool: { - ...p.pool, - token: { - ...p.pool.token, - address: p.pool.token.address as `0x${string}`, - } - } - })) - ), [positionsData]); + useMemo( + () => + positionsData && + updatePositionsFromGraph( + // postprocess this to assert that the nested address type is correct + positionsData.positions.positions.map((p) => ({ + ...p, + pool: { + ...p.pool, + token: { + ...p.pool.token, + address: p.pool.token.address as `0x${string}`, + }, + }, + })), + ), + [positionsData], + ); return { positions: Object.values(positions), updatePositionLocal, }; -} +}; diff --git a/web/src/lib/amounts.ts b/web/src/lib/amounts.ts index 96b4308f..2703b686 100644 --- a/web/src/lib/amounts.ts +++ b/web/src/lib/amounts.ts @@ -3,7 +3,11 @@ import { Position } from "@/hooks/usePostions"; import { output as seawaterContract } from "@/lib/abi/ISeawaterAMM"; -import { getSqrtRatioAtTick, getTokenAmountsNumeric, sqrtPriceX96ToPrice } from "./math"; +import { + getSqrtRatioAtTick, + getTokenAmountsNumeric, + sqrtPriceX96ToPrice, +} from "./math"; import { usdFormat } from "./usdFormat"; import { simulateContract } from "wagmi/actions"; import { config } from "@/config"; @@ -119,13 +123,17 @@ const getFormattedPriceFromTick = ( return formattedPrice.length > 20 ? "∞ " : formattedPrice; }; -const getUsdTokenAmountsForPosition = async (position: Pick, token0: Token, tokenPrice: number): Promise<[number, number]> => { +const getUsdTokenAmountsForPosition = async ( + position: Pick, + token0: Token, + tokenPrice: number, +): Promise<[number, number]> => { const positionLiquidity = await simulateContract(config, { address: ammAddress, abi: seawaterContract.abi, functionName: "positionLiquidity8D11C045", args: [token0.address, BigInt(position.positionId)], - }) + }); const curTick = await simulateContract(config, { address: ammAddress, abi: seawaterContract.abi, @@ -139,11 +147,12 @@ const getUsdTokenAmountsForPosition = async (position: Pick((set) => ({ // update raw amount if it doesn't exceed balance if (!balanceRaw || amountRaw <= BigInt(balanceRaw)) setToken0AmountRaw(amountRaw.toString()); - } catch { } + } catch {} return { token0Amount: amount }; }); }, @@ -120,7 +120,7 @@ export const useStakeStore = create((set) => ({ // update raw amount if it doesn't exceed balance if (!balanceRaw || amountRaw <= BigInt(balanceRaw)) setToken1AmountRaw(amountRaw.toString()); - } catch { } + } catch {} return { token1Amount: amount }; }); }, @@ -155,7 +155,7 @@ export const useStakeStore = create((set) => ({ setToken1AmountRaw(amount1.toString()); return { delta }; } - } catch { } + } catch {} return {}; }); }, @@ -177,7 +177,7 @@ export const useStakeStore = create((set) => ({ encodeSqrtPrice(priceN * 10 ** -decimals), ); tick = newTick; - } catch { } + } catch {} set({ tickLower: tick, priceLower: price, @@ -197,7 +197,7 @@ export const useStakeStore = create((set) => ({ encodeSqrtPrice(priceN * 10 ** -decimals), ); tick = newTick; - } catch { } + } catch {} set({ tickUpper: tick, priceUpper: price,