Skip to content

Commit

Permalink
support 9lives shares in longtail staking UI
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-d committed Dec 19, 2024
1 parent 6a3f5e1 commit e8a5547
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 13 deletions.
7 changes: 3 additions & 4 deletions web/src/app/stake/pool/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { motion } from "framer-motion";
import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react";
import { graphql, useFragment } from "@/gql";
import { useGraphqlGlobal } from "@/hooks/useGraphql";
import { useGetPool } from "@/hooks/useGraphql";
import { useFeatureFlag } from "@/hooks/useFeatureFlag";
import { usdFormat } from "@/lib/usdFormat";
import {
Expand Down Expand Up @@ -90,8 +90,6 @@ export default function PoolPage() {
const id = params.get("id");
const positionIdParam = Number(params.get("positionId"));

const { data: globalData } = useGraphqlGlobal();
const allPoolsData = useFragment(ManagePoolFragment, globalData?.pools);
const { positions: positionsData_, updatePositionLocal } = usePositions();
const positionsData = useMemo(
() =>
Expand Down Expand Up @@ -133,7 +131,8 @@ export default function PoolPage() {
handleTokens(token, fUSDC);
}, [id, expectedChainId, fUSDC, handleTokens, getTokenFromAddress]);

const poolData = allPoolsData?.find((pool) => pool.id === id);
const { data: getPoolData } = useGetPool(id as `0x${string}`);
const poolData = useFragment(ManagePoolFragment, getPoolData?.getPool);

const { liquidityCampaigns } = poolData || { liquidityCampaigns: [] };

Expand Down
15 changes: 11 additions & 4 deletions web/src/app/stake/pool/withdraw-liquidity/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ export default function WithdrawLiquidity() {
useStakeStore();

// Current tick of the pool
const { data: { result: curTickNum } = { result: 0 } } = useSimulateContract({
const { data: curTickData } = useSimulateContract({
address: ammContract.address,
abi: ammContract.abi,
functionName: "curTick181C6FD9",
args: [token0.address],
});
const curTick = BigInt(curTickNum);
const curTick = curTickData ? BigInt(curTickData.result) : undefined;

// Current liquidity of the position
const { data: positionLiquidity } = useSimulateContract({
Expand Down Expand Up @@ -107,7 +107,7 @@ export default function WithdrawLiquidity() {

// balanceUsd is the total balance of the position scaled to USD, used to determine a raw delta from a user-provided USD value
const balanceUsd = useMemo(() => {
if (curTick === 0n || !lowerTick || !upperTick) return 0;
if (curTick === undefined || !lowerTick || !upperTick) return 0;

const [amount0, amount1] = getAmountsForLiquidity(
getSqrtRatioAtTick(curTick),
Expand All @@ -133,6 +133,7 @@ export default function WithdrawLiquidity() {

// set the delta to delta/denom
const setDeltaOverDenom = (denom: number) =>
curTick !== undefined &&
setDelta(
(balanceUsd / denom).toString(),
curTick,
Expand All @@ -144,6 +145,7 @@ export default function WithdrawLiquidity() {
// TODO when clicking on a selected balance, should it unselect and set to 0?
const [balancePercent, setBalancePercent] = useState("");
const handleBalancePercentButton = (percentString: string) => {
if (curTick === undefined) return;
setBalancePercent(percentString);
switch (percentString) {
case "25%":
Expand Down Expand Up @@ -236,7 +238,12 @@ export default function WithdrawLiquidity() {
variant={"no-ring"}
value={deltaDisplay}
onChange={(e) => {
setDelta(e.target.value, curTick, positionBalance, balanceUsd);
setDelta(
e.target.value,
curTick ?? 0n,
positionBalance,
balanceUsd,
);
}}
/>
</div>
Expand Down
48 changes: 44 additions & 4 deletions web/src/components/StakeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Badge } from "@/components/ui/badge";
import ArrowDown from "@/assets/icons/arrow-down-white.svg";
import Padlock from "@/assets/icons/padlock.svg";
import Token from "@/assets/icons/token.svg";
import { useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { cn, EmptyToken } from "@/lib/utils";
import {
MAX_TICK,
MIN_TICK,
Expand Down Expand Up @@ -45,7 +45,7 @@ import Index from "@/components/Slider";
import { useWeb3Modal } from "@web3modal/wagmi/react";
import { useFeatureFlag } from "@/hooks/useFeatureFlag";
import { graphql, useFragment } from "@/gql";
import { useGraphqlGlobal } from "@/hooks/useGraphql";
import { useGetPool, useGraphqlGlobal } from "@/hooks/useGraphql";
import { usdFormat } from "@/lib/usdFormat";
import { useTokens, type Token as TokenType } from "@/config/tokens";
import { getFormattedPriceFromAmount } from "@/lib/amounts";
Expand All @@ -55,6 +55,7 @@ import { useContracts } from "@/config/contracts";
import { CheckboxContainer } from "./ui/checkbox";
import { superpositionMainnet, superpositionTestnet } from "@/config/chains";
import { usePositions } from "@/hooks/usePostions";
import LightweightERC20 from "@/config/abi/LightweightERC20";

type StakeFormProps = { poolId: string } & (
| {
Expand Down Expand Up @@ -87,6 +88,17 @@ const StakeFormFragment = graphql(`
}
`);

const StakeFormPoolFragment = graphql(`
fragment StakeFormPoolFragment on SeawaterPool {
token {
decimals
address
name
symbol
}
}
`);

export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => {
const [feeTier, setFeeTier] = useState<"auto" | "manual">("auto");
const { address, chainId } = useAccount();
Expand Down Expand Up @@ -133,7 +145,8 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => {
if (token0.address !== poolId && token1.address !== poolId) {
const poolToken = getTokenFromAddress(poolId);
if (!poolToken) {
router.push("/stake");
// check if this pool exists in the contract first
setToken0(EmptyToken);
return;
}
setToken0(poolToken);
Expand All @@ -160,6 +173,33 @@ export const StakeForm = ({ mode, poolId, positionId }: StakeFormProps) => {
(pool) => pool.address === poolId || pool.address === token0.address,
);

const { data: getPoolData } = useGetPool(poolId as `0x${string}`);
const poolFromId = useFragment(StakeFormPoolFragment, getPoolData?.getPool);
useEffect(() => {
if (!poolData) {
const { token } = poolFromId || {};
// this pool doesn't exist at all
if (!token) {
router.push("/stake");
return;
}
const name = token.name.split(/#\d+/)?.[1]?.trim();
// this pool isn't a 9lives share
if (!name) {
router.push("/stake");
return;
}
// this pool isn't tracked in the main list, but does exist - it's probably a 9lives share
setToken0({
...token,
address: token.address as `0x${string}`,
abi: LightweightERC20,
// assume 9lives tokens are always of the form '9lives #<number> <name>'
name,
});
}
}, [poolData, poolFromId, setToken0, router]);

useEffect(() => {
if (poolData?.fee) {
setFeePercentage(poolData.fee);
Expand Down
10 changes: 10 additions & 0 deletions web/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ const documents = {
"\n fragment MyPositionsInventoryWalletFragment on Wallet {\n id\n positions {\n positions {\n id\n pool {\n token {\n name\n address\n symbol\n }\n }\n }\n }\n }\n": types.MyPositionsInventoryWalletFragmentFragmentDoc,
"\n fragment TradeTabTransactionsFragment on SeawaterSwap {\n timestamp\n amountIn {\n token {\n symbol\n }\n valueScaled\n }\n amountOut {\n token {\n symbol\n }\n valueScaled\n }\n }\n": types.TradeTabTransactionsFragmentFragmentDoc,
"\n fragment StakeFormFragment on SeawaterPool {\n address\n earnedFeesAPRFUSDC\n fee\n config {\n classification\n }\n priceOverTime {\n daily\n }\n liquidity {\n tickLower\n tickUpper\n price\n liquidity\n }\n }\n": types.StakeFormFragmentFragmentDoc,
"\n fragment StakeFormPoolFragment on SeawaterPool {\n token {\n decimals\n address\n name\n symbol\n }\n }\n": types.StakeFormPoolFragmentFragmentDoc,
"\n fragment SwapFormFragment on SeawaterPool {\n address\n fee\n earnedFeesAPRFUSDC\n earnedFeesAPRToken1\n token {\n address\n decimals\n name\n symbol\n }\n }\n": types.SwapFormFragmentFragmentDoc,
"\n fragment SwapProPoolFragment on SeawaterPool {\n address\n token {\n address\n symbol\n }\n liquidity {\n liquidity\n }\n priceOverTime {\n daily\n monthly\n }\n volumeOverTime {\n monthly {\n token1 {\n timestamp\n valueUsd\n }\n fusdc {\n timestamp\n valueUsd\n }\n }\n daily {\n token1 {\n timestamp\n valueUsd\n }\n fusdc {\n timestamp\n valueUsd\n }\n }\n }\n liquidityOverTime {\n daily {\n timestamp\n fusdc {\n valueUsd\n }\n }\n monthly {\n timestamp\n fusdc {\n valueUsd\n }\n }\n }\n APR {\n total\n }\n swaps {\n swaps {\n transactionHash\n timestamp\n amountIn {\n valueScaled\n token {\n symbol\n }\n }\n amountOut {\n valueScaled\n token {\n symbol\n }\n }\n }\n }\n }\n": types.SwapProPoolFragmentFragmentDoc,
"\n fragment TokensFragment on SeawaterPool {\n token {\n address\n decimals\n name\n symbol\n image\n }\n }\n": types.TokensFragmentFragmentDoc,
"\n fragment FusdcFragment on Token {\n address\n decimals\n name\n symbol\n image\n }\n": types.FusdcFragmentFragmentDoc,
"\n query AllData {\n fusdc {\n address\n ...FusdcFragment\n }\n pools {\n # used for the pool selector\n address\n\n # add general fragments here\n ...SwapProPoolFragment\n ...AllPoolsFragment\n ...SelectPrimeAssetFragment\n ...SwapExploreFragment\n ...ManagePoolFragment\n ...SwapFormFragment\n ...StakeFormFragment\n ...TokensFragment\n }\n }\n": types.AllDataDocument,
"\n query ForUser($wallet: String!) {\n getSwapsForUser(wallet: $wallet, first: 10) {\n data {\n swaps {\n # add transaction fragments here\n ...TradeTabTransactionsFragment\n }\n }\n }\n\n getWallet(address: $wallet) {\n # add wallet fragments here\n ...MyPositionsInventoryWalletFragment\n ...PositionsFragment\n }\n\n notes(wallet: $wallet) {\n # add notes fragments here\n ...NotesFragment\n }\n }\n": types.ForUserDocument,
"\n query queryGetPoints($wallet: String!) {\n getPointsComponent(wallet: $wallet)\n }\n": types.QueryGetPointsDocument,
"\n query queryGetPool($token: String!) {\n getPool(token: $token) {\n ...StakeFormPoolFragment\n ...ManagePoolFragment\n }\n }\n": types.QueryGetPoolDocument,
"\n fragment PositionsFragment on Wallet {\n id\n positions {\n positions {\n created\n served {\n timestamp\n }\n positionId\n pool {\n token {\n name\n address\n symbol\n decimals\n }\n liquidityCampaigns {\n campaignId\n tickLower\n tickUpper\n fromTimestamp\n endTimestamp\n }\n }\n lower\n upper\n liquidity {\n fusdc {\n valueUsd\n }\n token1 {\n valueUsd\n }\n }\n isVested\n }\n }\n }\n": types.PositionsFragmentFragmentDoc,
};

Expand Down Expand Up @@ -77,6 +79,10 @@ export function graphql(source: "\n fragment TradeTabTransactionsFragment on Se
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment StakeFormFragment on SeawaterPool {\n address\n earnedFeesAPRFUSDC\n fee\n config {\n classification\n }\n priceOverTime {\n daily\n }\n liquidity {\n tickLower\n tickUpper\n price\n liquidity\n }\n }\n"): (typeof documents)["\n fragment StakeFormFragment on SeawaterPool {\n address\n earnedFeesAPRFUSDC\n fee\n config {\n classification\n }\n priceOverTime {\n daily\n }\n liquidity {\n tickLower\n tickUpper\n price\n liquidity\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment StakeFormPoolFragment on SeawaterPool {\n token {\n decimals\n address\n name\n symbol\n }\n }\n"): (typeof documents)["\n fragment StakeFormPoolFragment on SeawaterPool {\n token {\n decimals\n address\n name\n symbol\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down Expand Up @@ -105,6 +111,10 @@ export function graphql(source: "\n query ForUser($wallet: String!) {\n getS
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query queryGetPoints($wallet: String!) {\n getPointsComponent(wallet: $wallet)\n }\n"): (typeof documents)["\n query queryGetPoints($wallet: String!) {\n getPointsComponent(wallet: $wallet)\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query queryGetPool($token: String!) {\n getPool(token: $token) {\n ...StakeFormPoolFragment\n ...ManagePoolFragment\n }\n }\n"): (typeof documents)["\n query queryGetPool($token: String!) {\n getPool(token: $token) {\n ...StakeFormPoolFragment\n ...ManagePoolFragment\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading

0 comments on commit e8a5547

Please sign in to comment.