diff --git a/src/app/stake/page.js b/src/app/stake/page.js
index ceca21d..073f8c9 100644
--- a/src/app/stake/page.js
+++ b/src/app/stake/page.js
@@ -1,14 +1,11 @@
-'use client';
-
-import { useEffect, useState, useMemo } from 'react';
-import { Image } from '@chakra-ui/react';
-import Lottie from 'react-lottie-player';
-import lottieJson from '@/assets/animations/PE2.json';
-import XeonStakingPoolABI from '@/abi/XeonStakingPool.abi.json';
-import { Constants } from '@/abi/constants';
-import Header from '@/components/Header';
-import UserAssets from '@/components/staking/UserAssets';
-import { ethers } from 'ethers';
+"use client";
+
+import {useEffect, useState, useMemo} from "react";
+import XeonStakingPoolABI from "@/abi/XeonStakingPool.abi.json";
+import {Constants} from "@/abi/constants";
+import Header from "@/components/Header";
+import UserAssets from "@/components/staking/UserAssets";
+import {ethers} from "ethers";
import {
Modal,
ModalOverlay,
@@ -18,23 +15,29 @@ import {
ModalFooter,
Spinner,
useDisclosure,
-} from '@chakra-ui/react';
-import BookmarkAdded from '@/components/BookmarkAdded';
+} from "@chakra-ui/react";
+import BookmarkAdded from "@/components/BookmarkAdded";
+import {useActiveAccount} from "thirdweb/react";
function Page() {
- const [voteValue, setVoteValue] = useState(5); // state for user buyback vote value
- // todo: for mainnet, ensure currentPercentage is proper default
- const [currentPercentage, setCurrentPercentage] = useState(5); // state for current buyback percentage
const [loading, setLoading] = useState(false);
- const [message, setMessage] = useState('');
+ const [message, setMessage] = useState("");
+ const [epoch, setEpoch] = useState("0.00"); // todo: app doesn't set epoch, only reads it (value is whole number integer)
+ const [ethInPool, setEthInPool] = useState("0.00");
+ const [buyBackPercentage, setBuyBackPercentage] = useState("0.00");
+ const [teamPercentage, setTeamPercentage] = useState("0.00");
+ const [walletXeonBalance, setWalletXeonBalance] = useState("0.00"); // todo: display user's contract balance
+ const [stakedXeonBalance, setStakedXeonBalance] = useState("0.00"); // todo: display user's staked balance
+ const wallet = useActiveAccount();
+ const connectedAddress = wallet?.address;
const [provider, setProvider] = useState(null);
const [signer, setSigner] = useState(null);
- const { isOpen, onOpen, onClose } = useDisclosure();
+ const {isOpen, onOpen, onClose} = useDisclosure();
// init provider and signer
useEffect(() => {
const initializeProvider = async () => {
- if (typeof window !== 'undefined' && window.ethereum) {
+ if (typeof window !== "undefined" && window.ethereum) {
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = web3Provider.getSigner();
setProvider(web3Provider);
@@ -55,6 +58,59 @@ function Page() {
);
}, [provider, signer]);
+ const XeonToken = useMemo(() => {
+ if (!provider || !signer) return null;
+ return new ethers.Contract(
+ Constants.testnet.XeonToken,
+ XeonStakingPoolABI,
+ signer
+ );
+ }, [provider, signer]);
+ const WETH = useMemo(() => {
+ if (!provider) return null;
+ return new ethers.Contract(
+ Constants.testnet.WETH,
+ XeonStakingPoolABI,
+ provider
+ );
+ }, [provider]);
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ if (!XeonStakingPool || !WETH || !XeonToken || !connectedAddress)
+ return;
+
+ const epoch = await XeonStakingPool.epoch();
+ setEpoch(ethers.utils.formatUnits(epoch, 0));
+
+ const ethBalance = await WETH.balanceOf(
+ Constants.testnet.XeonStakingPool
+ );
+ setEthInPool(ethers.utils.formatEther(ethBalance));
+
+ const buyBackPercentage = await XeonStakingPool.buyBackPercentage();
+ setBuyBackPercentage(ethers.utils.formatUnits(buyBackPercentage, 0));
+
+ const teamPercentage = await XeonStakingPool.teamPercentage();
+ setTeamPercentage(ethers.utils.formatUnits(teamPercentage, 0));
+
+ const xeonBalance = await XeonToken.balanceOf(connectedAddress);
+ setWalletXeonBalance(ethers.utils.formatEther(xeonBalance));
+
+ const stakedXeonBalance = await XeonStakingPool.balanceOf(
+ connectedAddress
+ );
+ setStakedXeonBalance(ethers.utils.formatEther(stakedXeonBalance));
+ } catch (error) {
+ console.error("Error fetching asset values:", error);
+ }
+ };
+
+ if (connectedAddress) {
+ fetchData();
+ }
+ }, [connectedAddress, XeonStakingPool, XeonToken, WETH]);
+
useEffect(() => {
// fetch current buyback percentage from contract
const fetchBuybackPercentage = async () => {
@@ -64,7 +120,7 @@ function Page() {
const percentage = await XeonStakingPool.buyBackPercentage(); // assume integer value from contract
setCurrentPercentage(percentage.toNumber()); // update state with value
} catch (error) {
- console.error('Error fetching buyback percentage:', error);
+ console.error("Error fetching buyback percentage:", error);
}
}
};
@@ -72,170 +128,60 @@ function Page() {
fetchBuybackPercentage();
}, [XeonStakingPool]);
- // handle increment and decrement of vote value
- // todo: for mainnet, ensure vote value is clamped to contract min/max
- const handleIncrement = () => {
- setVoteValue((prevValue) => Math.min(prevValue + 1, 100));
- };
-
- const handleDecrement = () => {
- setVoteValue((prevValue) => Math.max(prevValue - 1, 1));
- };
-
- const handleVote = async () => {
- if (!XeonStakingPool || voteValue < 1 || voteValue > 100) {
- setMessage('Please enter a value between 1 and 100');
- return;
- }
-
- setLoading(true);
- onOpen();
-
- try {
- const tx = await XeonStakingPool.voteForBuybackPercentage(voteValue);
- await tx.wait();
- setLoading(false);
- setMessage(`Vote successful for ${voteValue}% buyback`);
- } catch (error) {
- console.error('Vote failed', error);
- setLoading(false);
- setMessage('Vote failed, please try again.');
- }
- };
-
- // handle vote value change
- const handleVoteChange = (e) => {
- const value = parseInt(e.target.value);
- if (Number.isNaN(value)) {
- setVoteValue(1);
- } else {
- setVoteValue(Math.min(Math.max(value, 1), 100));
- }
- };
-
return (
-
-
-
Stake
-
- Xeon
-
-
-
-
-
-
-
+
+
+
Stake your XEON tokens in just two simple steps.
-
-
+
+
Stake XEON tokens to be eligible for revenue sharing. The staking
window opens for 3 days at the end of each epoch, at which time
XEON can be staked or unstaked. Protocol revenue is deposited is
deposited into the staking pool.
-
-
-
-
-
-
-
-
-
- Settle
-
-
- Close expired positions and collect fess into the staking pool
-
-
-
- Settle
-
+
-
-
-
- $XEON Buyback
-
-
- What percentage of protocol revenue should be used to buyback
- $XEON token?
-
-
-
- -
-
-
-
-
-
- +
-
-
- Vote
-
+
+
Epoch # {epoch}
+
{ethInPool} ETH in pool
+
+ $XEON Buyback: {buyBackPercentage}%
+
+
+ Team Percentage: {teamPercentage}%
+
-
- Current Buyback Percentage: {currentPercentage}%
-
+
+
+
+
-
-
+
+
Vote Feedback
-
+
{loading ? (
) : (
)}
diff --git a/src/components/staking/UserAssets.js b/src/components/staking/UserAssets.js
index aa6e061..1e0a693 100644
--- a/src/components/staking/UserAssets.js
+++ b/src/components/staking/UserAssets.js
@@ -1,4 +1,4 @@
-'use client';
+"use client";
import {
FormControl,
FormLabel,
@@ -12,41 +12,36 @@ import {
ModalCloseButton,
ModalFooter,
useDisclosure,
-} from '@chakra-ui/react';
-import { motion } from 'framer-motion';
-import { useState, useEffect, useMemo } from 'react';
-import { FaEthereum } from 'react-icons/fa';
-import AssetsValues from '../wallet/AssetsValues';
-import { useActiveAccount } from 'thirdweb/react';
-import { ethers } from 'ethers';
-import XeonStakingPoolABI from '@/abi/XeonStakingPool.abi.json';
-import { Constants } from '@/abi/constants';
-import BookmarkAdded from '../BookmarkAdded';
+} from "@chakra-ui/react";
+import {motion} from "framer-motion";
+import {useState, useEffect, useMemo} from "react";
+
+import {ethers} from "ethers";
+import XeonStakingPoolABI from "@/abi/XeonStakingPool.abi.json";
+import {Constants} from "@/abi/constants";
+import BookmarkAdded from "../BookmarkAdded";
+import {useActiveAccount} from "thirdweb/react";
function UserAssets() {
const [isSwitched, setIsSwitched] = useState(false);
const [walletBalance, setWalletBalance] = useState(0);
const [stakedBalance, setStakedBalance] = useState(0);
- const [stakeAmount, setStakeAmount] = useState('');
+ const [stakeAmount, setStakeAmount] = useState("");
const [isApproved, setIsApproved] = useState(false);
- const [buttonText, setButtonText] = useState('APPROVE');
+ const [buttonText, setButtonText] = useState("APPROVE");
const [loading, setLoading] = useState(false);
- const [message, setMessage] = useState('');
- const [status, setStatus] = useState('');
- const [epoch, setEpoch] = useState('0.00'); // todo: app doesn't set epoch, only reads it (value is whole number integer)
- const [ethInPool, setEthInPool] = useState('0.00');
- const [buyBackPercentage, setBuyBackPercentage] = useState('0.00');
- const [teamPercentage, setTeamPercentage] = useState('0.00');
- const [walletXeonBalance, setWalletXeonBalance] = useState('0.00'); // todo: display user's contract balance
- const [stakedXeonBalance, setStakedXeonBalance] = useState('0.00'); // todo: display user's staked balance
- const { isOpen, onOpen, onClose } = useDisclosure();
- const wallet = useActiveAccount();
- const connectedAddress = wallet?.address;
+ const [message, setMessage] = useState("");
+ const [status, setStatus] = useState("");
+ const {isOpen, onOpen, onClose} = useDisclosure();
const [provider, setProvider] = useState(null);
const [signer, setSigner] = useState(null);
+ const wallet = useActiveAccount();
+ const [voteValue, setVoteValue] = useState(5); // state for user buyback vote value
+ // todo: for mainnet, ensure currentPercentage is proper default
+ const [currentPercentage, setCurrentPercentage] = useState(5); // state for current buyback percentage
useEffect(() => {
- if (typeof window !== 'undefined' && window.ethereum) {
+ if (typeof window !== "undefined" && window.ethereum) {
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = web3Provider.getSigner();
setProvider(web3Provider);
@@ -72,51 +67,6 @@ function UserAssets() {
);
}, [provider, signer]);
- const WETH = useMemo(() => {
- if (!provider) return null;
- return new ethers.Contract(
- Constants.testnet.WETH,
- XeonStakingPoolABI,
- provider
- );
- }, [provider]);
-
- useEffect(() => {
- const fetchData = async () => {
- try {
- if (!XeonStakingPool || !WETH || !XeonToken || !connectedAddress)
- return;
-
- const epoch = await XeonStakingPool.epoch();
- setEpoch(ethers.utils.formatUnits(epoch, 0));
-
- const ethBalance = await WETH.balanceOf(
- Constants.testnet.XeonStakingPool
- );
- setEthInPool(ethers.utils.formatEther(ethBalance));
-
- const buyBackPercentage = await XeonStakingPool.buyBackPercentage();
- setBuyBackPercentage(ethers.utils.formatUnits(buyBackPercentage, 0));
-
- const teamPercentage = await XeonStakingPool.teamPercentage();
- setTeamPercentage(ethers.utils.formatUnits(teamPercentage, 0));
-
- const xeonBalance = await XeonToken.balanceOf(connectedAddress);
- setWalletXeonBalance(ethers.utils.formatEther(xeonBalance));
-
- const stakedXeonBalance =
- await XeonStakingPool.balanceOf(connectedAddress);
- setStakedXeonBalance(ethers.utils.formatEther(stakedXeonBalance));
- } catch (error) {
- console.error('Error fetching asset values:', error);
- }
- };
-
- if (connectedAddress) {
- fetchData();
- }
- }, [connectedAddress, XeonStakingPool, XeonToken, WETH]);
-
useEffect(() => {
if (wallet && XeonToken && XeonStakingPool) {
XeonToken.balanceOf(wallet.address).then((balance) => {
@@ -131,7 +81,7 @@ function UserAssets() {
(allowance) => {
if (ethers.utils.formatEther(allowance) > 0) {
setIsApproved(true);
- setButtonText('STAKE');
+ setButtonText("STAKE");
}
}
);
@@ -145,6 +95,10 @@ function UserAssets() {
const handleApprove = async () => {
setLoading(true);
try {
+ if (parseFloat(stakeAmount) > parseFloat(walletBalance)) {
+ throw new Error("Amount exceeds wallet balance");
+ }
+
if (!isApproved && XeonToken) {
const tx = await XeonToken.approve(
XeonStakingPool.address,
@@ -152,14 +106,14 @@ function UserAssets() {
);
await tx.wait();
setIsApproved(true);
- setButtonText('STAKE');
- setStatus('success');
- setMessage('Approval successful!');
+ setButtonText("STAKE");
+ setStatus("success");
+ setMessage("Approval successful!");
}
} catch (error) {
- setStatus('error');
- setMessage('Approval failed.');
- console.error('Approval failed', error);
+ setStatus("error");
+ setMessage(error.message || "Approval failed.");
+ console.error("Approval failed", error);
} finally {
setLoading(false);
onOpen();
@@ -169,18 +123,22 @@ function UserAssets() {
const handleStake = async () => {
setLoading(true);
try {
+ if (parseFloat(stakeAmount) > parseFloat(walletBalance)) {
+ throw new Error("Amount exceeds wallet balance");
+ }
+
if (isApproved && XeonStakingPool) {
const tx = await XeonStakingPool.stake(
ethers.utils.parseEther(stakeAmount)
);
await tx.wait();
- setStatus('success');
- setMessage('Stake successful!');
+ setStatus("success");
+ setMessage("Stake successful!");
}
} catch (error) {
- setStatus('error');
- setMessage('Staking failed.');
- console.error('Staking failed', error);
+ setStatus("error");
+ setMessage(error.message || "Staking failed.");
+ console.error("Staking failed", error);
} finally {
setLoading(false);
onOpen();
@@ -191,17 +149,17 @@ function UserAssets() {
setLoading(true);
try {
if (parseFloat(stakeAmount) > parseFloat(stakedBalance)) {
- throw new Error('Unstake amount exceeds staked balance');
+ throw new Error("Unstake amount exceeds staked balance");
}
const tx = await XeonStakingPool.unstake(
ethers.utils.parseEther(stakeAmount)
);
await tx.wait();
- setMessage('Unstake successful');
+ setMessage("Unstake successful");
} catch (error) {
- console.error('Unstaking failed', error);
- setMessage(error.message || 'Unstaking failed');
+ console.error("Unstaking failed", error);
+ setMessage(error.message || "Unstaking failed");
} finally {
setLoading(false);
onOpen();
@@ -220,138 +178,222 @@ function UserAssets() {
const handleStakeAmountChange = (e) => {
const value = e.target.value;
- if (isSwitched && parseFloat(value) > parseFloat(stakedBalance)) {
- alert('Unstake amount exceeds your staked balance');
- } else if (!isSwitched && parseFloat(value) > parseFloat(walletBalance)) {
- alert('Amount exceeds wallet balance');
+ setStakeAmount(value);
+ };
+ // handle increment and decrement of vote value
+ // todo: for mainnet, ensure vote value is clamped to contract min/max
+ const handleIncrement = () => {
+ setVoteValue((prevValue) => Math.min(prevValue + 1, 100));
+ };
+
+ const handleDecrement = () => {
+ setVoteValue((prevValue) => Math.max(prevValue - 1, 1));
+ };
+
+ const handleVote = async () => {
+ if (!XeonStakingPool || voteValue < 1 || voteValue > 100) {
+ setMessage("Please enter a value between 1 and 100");
+ return;
+ }
+
+ setLoading(true);
+ onOpen();
+
+ try {
+ const tx = await XeonStakingPool.voteForBuybackPercentage(voteValue);
+ await tx.wait();
+ setLoading(false);
+ setMessage(`Vote successful for ${voteValue}% buyback`);
+ } catch (error) {
+ console.error("Vote failed", error);
+ setLoading(false);
+ setMessage("Vote failed, please try again.");
+ }
+ };
+
+ // handle vote value change
+ const handleVoteChange = (e) => {
+ const value = parseInt(e.target.value);
+ if (Number.isNaN(value)) {
+ setVoteValue(1);
} else {
- setStakeAmount(value);
+ setVoteValue(Math.min(Math.max(value, 1), 100));
}
};
return (
-
-
-
-
-
- {isSwitched ? 'Unstaking mode' : 'Staking mode'}
-
-
-
- {isSwitched ? 'Unstake Tokens' : 'Stake Tokens'}
+
+
+
+ {isSwitched ? "Unstake" : "Stake"}
- {isSwitched ? (
-
-
-
-
- {loading ? : 'Unstake'}
-
-
-
- ) : (
-
-
-
-
-
-
- {loading ? : buttonText}
-
-
-
-
- )}
-
-
-
-
-
-
-
-
- {wallet?.address.slice(0, 6) +
- '...' +
- wallet?.address.slice(-4)}
-
-
-
-
-
-
-
-
-
Staked
-
-
$XEON {stakedBalance}
+
+ {isSwitched ? (
+
+
+
+
+
+ Staked: {stakedBalance} $XEON
+
+
+
+
+ Wallet: {walletBalance} $XEON
+
+
+
+
+
+
+
+ {loading ? : "Unstake"}
+
+
+
+
+
-
-
-
Wallet
-
-
$XEON {walletBalance}
+
+ ) : (
+
+
+
+
+
+ Staked: {stakedBalance} $XEON
+
+
+
+
+ Wallet: {walletBalance} $XEON
+
+
+
+
+
+
+
+ {loading ? : buttonText}
+
+
+
+
+
+
+ )}
+
+
+
+
+
+
Settle
+
+
+ Close expired positions and collect fees into the staking pool
+
+
+
+ Settle
+
+
+
$XEON Buyback
+
+
+ What percentage of protocol revenue should be used to buyback $XEON
+ token?
+
+
+
+ -
+
+
+
+ +
+
+
+ Vote
+
+
+
+ Current Buyback Percentage: {currentPercentage}%
+
+
+
-
-
- {status === 'success' ? 'Success' : 'Error'}
+
+
+ {status === "success" ? "Success" : "Error"}
-
+
{loading ? (
) : (