From 26c028dd414fb4b396127ca8f787e96bde200518 Mon Sep 17 00:00:00 2001 From: vidvidvid Date: Wed, 27 Nov 2024 13:05:33 +0100 Subject: [PATCH 1/6] set mode as default --- .../ui/app/_components/stake/MaxDeposit.tsx | 227 +++++++++--------- .../app/_components/stake/TokenSelector.tsx | 88 +++---- packages/ui/app/stake/page.tsx | 23 +- packages/ui/components/ui/button.tsx | 1 + packages/ui/constants/BorrowCapDisable.ts | 12 - 5 files changed, 177 insertions(+), 174 deletions(-) delete mode 100644 packages/ui/constants/BorrowCapDisable.ts diff --git a/packages/ui/app/_components/stake/MaxDeposit.tsx b/packages/ui/app/_components/stake/MaxDeposit.tsx index 75ba829b1c..c4aefb9952 100644 --- a/packages/ui/app/_components/stake/MaxDeposit.tsx +++ b/packages/ui/app/_components/stake/MaxDeposit.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @next/next/no-img-element */ -'use client'; - import { useState, useMemo, @@ -11,13 +8,22 @@ import { } from 'react'; import dynamic from 'next/dynamic'; +import Image from 'next/image'; import { formatUnits, parseUnits } from 'viem'; -// import { mode } from 'viem/chains'; import { useAccount, useBalance } from 'wagmi'; +import { Button } from '@ui/components/ui/button'; +import { Card, CardContent } from '@ui/components/ui/card'; +import { Input } from '@ui/components/ui/input'; + import TokenSelector from './TokenSelector'; +export interface IBal { + decimals: number; + value: bigint; +} + interface IMaxDeposit { amount?: string; tokenName?: string; @@ -32,11 +38,6 @@ interface IMaxDeposit { setMaxTokenForUtilization?: Dispatch>; } -export interface IBal { - decimals: number; - value: bigint; -} - function MaxDeposit({ headerText = 'Deposit', amount, @@ -71,138 +72,146 @@ function MaxDeposit({ value: parseUnits(max, data?.decimals ?? 18), decimals: data?.decimals ?? 18 }); - // setMaxTokenForUtilization && - // setMaxTokenForUtilization({ - // value: parseUnits(max, data?.decimals ?? 18), - // decimals: data?.decimals ?? 18 - // }); - } else if (max == '0') { + } else if (max === '0') { setBal({ value: BigInt(0), decimals: data?.decimals ?? 18 }); - // setMaxTokenForUtilization && - // setMaxTokenForUtilization({ - // value: BigInt(0), - // decimals: data?.decimals ?? 18 - // }); } else { data && setBal({ value: data?.value, decimals: data?.decimals }); } }, [data, max]); - // console.log(data); + function handlInpData(e: React.ChangeEvent) { if ( bal && - Number(e.target.value) > Number(formatUnits(bal?.value, bal?.decimals)) + Number(e.target.value) > Number(formatUnits(bal.value, bal.decimals)) ) return; if (!handleInput) return; handleInput(e.target.value); } + function handleMax(val: string) { if (!handleInput) return; handleInput(val); } - const newRef = useRef(null!); + const newRef = useRef(null); const [open, setOpen] = useState(false); + useEffect(() => { + const handleOutsideClick = (e: MouseEvent) => { + if (newRef.current && !newRef.current.contains(e.target as Node)) { + setOpen(false); + } + }; + document.addEventListener('mousedown', handleOutsideClick); return () => { document.removeEventListener('mousedown', handleOutsideClick); }; }, []); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const handleOutsideClick = (e: any) => { - //@ts-ignore - if (newRef.current && !newRef.current?.contains(e?.target)) { - setOpen(false); - } - }; + // Split tokenName if it contains multiple tokens + const tokens = tokenName?.split('/') ?? ['eth']; + return ( - <> -
- {headerText} -
- {' '} - {tokenName?.toUpperCase() ?? ''} Balance :{' '} - {bal - ? parseFloat(formatUnits(bal?.value, bal?.decimals)).toLocaleString( - 'en-US', - { - maximumFractionDigits: 3 - } - ) - : max} - {handleInput && ( - - )} -
-
-
- handlInpData(e)} - disabled={handleInput ? false : true} - /> + +
- {tokenSelector ? ( - - ) : ( - <> - {' '} - ion logo { - currentTarget.onerror = null; // prevents looping - currentTarget.src = '/img/logo/ION.png'; + {headerText} +
+ {tokenName?.toUpperCase() ?? ''} Balance:{' '} + {bal + ? parseFloat(formatUnits(bal.value, bal.decimals)).toLocaleString( + 'en-US', + { + maximumFractionDigits: 3 + } + ) + : max} + {handleInput && ( + + )} +
+
+
+ +
+ {tokenSelector ? ( + - {' '} - - )} + ) : ( +
+
+ {tokens.map((token, index) => ( +
0 ? '-0.5rem' : '0', + zIndex: tokens.length - index + }} + > + {`${token} { + const target = e.target as HTMLImageElement; + target.onerror = null; + target.src = '/img/logo/ION.png'; + }} + /> +
+ ))} +
+ {tokenName?.toUpperCase()} +
+ )} +
-
- + + ); } diff --git a/packages/ui/app/_components/stake/TokenSelector.tsx b/packages/ui/app/_components/stake/TokenSelector.tsx index 2c9227c694..fbb5c0d218 100644 --- a/packages/ui/app/_components/stake/TokenSelector.tsx +++ b/packages/ui/app/_components/stake/TokenSelector.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ 'use client'; /* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback } from 'react'; import Link from 'next/link'; import { usePathname, useSearchParams } from 'next/navigation'; @@ -11,22 +11,19 @@ interface ITokenSelector { open: boolean; setOpen: any; tokenArr?: string[]; - // chain: number; + selectedToken?: string; } export default function TokenSelector({ setOpen, open, newRef, - tokenArr = ['eth', 'weth'] + tokenArr = ['eth', 'weth'], + selectedToken }: ITokenSelector) { const pathname = usePathname(); const searchParams = useSearchParams(); - //URL passed Data ---------------------------- - const queryToken = searchParams.get('token'); - const selectedtokenQuery = queryToken ?? tokenArr[0]; - const createQueryString = useCallback( (value: string) => { const params = new URLSearchParams(searchParams.toString()); @@ -36,65 +33,56 @@ export default function TokenSelector({ [searchParams] ); - const [selectedtoken, setSelectedtoken] = useState(''); - - useEffect(() => { - setSelectedtoken(selectedtokenQuery); - }, [selectedtokenQuery]); return (
setOpen((prevState: any) => !prevState)} >
symbol - {selectedtoken?.toUpperCase() ?? 'Select Token'} + {selectedToken?.toUpperCase() ?? 'Select Token'} expand-arrow--v2
-
    - {tokenArr.map((token: string, idx: number) => ( - - {token.toUpperCase()}{' '} - {selectedtoken === token ? ( - checkmark--v1 - ) : ( - logos - )} - - ))} -
+ {open && ( +
    + {tokenArr?.map((token: string, idx: number) => ( + + {token.toUpperCase()}{' '} + {selectedToken === token ? ( + checkmark--v1 + ) : ( + logos + )} + + ))} +
+ )}
); diff --git a/packages/ui/app/stake/page.tsx b/packages/ui/app/stake/page.tsx index 12912d783f..770c0a2728 100644 --- a/packages/ui/app/stake/page.tsx +++ b/packages/ui/app/stake/page.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ 'use client'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import dynamic from 'next/dynamic'; import { useSearchParams } from 'next/navigation'; @@ -70,7 +70,24 @@ export default function Stake() { const searchParams = useSearchParams(); const querychain = searchParams.get('chain'); const queryToken = searchParams.get('token'); - const selectedtoken = queryToken ?? 'eth'; + const getDefaultToken = (chain: string) => { + return chain === '34443' ? 'mode' : 'eth'; + }; + + const selectedtoken = + queryToken ?? getDefaultToken(querychain ?? String(chainId)); + + useEffect(() => { + if (!queryToken && window) { + const url = new URL(window.location.href); + url.searchParams.set( + 'token', + getDefaultToken(querychain ?? String(chainId)) + ); + window.history.pushState({}, '', url); + } + }, [chainId, queryToken, querychain]); + const chain = querychain ? querychain : String(chainId); const stakingContractAddress = getStakingToContract( +chain, @@ -612,7 +629,7 @@ export default function Stake() { > = { - [mode.id]: { - '0': [] - }, - [base.id]: { - '0': [] - } -}; From 89a4ed232089bad285c1495373284ccb3c290f68 Mon Sep 17 00:00:00 2001 From: vidvidvid Date: Wed, 27 Nov 2024 13:19:01 +0100 Subject: [PATCH 2/6] unify MaxDeposit --- .../_components/{stake => }/MaxDeposit.tsx | 102 +++++---- .../dashboards/CollateralSwapPopup.tsx | 7 +- .../app/_components/dashboards/MaxDeposit.tsx | 206 ------------------ packages/ui/app/stake/page.tsx | 7 +- packages/ui/app/xION/page.tsx | 2 +- 5 files changed, 65 insertions(+), 259 deletions(-) rename packages/ui/app/_components/{stake => }/MaxDeposit.tsx (70%) delete mode 100644 packages/ui/app/_components/dashboards/MaxDeposit.tsx diff --git a/packages/ui/app/_components/stake/MaxDeposit.tsx b/packages/ui/app/_components/MaxDeposit.tsx similarity index 70% rename from packages/ui/app/_components/stake/MaxDeposit.tsx rename to packages/ui/app/_components/MaxDeposit.tsx index c4aefb9952..60724717aa 100644 --- a/packages/ui/app/_components/stake/MaxDeposit.tsx +++ b/packages/ui/app/_components/MaxDeposit.tsx @@ -1,6 +1,5 @@ import { useState, - useMemo, useEffect, useRef, type SetStateAction, @@ -10,14 +9,16 @@ import { import dynamic from 'next/dynamic'; import Image from 'next/image'; -import { formatUnits, parseUnits } from 'viem'; -import { useAccount, useBalance } from 'wagmi'; +import { formatUnits, type Address } from 'viem'; +import { useAccount, useBalance, useReadContract } from 'wagmi'; import { Button } from '@ui/components/ui/button'; import { Card, CardContent } from '@ui/components/ui/card'; import { Input } from '@ui/components/ui/input'; -import TokenSelector from './TokenSelector'; +import TokenSelector from './stake/TokenSelector'; + +import { icErc20Abi } from '@ionicprotocol/sdk'; export interface IBal { decimals: number; @@ -25,59 +26,77 @@ export interface IBal { } interface IMaxDeposit { - amount?: string; - tokenName?: string; - token?: `0x${string}`; + amount: string; + tokenName: string; + token?: Address; handleInput?: (val?: string) => void; fetchOwn?: boolean; headerText?: string; - max?: string; chain: number; tokenSelector?: boolean; tokenArr?: string[]; setMaxTokenForUtilization?: Dispatch>; + // Optional props for dashboard-specific features + useUnderlyingBalance?: boolean; // Flag to determine which balance to use + footerText?: string; + decimals?: number; } function MaxDeposit({ - headerText = 'Deposit', + headerText, amount, - tokenName = 'eth', + tokenName, token, handleInput, fetchOwn = false, - max = '', chain, tokenSelector = false, tokenArr, - setMaxTokenForUtilization + setMaxTokenForUtilization, + useUnderlyingBalance = false, // Default to regular balance + footerText, + decimals: propDecimals }: IMaxDeposit) { const [bal, setBal] = useState(); - const { address } = useAccount(); - const hooktoken = - token === '0x0000000000000000000000000000000000000000' ? undefined : token; - const { data } = useBalance({ + // Use either underlying balance or regular balance based on flag + const { data: regularBalance } = useBalance({ address, - token: hooktoken, + token, chainId: chain, query: { + enabled: !useUnderlyingBalance, + refetchInterval: 5000 + } + }); + + const { data: underlyingBalance } = useReadContract({ + abi: icErc20Abi, + address: token, + functionName: 'balanceOfUnderlying', + args: [address!], + query: { + enabled: useUnderlyingBalance, refetchInterval: 5000 } }); - useMemo(() => { - if (max) { - setBal({ - value: parseUnits(max, data?.decimals ?? 18), - decimals: data?.decimals ?? 18 + // Determine which balance and decimals to use + const balance = useUnderlyingBalance + ? underlyingBalance ?? 0n + : regularBalance?.value ?? 0n; + const decimals = propDecimals ?? regularBalance?.decimals ?? 18; + + useEffect(() => { + if (balance) { + setBal({ value: balance, decimals }); + setMaxTokenForUtilization?.({ + value: balance, + decimals }); - } else if (max === '0') { - setBal({ value: BigInt(0), decimals: data?.decimals ?? 18 }); - } else { - data && setBal({ value: data?.value, decimals: data?.decimals }); } - }, [data, max]); + }, [balance, decimals, setMaxTokenForUtilization]); function handlInpData(e: React.ChangeEvent) { if ( @@ -85,12 +104,11 @@ function MaxDeposit({ Number(e.target.value) > Number(formatUnits(bal.value, bal.decimals)) ) return; - if (!handleInput) return; - handleInput(e.target.value); + handleInput?.(e.target.value); } - function handleMax(val: string) { - if (!handleInput) return; + function handleMax(val?: string) { + if (!handleInput || !val) return; handleInput(val); } @@ -131,19 +149,14 @@ function MaxDeposit({ maximumFractionDigits: 3 } ) - : max} + : '0'} {handleInput && ( - )} - - -
- handlInpData(e)} - disabled={handleInput ? false : true} - /> -
- {tokenSelector ? ( - - ) : ( - <> - {' '} - ion logo { - currentTarget.onerror = null; // prevents looping - currentTarget.src = '/img/logo/ION.png'; - }} - /> - {' '} - - )} -
-
-
- {footerText} -
- - ); -} - -export default dynamic(() => Promise.resolve(MaxDeposit), { ssr: false }); diff --git a/packages/ui/app/stake/page.tsx b/packages/ui/app/stake/page.tsx index 770c0a2728..1869cf9f6f 100644 --- a/packages/ui/app/stake/page.tsx +++ b/packages/ui/app/stake/page.tsx @@ -38,11 +38,11 @@ import { } from '@ui/utils/getStakingTokens'; import { handleSwitchOriginChain } from '@ui/utils/NetworkChecker'; +import MaxDeposit from '../_components/MaxDeposit'; import SliderComponent from '../_components/popup/Slider'; import ResultHandler from '../_components/ResultHandler'; import BaseBreakdown from '../_components/stake/BaseBreakdown'; import ClaimRewards from '../_components/stake/ClaimRewards'; -import MaxDeposit from '../_components/stake/MaxDeposit'; import ModeBreakdown from '../_components/stake/ModeBreakdown'; import OPBreakdown from '../_components/stake/OPBreakdown'; import Toggle from '../_components/Toggle'; @@ -740,11 +740,6 @@ export default function Stake() { )} {step3Toggle === 'Unstake' && ( Date: Thu, 28 Nov 2024 10:07:17 +0100 Subject: [PATCH 3/6] set initial selected token --- packages/ui/app/stake/page.tsx | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/ui/app/stake/page.tsx b/packages/ui/app/stake/page.tsx index 1869cf9f6f..b56cb99122 100644 --- a/packages/ui/app/stake/page.tsx +++ b/packages/ui/app/stake/page.tsx @@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from 'react'; import dynamic from 'next/dynamic'; -import { useSearchParams } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import { erc20Abi, @@ -68,25 +68,39 @@ export default function Stake() { //--------------- const chainId = useChainId(); const searchParams = useSearchParams(); + const router = useRouter(); const querychain = searchParams.get('chain'); const queryToken = searchParams.get('token'); + const getDefaultToken = (chain: string) => { - return chain === '34443' ? 'mode' : 'eth'; + return chain === String(mode.id) ? 'mode' : 'eth'; }; const selectedtoken = queryToken ?? getDefaultToken(querychain ?? String(chainId)); useEffect(() => { - if (!queryToken && window) { - const url = new URL(window.location.href); - url.searchParams.set( - 'token', - getDefaultToken(querychain ?? String(chainId)) - ); - window.history.pushState({}, '', url); + // Create new URLSearchParams instance + const params = new URLSearchParams(searchParams); + const currentChain = querychain ?? String(chainId); + let shouldUpdate = false; + + // If chain is Mode, ensure token is 'mode' + if (currentChain === String(mode.id) && params.get('token') !== 'mode') { + params.set('token', 'mode'); + shouldUpdate = true; + } + // For other chains, set default token if none exists + else if (!params.get('token')) { + params.set('token', getDefaultToken(currentChain)); + shouldUpdate = true; + } + + // Only update if changes were made + if (shouldUpdate) { + router.push(`?${params.toString()}`, { scroll: false }); } - }, [chainId, queryToken, querychain]); + }, [chainId, querychain, router, searchParams]); const chain = querychain ? querychain : String(chainId); const stakingContractAddress = getStakingToContract( From a2cd52204cb03e1dfffa98d0a0035accd44e67f6 Mon Sep 17 00:00:00 2001 From: vidvidvid Date: Thu, 28 Nov 2024 11:01:31 +0100 Subject: [PATCH 4/6] max deposit wip fix max deposit --- packages/ui/app/_components/MaxDeposit.tsx | 115 ++++++++++-------- .../app/_components/markets/StakingTile.tsx | 23 +--- .../app/_components/stake/RewardDisplay.tsx | 112 +++++++++++++++++ packages/ui/app/stake/page.tsx | 89 +++++++++----- 4 files changed, 237 insertions(+), 102 deletions(-) create mode 100644 packages/ui/app/_components/stake/RewardDisplay.tsx diff --git a/packages/ui/app/_components/MaxDeposit.tsx b/packages/ui/app/_components/MaxDeposit.tsx index 60724717aa..b30f11f249 100644 --- a/packages/ui/app/_components/MaxDeposit.tsx +++ b/packages/ui/app/_components/MaxDeposit.tsx @@ -1,6 +1,6 @@ import { useState, - useEffect, + useMemo, useRef, type SetStateAction, type Dispatch @@ -9,16 +9,15 @@ import { import dynamic from 'next/dynamic'; import Image from 'next/image'; -import { formatUnits, type Address } from 'viem'; +import { formatUnits, parseUnits, type Address } from 'viem'; import { useAccount, useBalance, useReadContract } from 'wagmi'; import { Button } from '@ui/components/ui/button'; import { Card, CardContent } from '@ui/components/ui/card'; import { Input } from '@ui/components/ui/input'; -import TokenSelector from './stake/TokenSelector'; - import { icErc20Abi } from '@ionicprotocol/sdk'; +import TokenSelector from './stake/TokenSelector'; export interface IBal { decimals: number; @@ -26,44 +25,48 @@ export interface IBal { } interface IMaxDeposit { - amount: string; - tokenName: string; + amount?: string; + tokenName?: string; token?: Address; handleInput?: (val?: string) => void; fetchOwn?: boolean; headerText?: string; + max?: string; chain: number; tokenSelector?: boolean; tokenArr?: string[]; setMaxTokenForUtilization?: Dispatch>; - // Optional props for dashboard-specific features - useUnderlyingBalance?: boolean; // Flag to determine which balance to use + useUnderlyingBalance?: boolean; footerText?: string; decimals?: number; } function MaxDeposit({ - headerText, + headerText = 'Deposit', amount, - tokenName, + tokenName = 'eth', token, handleInput, fetchOwn = false, + max, chain, tokenSelector = false, tokenArr, setMaxTokenForUtilization, - useUnderlyingBalance = false, // Default to regular balance + useUnderlyingBalance = false, footerText, decimals: propDecimals }: IMaxDeposit) { const [bal, setBal] = useState(); const { address } = useAccount(); - // Use either underlying balance or regular balance based on flag + // For regular token balance + const hooktoken = + token === '0x0000000000000000000000000000000000000000' ? undefined : token; + const { data: regularBalance } = useBalance({ address, - token, + token: hooktoken, chainId: chain, query: { enabled: !useUnderlyingBalance, @@ -71,32 +74,50 @@ function MaxDeposit({ } }); + // For underlying token balance const { data: underlyingBalance } = useReadContract({ abi: icErc20Abi, address: token, functionName: 'balanceOfUnderlying', args: [address!], query: { - enabled: useUnderlyingBalance, + enabled: useUnderlyingBalance && !!address, refetchInterval: 5000 } }); - // Determine which balance and decimals to use - const balance = useUnderlyingBalance - ? underlyingBalance ?? 0n - : regularBalance?.value ?? 0n; - const decimals = propDecimals ?? regularBalance?.decimals ?? 18; - - useEffect(() => { - if (balance) { - setBal({ value: balance, decimals }); + useMemo(() => { + const decimals = propDecimals ?? regularBalance?.decimals ?? 18; + + if (max) { + const value = parseUnits(max, decimals); + setBal({ value, decimals }); + setMaxTokenForUtilization?.({ value, decimals }); + } else if (max === '0') { + setBal({ value: BigInt(0), decimals }); + setMaxTokenForUtilization?.({ value: BigInt(0), decimals }); + } else if (useUnderlyingBalance) { + const value = underlyingBalance ?? BigInt(0); + setBal({ value, decimals }); + setMaxTokenForUtilization?.({ value, decimals }); + } else if (!useUnderlyingBalance && regularBalance) { + setBal({ + value: regularBalance.value, + decimals: regularBalance.decimals + }); setMaxTokenForUtilization?.({ - value: balance, - decimals + value: regularBalance.value, + decimals: regularBalance.decimals }); } - }, [balance, decimals, setMaxTokenForUtilization]); + }, [ + max, + regularBalance, + underlyingBalance, + useUnderlyingBalance, + propDecimals, + setMaxTokenForUtilization + ]); function handlInpData(e: React.ChangeEvent) { if ( @@ -104,31 +125,23 @@ function MaxDeposit({ Number(e.target.value) > Number(formatUnits(bal.value, bal.decimals)) ) return; - handleInput?.(e.target.value); + if (!handleInput) return; + handleInput(e.target.value); } - function handleMax(val?: string) { - if (!handleInput || !val) return; - handleInput(val); + function handleMax() { + if (!handleInput || !bal) return; + const maxValue = formatUnits(bal.value, bal.decimals); + handleInput(maxValue); + setMaxTokenForUtilization?.({ + value: bal.value, + decimals: bal.decimals + }); } const newRef = useRef(null); const [open, setOpen] = useState(false); - useEffect(() => { - const handleOutsideClick = (e: MouseEvent) => { - if (newRef.current && !newRef.current.contains(e.target as Node)) { - setOpen(false); - } - }; - - document.addEventListener('mousedown', handleOutsideClick); - return () => { - document.removeEventListener('mousedown', handleOutsideClick); - }; - }, []); - - // Split tokenName if it contains multiple tokens const tokens = tokenName?.split('/') ?? ['eth']; return ( @@ -149,22 +162,20 @@ function MaxDeposit({ maximumFractionDigits: 3 } ) - : '0'} + : max ?? '0'} {handleInput && ( )} -
+
-
+
{tokenSelector ? (

$ION Staking

- {/*
- APY - 150% -
-
- Ionic Staked - 32678.4 -
*/} - {+chain === mode.id && ( - - )} - {+chain === base.id && } - {+chain !== mode.id && +chain !== base.id && ( + ) : ( Stake your IONs on Base/Mode diff --git a/packages/ui/app/_components/stake/RewardDisplay.tsx b/packages/ui/app/_components/stake/RewardDisplay.tsx new file mode 100644 index 0000000000..6bb327fa9b --- /dev/null +++ b/packages/ui/app/_components/stake/RewardDisplay.tsx @@ -0,0 +1,112 @@ +import Image from 'next/image'; +import { base, mode, optimism } from 'viem/chains'; +import { BaseSugarAddress } from '@ui/constants/baselp'; +import { ModeSugarAddress } from '@ui/constants/lp'; +import { OPSugarAddress } from '@ui/constants/oplp'; +import useSugarAPR from '@ui/hooks/useSugarAPR'; + +type ChainConfig = { + poolIndex: bigint; + sugarAddress: `0x${string}`; + rewardToken: { + name: string; + logo: string; + }; +}; + +type ChainConfigs = { + [key: number]: { + defaultConfig: ChainConfig; + tokenConfigs?: { + [key: string]: ChainConfig; + }; + }; +}; + +export const CHAIN_CONFIGS: ChainConfigs = { + [mode.id]: { + defaultConfig: { + poolIndex: 6n, + sugarAddress: ModeSugarAddress, + rewardToken: { + name: 'Velodrome', + logo: '/img/symbols/32/color/velo.png' + } + }, + tokenConfigs: { + mode: { + poolIndex: 26n, + sugarAddress: ModeSugarAddress, + rewardToken: { + name: 'Velodrome', + logo: '/img/symbols/32/color/velo.png' + } + } + } + }, + [optimism.id]: { + defaultConfig: { + poolIndex: 910n, + sugarAddress: OPSugarAddress, + rewardToken: { + name: 'Velodrome', + logo: '/img/symbols/32/color/velo.png' + } + } + }, + [base.id]: { + defaultConfig: { + poolIndex: 1489n, + sugarAddress: BaseSugarAddress, + rewardToken: { + name: 'Aerodrome', + logo: '/img/logo/AERO.png' + } + } + } +}; + +type RewardDisplayProps = { + chainId: number; + isUnstaking?: boolean; + selectedToken?: 'eth' | 'mode' | 'weth'; +}; + +export default function RewardDisplay({ + chainId, + isUnstaking = false, + selectedToken = 'eth' +}: RewardDisplayProps) { + const chainConfig = CHAIN_CONFIGS[chainId]; + + const config = + chainConfig.tokenConfigs?.[selectedToken] || chainConfig.defaultConfig; + + const { apr } = useSugarAPR({ + sugarAddress: config.sugarAddress, + poolIndex: config.poolIndex, + chainId, + selectedToken, + isMode: chainId === mode.id + }); + + if (!chainConfig) return null; + + return ( +
+
+ {`${config.rewardToken.name} +
+ {config.rewardToken.name} APR + + {apr} + +
+ ); +} diff --git a/packages/ui/app/stake/page.tsx b/packages/ui/app/stake/page.tsx index b56cb99122..be905c6749 100644 --- a/packages/ui/app/stake/page.tsx +++ b/packages/ui/app/stake/page.tsx @@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from 'react'; import dynamic from 'next/dynamic'; -import { useRouter, useSearchParams } from 'next/navigation'; +import { useSearchParams, useRouter } from 'next/navigation'; import { erc20Abi, @@ -38,13 +38,12 @@ import { } from '@ui/utils/getStakingTokens'; import { handleSwitchOriginChain } from '@ui/utils/NetworkChecker'; +import RewardDisplay from '../_components/stake/RewardDisplay'; + import MaxDeposit from '../_components/MaxDeposit'; import SliderComponent from '../_components/popup/Slider'; import ResultHandler from '../_components/ResultHandler'; -import BaseBreakdown from '../_components/stake/BaseBreakdown'; import ClaimRewards from '../_components/stake/ClaimRewards'; -import ModeBreakdown from '../_components/stake/ModeBreakdown'; -import OPBreakdown from '../_components/stake/OPBreakdown'; import Toggle from '../_components/Toggle'; const NetworkSelector = dynamic( @@ -67,51 +66,71 @@ export default function Stake() { const [step3Toggle, setstep3Toggle] = useState(''); //--------------- const chainId = useChainId(); - const searchParams = useSearchParams(); const router = useRouter(); + const searchParams = useSearchParams(); const querychain = searchParams.get('chain'); const queryToken = searchParams.get('token'); + const chain = querychain ? querychain : String(chainId); + console.log('chain', chain); const getDefaultToken = (chain: string) => { return chain === String(mode.id) ? 'mode' : 'eth'; }; - const selectedtoken = queryToken ?? getDefaultToken(querychain ?? String(chainId)); + console.log('selectedtoken', selectedtoken); + + const stakingContractAddress = getStakingToContract( + +chain, + selectedtoken as 'eth' | 'mode' | 'weth' + ); + const stakingTokenAddress = getAvailableStakingToken( + +chain, + selectedtoken as 'eth' | 'mode' | 'weth' + ); + + function resetAllInputs() { + setMaxDeposit({ ion: '', eth: '' }); + setMaxWithdrawl({ ion: '', eth: '' }); + setUtilization(0); + setMaxLp(''); + setMaxUnstake(''); + } + + useEffect(() => { + resetAllInputs(); + }, [step2Toggle, step3Toggle, chain]); useEffect(() => { - // Create new URLSearchParams instance const params = new URLSearchParams(searchParams); const currentChain = querychain ?? String(chainId); let shouldUpdate = false; - // If chain is Mode, ensure token is 'mode' - if (currentChain === String(mode.id) && params.get('token') !== 'mode') { - params.set('token', 'mode'); - shouldUpdate = true; + // If on Mode chain, ALWAYS set token to 'mode' + if (currentChain === String(mode.id)) { + if (params.get('token') !== 'mode') { + params.set('token', 'mode'); + shouldUpdate = true; + } } - // For other chains, set default token if none exists - else if (!params.get('token')) { - params.set('token', getDefaultToken(currentChain)); - shouldUpdate = true; + // For other chains + else { + // Get available tokens for current chain + const availableTokens = tokenArrOfChain[+currentChain] || ['eth', 'weth']; + const currentToken = params.get('token'); + + // If current token isn't available on this chain or no token is set + if (!currentToken || !availableTokens.includes(currentToken)) { + params.set('token', 'eth'); + shouldUpdate = true; + } } - // Only update if changes were made if (shouldUpdate) { router.push(`?${params.toString()}`, { scroll: false }); } }, [chainId, querychain, router, searchParams]); - const chain = querychain ? querychain : String(chainId); - const stakingContractAddress = getStakingToContract( - +chain, - selectedtoken as 'eth' | 'mode' | 'weth' - ); - const stakingTokenAddress = getAvailableStakingToken( - +chain, - selectedtoken as 'eth' | 'mode' | 'weth' - ); - const { address, isConnected } = useAccount(); const publicClient = usePublicClient(); const { data: walletClient } = useWalletClient(); @@ -754,6 +773,11 @@ export default function Stake() { )} {step3Toggle === 'Unstake' && ( {/* breakdowns */} - {+chain === mode.id && ( - )} - {+chain === optimism.id && ( - - )} - {+chain === base.id && }