diff --git a/public/static/stake-background.webp b/public/static/stake-background.webp new file mode 100644 index 0000000..814eeaf Binary files /dev/null and b/public/static/stake-background.webp differ diff --git a/src/components/stakingPoolDetailsView.tsx b/src/components/stakingPoolDetailsView.tsx index 11008f1..966469a 100644 --- a/src/components/stakingPoolDetailsView.tsx +++ b/src/components/stakingPoolDetailsView.tsx @@ -1,12 +1,22 @@ -import StakingCalculator from "@/components/stakingCalculator"; -import UnstakingCalculator from "@/components/unstakingCalculator"; -import WithdrawZilPanel from "@/components/withdrawUnstakedZilPanel"; -import { WalletConnector } from "@/contexts/walletConnector"; -import { formatPercentage, formatUnitsToHumanReadable } from "@/misc/formatting"; -import { StakingPool } from "@/misc/stakingPoolsConfig"; -import { UserStakingPoolData, UserUnstakingPoolData } from "@/misc/walletsConfig"; -import { DateTime } from "luxon"; -import { useState } from "react"; +import StakingCalculator from '@/components/stakingCalculator'; +import UnstakingCalculator from '@/components/unstakingCalculator'; +import WithdrawZilPanel from '@/components/withdrawUnstakedZilPanel'; +import { WalletConnector } from '@/contexts/walletConnector'; +import { getViemClient } from '@/misc/chainConfig'; +import { + formatPercentage, + formatUnitsToHumanReadable, +} from '@/misc/formatting'; +import { StakingPool } from '@/misc/stakingPoolsConfig'; +import { + UserStakingPoolData, + UserUnstakingPoolData, +} from '@/misc/walletsConfig'; +import { Button } from 'antd'; +import { DateTime } from 'luxon'; +import { useState } from 'react'; +import { useWatchAsset } from 'wagmi'; +import { useWalletClient } from 'wagmi'; interface StakingPoolDetailsViewProps { stakingPoolData: StakingPool; @@ -15,131 +25,202 @@ interface StakingPoolDetailsViewProps { selectStakingPoolForStaking: (stakingPoolId: string) => void; } -const StakingPoolDetailsView: React.FC = ({ +const StakingPoolDetailsView: React.FC< + StakingPoolDetailsViewProps +> = ({ stakingPoolData, userStakingPoolData, userUnstakingPoolData, }) => { - - const { - zilAvailable, - } = WalletConnector.useContainer(); + const { zilAvailable } = WalletConnector.useContainer(); const [selectedPane, setSelectedPane] = useState('Stake'); const colorInfoEntry = (title: string, value: string | null) => (
-
- { value } -
-
- { title } -
+
{value}
+
{title}
- ) + ); - const greyInfoEntry = (title: string, value: string | JSX.Element | null) => ( + const greyInfoEntry = ( + title: string, + value: string | JSX.Element | null + ) => (
- { - value ? ( -
- { value } -
- ) : ( -
- ) - } -
- { title } + {value ? ( +
+ {value} +
+ ) : ( +
+ )} +
+ {title}
- ) - - const pendingUnstakesValue = userUnstakingPoolData?.filter( - (item) => item.availableAt > DateTime.now() - ).reduce( - (acc, item) => acc + item.zilAmount, - 0n ); - const availableToClaim = userUnstakingPoolData?.filter( - (item) => item.availableAt <= DateTime.now() - ).reduce( - (acc, item) => acc + item.zilAmount, - 0n + const pendingUnstakesValue = userUnstakingPoolData + ?.filter((item) => item.availableAt > DateTime.now()) + .reduce((acc, item) => acc + item.zilAmount, 0n); + + const availableToClaim = userUnstakingPoolData + ?.filter((item) => item.availableAt <= DateTime.now()) + .reduce((acc, item) => acc + item.zilAmount, 0n); + + const doesUserHoldAnyFundsInThisPool = !!( + userStakingPoolData?.stakingTokenAmount || + pendingUnstakesValue || + availableToClaim ); - const doesUserHoldAnyFundsInThisPool = !!(userStakingPoolData?.stakingTokenAmount || pendingUnstakesValue || availableToClaim); + const humanReadableStakingToken = (value: bigint) => + formatUnitsToHumanReadable( + value, + stakingPoolData.definition.tokenDecimals + ); - const humanReadableStakingToken = (value: bigint) => formatUnitsToHumanReadable(value, stakingPoolData.definition.tokenDecimals); + const { watchAsset } = useWatchAsset(); + + const handleClickAaddToken = () => + watchAsset( + { + type: 'ERC20', + options: { + address: stakingPoolData.definition.tokenAddress, + symbol: stakingPoolData.definition.tokenSymbol, + decimals: stakingPoolData.definition.tokenDecimals, + }, + }, + { + onSuccess: (data) => { + console.log('Asset watched successfully:', data); + }, + onError: (error) => { + console.error('Failed to watch the asset:', error); + }, + } + ); return ( -
- + {stakingPoolData.definition.name} - + {stakingPoolData.definition.tokenSymbol}
+
+ +
- {doesUserHoldAnyFundsInThisPool && -
- { colorInfoEntry("Available to stake", `${formatUnitsToHumanReadable(zilAvailable || 0n, 18)} ZIL`) } - { colorInfoEntry("Staked", `${humanReadableStakingToken(userStakingPoolData?.stakingTokenAmount || 0n)} ${stakingPoolData.definition.tokenSymbol}`) } - { colorInfoEntry("Unstake", pendingUnstakesValue ? `${humanReadableStakingToken(pendingUnstakesValue)} ${stakingPoolData.definition.tokenSymbol}`: "-" ) } - { colorInfoEntry("Available to claim", availableToClaim ? `${humanReadableStakingToken(availableToClaim)} ${stakingPoolData.definition.tokenSymbol}` : "-") } -
- } + {doesUserHoldAnyFundsInThisPool && ( +
+ {colorInfoEntry( + 'Available to stake', + `${formatUnitsToHumanReadable( + zilAvailable || 0n, + 18 + )} ZIL` + )} + {colorInfoEntry( + 'Staked', + `${humanReadableStakingToken( + userStakingPoolData?.stakingTokenAmount || 0n + )} ${stakingPoolData.definition.tokenSymbol}` + )} + {colorInfoEntry( + 'Unstake', + pendingUnstakesValue + ? `${humanReadableStakingToken( + pendingUnstakesValue + )} ${stakingPoolData.definition.tokenSymbol}` + : '-' + )} + {colorInfoEntry( + 'Available to claim', + availableToClaim + ? `${humanReadableStakingToken(availableToClaim)} ${ + stakingPoolData.definition.tokenSymbol + }` + : '-' + )} +
+ )}
- { greyInfoEntry("Voting power", stakingPoolData.data && formatPercentage(stakingPoolData.data.votingPower)) } - { greyInfoEntry("Total supply", stakingPoolData.data && `${humanReadableStakingToken(stakingPoolData.data.tvl)} ${stakingPoolData.definition.tokenSymbol}`) } - { greyInfoEntry("Commission", stakingPoolData.data && formatPercentage(stakingPoolData.data.commission)) } - { greyInfoEntry("", stakingPoolData.data && - ( + {greyInfoEntry( + 'Voting power', + stakingPoolData.data && + formatPercentage(stakingPoolData.data.votingPower) + )} + {greyInfoEntry( + 'Total supply', + stakingPoolData.data && + `${humanReadableStakingToken( + stakingPoolData.data.tvl + )} ${stakingPoolData.definition.tokenSymbol}` + )} + {greyInfoEntry( + 'Commission', + stakingPoolData.data && + formatPercentage(stakingPoolData.data.commission) + )} + {greyInfoEntry( + '', + stakingPoolData.data && ( <> 1 ZIL ~
- {stakingPoolData.data.zilToTokenRate.toPrecision(3)} {stakingPoolData.definition.tokenSymbol} + {stakingPoolData.data.zilToTokenRate.toPrecision( + 3 + )}{' '} + {stakingPoolData.definition.tokenSymbol} - )) } + ) + )}
- { - ['Stake', 'Unstake', 'Claim'].map((pane) => ( -
setSelectedPane(pane)} - > - {pane} -
- )) - } + {['Stake', 'Unstake', 'Claim'].map((pane) => ( +
setSelectedPane(pane)} + > + {pane} +
+ ))}
- { - selectedPane === 'Stake' ? ( - - ) : selectedPane === 'Unstake' ? ( - - ) : ( - - ) - } - + {selectedPane === 'Stake' ? ( + + ) : selectedPane === 'Unstake' ? ( + + ) : ( + + )}
- ) - -} + ); +}; -export default StakingPoolDetailsView; \ No newline at end of file +export default StakingPoolDetailsView; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index a242c4d..2c207dd 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -18,6 +18,7 @@ import { AppConfigStorage } from "@/contexts/appConfigStorage"; export default function App({ Component, pageProps }: AppProps) { const queryClient = new QueryClient(); const [appConfig, setAppConfig] = useState(null); + const [loadingPercentage, setLoadingPercentage] = useState(0); useEffect(() => { fetch("/api/config") @@ -26,10 +27,51 @@ export default function App({ Component, pageProps }: AppProps) { .catch(console.error); }, []); + useEffect(() => { + const fetchConfig = async () => { + try { + const res = await fetch("/api/config"); + const reader = res.body?.getReader(); + const contentLength = res.headers.get("Content-Length"); + + if (reader && contentLength) { + const totalLength = parseInt(contentLength, 10); + let loaded = 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + loaded += value?.length || 0; + const progress = Math.round((loaded / totalLength) * 100); + setLoadingPercentage(progress); + } + } + + const data = await res.json(); + setAppConfig(data); + } catch (error) { + console.error(error); + } + }; - if (!appConfig) { - return
Loading...
// APT-1605 - } + fetchConfig(); + }, []); + + + if (!appConfig) { + return( +
+
+
+
+
+
{loadingPercentage}%
+
+ ) +} return ( diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 642e44c..e456a0a 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -4,7 +4,7 @@ export default function Document() { return ( - +
diff --git a/tailwind.config.ts b/tailwind.config.ts index c4cc5d3..ea66437 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -13,8 +13,9 @@ const config: Config = { "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 'primary-gradient' : 'radial-gradient(120.62% 683.52% at 110.84% 156.15%, #C5FFFD 6.84%, rgba(111, 255, 194, 0.760784) 48.36%, #00DABA 100%)', - 'gradientbg': 'linear-gradient(129.93deg, rgba(175, 175, 175, 0.12) 16.6%, rgba(17, 243, 179, 0.12) 90.65%)', - 'darkbg': 'linear-gradient(314.92deg, rgba(17, 39, 49, 0.4) 28.08%, rgba(9, 9, 9, 0.4) 97.04%)', + 'gradientbg': 'linear-gradient(129.93deg, rgba(175, 175, 175, 0.12) 16.6%, rgba(17, 243, 179, 0.12) 90.65%)', + 'darkbg': 'linear-gradient(314.92deg, rgba(17, 39, 49, 0.4) 28.08%, rgba(9, 9, 9, 0.4) 97.04%)', + 'colorful-gradient':'linear-gradient(270deg, #00DABA 8%, #4AA1A3 23%, #8A7191 36%, #B15485 46%, #C14981 51%, #A73993 62%, #8726AA 78%, #741BB7 91%, #6D17BD 100%)', }, colors: { black1: '#010101', @@ -56,6 +57,7 @@ const config: Config = { '48': '48px', '64': '64px', '80': '80px', + '114': '114px' }, fontFamily: { "int-light": "interLight",