diff --git a/src/App.css b/src/App.css index 11169d4..78b7344 100644 --- a/src/App.css +++ b/src/App.css @@ -605,7 +605,9 @@ button.btn.btn-success:not(:disabled) { } .btn.btn-primary:hover, -button.btn.btn-primary:hover { +button.btn.btn-primary:hover, +.btn.btn-primary.active, +button.btn.btn-primary.active { color: rgb(var(--btn-solid-color)); background: rgba(var(--primary-color), 1); box-shadow: var(--btn-solid-outline-hover), @@ -667,7 +669,9 @@ button.btn.btn-outline:not(:disabled) { } .btn.btn-outline:hover, -button.btn.btn-outline:hover { +button.btn.btn-outline:hover, +.btn.btn-outline.active, +button.btn.btn-outline.active { color: var(--alt-btn-txt-color); background: rgba(255,255,255, 0.1); box-shadow: var(--btn-outline-outline-hover), diff --git a/src/App.jsx b/src/App.jsx index fd891e8..3029833 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,7 +10,9 @@ import Vaults from './pages/vaults/Vaults'; import Vault from './pages/vault/Vault'; import VaultHistory from './pages/vault/VaultHistory'; import LiquidationPools from './pages/liquidation-pools/LiquidationPools'; -import StakingPool from './pages/staking-pool/StakingPool'; +import TstStaking from './pages/tst-staking/TstStaking'; +import StakingPool from './pages/staking-pool/StakingPoolLegacy'; +import LegacyPools from './pages/legacy-pools/LegacyPools'; import TermsOfUse from './pages/TermsOfUse'; import Dex from './pages/dex/Dex'; @@ -30,7 +32,9 @@ function App() { } /> } /> } /> - } /> + } /> + {/* } /> */} + {/* } /> */} } /> } /> } /> diff --git a/src/abis/stakingPoolV3.jsx b/src/abis/stakingPoolV3.jsx new file mode 100644 index 0000000..52029fd --- /dev/null +++ b/src/abis/stakingPoolV3.jsx @@ -0,0 +1,130 @@ +export const abi = [ + { + "inputs": [], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dailyYield", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct StakingTST.Reward[]", + "name": "_rewards", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_tst", + "type": "uint256" + } + ], + "name": "decreaseStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_tst", + "type": "uint256" + } + ], + "name": "increaseStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "TST", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_holder", + "type": "address" + } + ], + "name": "projectedEarnings", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct StakingTST.Reward[]", + "name": "_rewards", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "start", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] + +export default abi; \ No newline at end of file diff --git a/src/components/staking-pool/ClaimingRewardsModal.jsx b/src/components/staking-pool/ClaimingRewardsModal.jsx index ad9d7c4..ab85edf 100644 --- a/src/components/staking-pool/ClaimingRewardsModal.jsx +++ b/src/components/staking-pool/ClaimingRewardsModal.jsx @@ -148,7 +148,7 @@ const ClaimingRewardsModal = ({ Claiming your rewards will end your current staking period and restart a new one. - + {/* By opting to compound your EUROs rewards, those EUROs will be added to the EUROs in your new stake.
@@ -157,7 +157,7 @@ const ClaimingRewardsModal = ({ onChange={() => setCompound(!compound)} label="I would like to compound my EUROs rewards" /> -
+ */} + + + )} + + + + ) + } + + return ( + <> + { + handleCloseModal(); + }} + > + <> + {claimLoading ? ( + <> + + Claiming Your Rewards + + + + ) : ( + <> +
+ + Claim Your Rewards + + + Claiming your rewards will end your current staking period and restart a new one. + +
+ + + + )} + +
+ + ) +}; + +export default ClaimingRewardsModal; \ No newline at end of file diff --git a/src/components/tst-staking/StakingAssets.jsx b/src/components/tst-staking/StakingAssets.jsx new file mode 100644 index 0000000..0f52f86 --- /dev/null +++ b/src/components/tst-staking/StakingAssets.jsx @@ -0,0 +1,107 @@ +import { useState } from "react"; + +import { ethers } from "ethers"; + +import Button from "../ui/Button"; +import Card from "../ui/Card"; +import Typography from "../ui/Typography"; +import CenterLoader from "../ui/CenterLoader"; + +import StakingDecreaseModal from "./StakingDecreaseModal"; + +const StakingAssets = ({ + positions, +}) => { + const [open, setOpen] = useState(false); + + if (!positions) { + return ( + +
+ +
+
+ ) + } + + const tstAmount = positions[1] || 0; + + const useRows = [ + { + asset: 'TST', + amount: tstAmount || 0 + }, + ] + + const handleCloseModal = () => { + setOpen(false) + }; + + const rows = useRows || []; + + let noStaked = true; + if (rows.some(e => e.amount > 0)) { + noStaked = false; + } + + return ( + +
+ + Staked Assets + + +
+ + + + + + + + {!positions ? (null) : ( + + {rows.map(function(asset, index) { + const amount = asset?.amount; + const decimals = asset?.dec; + const symbol = asset?.asset; + + return( + + + + + )} + )} + + )} +
AssetAmount
+ {symbol} + + {ethers.formatUnits(amount, decimals)} +
+ {!positions ? ( + + ) : (null)} +
+ +
+ +
+ +
+
+ ) +}; + +export default StakingAssets; diff --git a/src/components/tst-staking/StakingDecreaseModal.jsx b/src/components/tst-staking/StakingDecreaseModal.jsx new file mode 100644 index 0000000..24769df --- /dev/null +++ b/src/components/tst-staking/StakingDecreaseModal.jsx @@ -0,0 +1,242 @@ +import { useState, useEffect, useRef } from "react"; +import { toast } from 'react-toastify'; + +import { + useWriteContract, + useChainId, +} from "wagmi"; +import { arbitrumSepolia } from "wagmi/chains"; +import { formatEther, parseEther } from "viem"; + +import { + useStakingPoolv3AddressStore, + useStakingPoolv3AbiStore +} from "../../store/Store"; + +import Button from "../ui/Button"; +import Modal from "../ui/Modal"; +import Typography from "../ui/Typography"; +import Input from "../ui/Input"; +import CenterLoader from "../ui/CenterLoader"; + +const StakingDecreaseModal = ({ + stakedPositions, + isOpen, + handleCloseModal, +}) => { + const { + arbitrumSepoliaStakingPoolv3Address, + arbitrumStakingPoolv3Address, + } = useStakingPoolv3AddressStore(); + const { stakingPoolv3Abi } = useStakingPoolv3AbiStore(); + const [claimLoading, setClaimLoading] = useState(false); + const [showError, setShowError] = useState(false); + const [tstWithdrawAmount, setTstWithdrawAmount] = useState(0); + const chainId = useChainId(); + + const tstInputRef = useRef(null); + + const tstPosition = stakedPositions?.find((item) => item.asset === 'TST'); + + const tstStakedAmount = tstPosition?.amount; + + const useTstStakedAmount = formatEther(tstStakedAmount.toString()); + + const stakingPoolv3Address = chainId === arbitrumSepolia.id ? arbitrumSepoliaStakingPoolv3Address : + arbitrumStakingPoolv3Address; + + const { writeContract, isError, isPending, isSuccess, error } = useWriteContract(); + + const handleApproveWithdraw = async () => { + try { + writeContract({ + abi: stakingPoolv3Abi, + address: stakingPoolv3Address, + functionName: "decreaseStake", + args: [ + tstWithdrawAmount, + ], + }); + } catch (error) { + let errorMessage = ''; + if (error && error.shortMessage) { + errorMessage = error.shortMessage; + } + toast.error(errorMessage || 'There was a problem'); + } + }; + + useEffect(() => { + if (isPending) { + setClaimLoading(true); + } else if (isSuccess) { + toast.success('Success!'); + setClaimLoading(false); + setTstWithdrawAmount(0); + handleCloseModal(); + } else if (isError) { + setShowError(true) + setClaimLoading(false); + setTstWithdrawAmount(0); + } + }, [ + isPending, + isSuccess, + isError, + error, + ]); + + const handleTstAmount = (e) => { + if (Number(e.target.value) < 10n ** 21n) { + setTstWithdrawAmount(parseEther(e.target.value.toString())); + } + }; + + const handleTstInputMax = () => { + const formatBalance = formatEther(tstStakedAmount); + tstInputRef.current.value = formatBalance; + handleTstAmount({target: {value: formatBalance}}); + } + + if (showError) { + return ( + <> + { + setShowError(false); + handleCloseModal(); + }} + > +
+ {claimLoading ? ( + <> + + Withdrawing Your Tokens + + + + ) : ( + <> +
+ + Withdraw Unsuccessful + + + There was a problem processing your withdraw request. + + + It is possible that your withdraw request exceeds the amount of tokens you have staked. + +
+ +
+ + +
+ + )} +
+
+ + ) + } + + return ( + <> + { + handleCloseModal(); + }} + > + <> + {claimLoading ? ( + <> + + Withdrawing & Claiming Your Rewards + + + + ) : ( + <> +
+ + Withdraw Your Staked TST + + + Here you can reduce your position by withdrawing your TST. + + + Any withdrawals will automatically claim your existing rewards, ending your current staking period and restarting a new one. + +
+ +
+ + TST Withdraw Amount + + + Available: {useTstStakedAmount || '0'} + +
+ +
+ + +
+ + + + )} + +
+ + ) +}; + +export default StakingDecreaseModal; diff --git a/src/components/tst-staking/StakingIncrease.jsx b/src/components/tst-staking/StakingIncrease.jsx new file mode 100644 index 0000000..e4684db --- /dev/null +++ b/src/components/tst-staking/StakingIncrease.jsx @@ -0,0 +1,252 @@ +import { useRef, useState, useEffect } from "react"; +import { toast } from 'react-toastify'; + +import { + useAccount, + useReadContracts, + useWriteContract, + useChainId, + useWatchBlockNumber, +} from "wagmi"; +import { arbitrumSepolia } from "wagmi/chains"; +import { ethers } from "ethers"; + +import { + useTstAddressStore, + useErc20AbiStore, + usesEuroAddressStore, + useStakingPoolv3AbiStore, + useStakingPoolv3AddressStore, +} from "../../store/Store.jsx"; + +import Card from "../ui/Card"; +import Typography from "../ui/Typography"; +import Button from "../ui/Button"; +import Input from "../ui/Input"; + +const StakingIncrease = () => { + const chainId = useChainId(); + const { + arbitrumTstAddress, + arbitrumSepoliaTstAddress, + } = useTstAddressStore(); + const { + arbitrumsEuroAddress, + arbitrumSepoliasEuroAddress, + } = usesEuroAddressStore(); + const { + arbitrumSepoliaStakingPoolv3Address, + arbitrumStakingPoolv3Address, + } = useStakingPoolv3AddressStore(); + const { address } = useAccount(); + const { erc20Abi } = useErc20AbiStore(); + const { stakingPoolv3Abi } = useStakingPoolv3AbiStore(); + const [tstStakeAmount, setTstStakeAmount] = useState(0); + const [stage, setStage] = useState(''); + const [helpOpen, setHelpOpen] = useState(false); + + const tstInputRef = useRef(null); + + const tstAddress = chainId === arbitrumSepolia.id ? + arbitrumSepoliaTstAddress : + arbitrumTstAddress; + + const stakingPoolv3Address = chainId === arbitrumSepolia.id ? arbitrumSepoliaStakingPoolv3Address : + arbitrumStakingPoolv3Address; + + const tstContract = { + address: tstAddress, + abi: erc20Abi, + } + + const { data: tstData, refetch: refetchTst } = useReadContracts({ + contracts: [{ + ... tstContract, + functionName: "allowance", + args: [address, stakingPoolv3Address] + },{ + ... tstContract, + functionName: "balanceOf", + args: [address] + }], + }); + + useWatchBlockNumber({ + onBlockNumber() { + refetchTst(); + }, + }) + + const existingTstAllowance = tstData && tstData[0].result; + const tstBalance = tstData && tstData[1].result; + + const { writeContract, isError, isPending, isSuccess, error } = useWriteContract(); + + const handleApproveTst = async () => { + setStage('APPROVE_TST'); + try { + writeContract({ + abi: erc20Abi, + address: tstAddress, + functionName: "approve", + args: [stakingPoolv3Address, tstStakeAmount], + }); + } catch (error) { + let errorMessage = ''; + if (error && error.shortMessage) { + errorMessage = error.shortMessage; + } + toast.error(errorMessage || 'There was a problem'); + } + }; + + const handleDepositToken = async () => { + setStage('DEPOSIT_TOKEN'); + setTimeout(() => { + try { + writeContract({ + abi: stakingPoolv3Abi, + address: stakingPoolv3Address, + functionName: "increaseStake", + args: [ + tstStakeAmount, + ], + }); + } catch (error) { + let errorMessage = ''; + if (error && error.shortMessage) { + errorMessage = error.shortMessage; + } + toast.error(errorMessage || 'There was a problem'); + } + }, 1000); + }; + + const handleLetsStake = async () => { + if (existingTstAllowance < tstStakeAmount) { + handleApproveTst(); + } else { + handleDepositToken(); + } + }; + + useEffect(() => { + if (stage === 'APPROVE_TST') { + if (isPending) { + // + } else if (isSuccess) { + setStage(''); + toast.success('TST Approved'); + handleDepositToken(); + } else if (isError) { + setStage(''); + toast.error('There was a problem'); + } + } + if (stage === 'DEPOSIT_TOKEN') { + if (isPending) { + // + } else if (isSuccess) { + setStage(''); + toast.success('Deposited Successfully!'); + tstInputRef.current.value = ""; + setTstStakeAmount(0); + } else if (isError) { + setStage(''); + toast.error('There was a problem'); + tstInputRef.current.value = ""; + setTstStakeAmount(0); + } + } + }, [ + isPending, + isSuccess, + isError, + error + ]); + + const handleTstAmount = (e) => { + if (Number(e.target.value) < 10n ** 21n) { + setTstStakeAmount(ethers.parseEther(e.target.value.toString())); + } + }; + + const handleTstInputMax = () => { + const formatBalance = ethers.formatEther(tstBalance); + tstInputRef.current.value = formatBalance; + handleTstAmount({target: {value: formatBalance}}); + } + + let maxTst = 0; + + if (tstBalance) { + maxTst = ethers.formatEther(tstBalance.toString()); + } + + return ( + <> + +
+ + Deposit + + + Increase your TST position to earn USDs & more rewards. + + + Depositing will automatically claim your existing rewards, ending your current staking period and restarting a new one. + +
+ {/* + TST Deposit Amount + */} +
+ + TST Deposit Amount + + + Available: {maxTst || '0'} + +
+
+ + +
+
+ +
+
+
+
+ + ); +}; + +export default StakingIncrease; \ No newline at end of file diff --git a/src/components/tst-staking/StakingRewards.jsx b/src/components/tst-staking/StakingRewards.jsx new file mode 100644 index 0000000..db0e00f --- /dev/null +++ b/src/components/tst-staking/StakingRewards.jsx @@ -0,0 +1,180 @@ +import { useState } from "react"; +import { ethers } from "ethers"; +import { + useReadContracts, +} from "wagmi"; + +import { + useErc20AbiStore, +} from "../../store/Store"; + +import Button from "../ui/Button"; +import Card from "../ui/Card"; +import Typography from "../ui/Typography"; +import CenterLoader from "../ui/CenterLoader"; + +import ClaimingRewardsModal from "./ClaimingRewardsModal"; + +const StakingRewards = ({ + stakedSince, + collaterals, + poolRewardsLoading, + collatDaily, +}) => { + const [open, setOpen] = useState(false); + const { erc20Abi } = useErc20AbiStore(); + + if (poolRewardsLoading) { + return ( + +
+ +
+
+ ) + } + + const { data: rewardDecimals } = useReadContracts({ + contracts:collaterals.map((item) =>({ + address: item.token, + abi: erc20Abi, + functionName: "decimals", + args: [], + })) + }) + + const { data: rewardSymbols } = useReadContracts({ + contracts:collaterals.map((item) =>({ + address: item.token, + abi: erc20Abi, + functionName: "symbol", + args: [], + })) + }) + + const rewardData = collaterals?.map((item, index) => { + const amount = item.amount; + let decimals = 18; + if (rewardDecimals) { + if (rewardDecimals[index]) { + if (rewardDecimals[index].result) { + decimals = rewardDecimals[index].result; + } + } + } + let symbol = 'ETH'; + if (rewardSymbols) { + if (rewardSymbols[index]) { + if (rewardSymbols[index].result) { + symbol = rewardSymbols[index].result; + } + } + } + let useDailyReward = 0n; + + const rewardItem = collatDaily?.find((reward) => reward.token === item.token); + + if (rewardItem && rewardItem.amount) { + useDailyReward = rewardItem.amount; + } + + return { + key: index, + asset: symbol, + amount: amount, + decimals: decimals, + dailyReward: useDailyReward, + } + }); + + const handleCloseModal = () => { + setOpen(false) + }; + + const rows = rewardData || []; + + let noRewards = true; + if (rows.some(e => e.amount > 0)) { + noRewards = false; + } + + return ( + +
+ + Projected Rewards + +
+ + You can earn rewards every 24 hours after your staking period begins. + + + Your reward rates are based on a the number of tokens you have staked. + + {stakedSince ? ( + + Staked Since: + {stakedSince} + + ) : null} +
+ +
+ + + + + + + + + {poolRewardsLoading ? (null) : ( + + {rows.map(function(asset, index) { + const amount = asset?.amount || 0n; + const decimals = asset?.decimals; + const symbol = asset?.asset; + const dailyReward = asset?.dailyReward || 0n; + + return( + + + + + + )} + )} + + )} +
AssetAmountDaily Reward Per Token
+ {symbol} + + {ethers.formatUnits(amount, decimals)} + + {ethers.formatUnits(dailyReward, decimals)} + / TST +
+ {poolRewardsLoading ? ( + + ) : (null)} +
+ +
+ +
+ +
+
+ ) +}; + +export default StakingRewards; diff --git a/src/components/ui/Modal.jsx b/src/components/ui/Modal.jsx index a43a6de..0899e7e 100644 --- a/src/components/ui/Modal.jsx +++ b/src/components/ui/Modal.jsx @@ -34,7 +34,7 @@ const Modal = (props) => { }} >
diff --git a/src/components/ui/SideNav.jsx b/src/components/ui/SideNav.jsx index 6151933..9041c89 100644 --- a/src/components/ui/SideNav.jsx +++ b/src/components/ui/SideNav.jsx @@ -8,7 +8,8 @@ import { BanknotesIcon, XMarkIcon, Square3Stack3DIcon, - ArrowPathRoundedSquareIcon + ArrowPathRoundedSquareIcon, + ArchiveBoxIcon } from '@heroicons/react/24/outline'; import { @@ -81,32 +82,32 @@ const SideNav = (props) => { isActive || - location.pathname.includes('/liquidation-pools') ? + location.pathname.includes('/dex') ? 'navbar-item active' : 'navbar-item' } > - - - Liquidation Pools - + + Cross-Chain Dex - + {/* isActive || - location.pathname.includes('/dex') ? + location.pathname.includes('/legacy-pools') ? 'navbar-item active' : 'navbar-item' } > - - Cross-Chain Dex + + + Legacy Pools + - + */}
{/* Med + */}
@@ -153,44 +154,44 @@ const SideNav = (props) => { isActive || - location.pathname.includes('/liquidation-pools') ? + location.pathname.includes('/dex') ? 'navbar-item active' : 'navbar-item' } > - + - Liquidation Pools + Cross-Chain Dex - isActive || - location.pathname.includes('/dex') ? + location.pathname.includes('/legacy-pools') ? 'navbar-item active' : 'navbar-item' } > - + - Cross-Chain Dex + Legacy Pools - + */}
diff --git a/src/components/vault/yield/YieldList.jsx b/src/components/vault/yield/YieldList.jsx index bb42dce..f9e154b 100644 --- a/src/components/vault/yield/YieldList.jsx +++ b/src/components/vault/yield/YieldList.jsx @@ -11,6 +11,7 @@ import { import YieldClaimModal from "./YieldClaimModal"; import YieldViewModal from "./YieldViewModal"; +import YieldPerformanceModal from "./YieldPerformanceModal"; import { ArbitrumVaults, SepoliaVaults, @@ -194,7 +195,7 @@ const YieldList = (props) => { yieldHypervisor={yieldHypervisor || ''} gammaUser={gammaUser} /> - handleCloseModal()} isOpen={open === 'VIEW'} openClaim={() => handleOpenModal(yieldPair, yieldQuantities, yieldHypervisor, 'CLAIM')} diff --git a/src/components/vault/yield/YieldParent.jsx b/src/components/vault/yield/YieldParent.jsx index daff187..10a5dd1 100644 --- a/src/components/vault/yield/YieldParent.jsx +++ b/src/components/vault/yield/YieldParent.jsx @@ -8,19 +8,15 @@ import axios from "axios"; import { AdjustmentsHorizontalIcon, - QuestionMarkCircleIcon } from '@heroicons/react/24/outline'; -import { - Tooltip, -} from 'react-daisyui'; - import { useVaultAddressStore, useSmartVaultABIStore, } from "../../../store/Store"; import YieldList from "./YieldList"; +import YieldSummary from "./YieldSummary"; import Card from "../../ui/Card"; import Typography from "../../ui/Typography"; @@ -168,6 +164,9 @@ const Vault = (props) => { const netMarketReturnsUSD = userHypervisor?.returns?.netMarketReturnsUSD; const hypervisorReturnsUSD = userHypervisor?.returns?.hypervisorReturnsUSD; + const hypervisorReturnsPercentage = userHypervisor?.returns?.hypervisorReturnsPercentage; + const netMarketReturnsPercentage = userHypervisor?.returns?.netMarketReturnsPercentage; + totalUSD.initialTokenUSD = totalUSD.initialTokenUSD + initialTokenUSD; totalUSD.currentUSD = totalUSD.currentUSD + currentUSD; totalUSD.netMarketReturnsUSD = totalUSD.netMarketReturnsUSD + netMarketReturnsUSD; @@ -177,6 +176,10 @@ const Vault = (props) => { hypervisor: useAddress, initialTokenUSD: initialTokenUSD, currentUSD: currentUSD, + netMarketReturnsUSD: netMarketReturnsUSD, + hypervisorReturnsUSD: hypervisorReturnsUSD, + hypervisorReturnsPercentage: hypervisorReturnsPercentage, + netMarketReturnsPercentage: netMarketReturnsPercentage, }); }); @@ -191,146 +194,31 @@ const Vault = (props) => { {yieldEnabled ? ( <> {yieldData && yieldData.length ? ( - -
- - {/* */} - -
- -
- - Total Yield Earned - - - - - - {gammaUserLoading ? ( - <> - - - ) : ( - <> - {userSummary?.totalUSD?.hypervisorReturnsUSD ? ( - <> - {userSummary?.totalUSD?.hypervisorReturnsUSD.toFixed(2) < 0 ? ( - '-$' - ) : ( - '$' - )} - { - Math.abs( - userSummary?.totalUSD?.hypervisorReturnsUSD - )?.toFixed(2) || '' - } - - ) : ( - '' - )} - - )} - -
-
- - Total Yield Earned (Market) - - - - - - {gammaUserLoading ? ( - <> - - - ) : ( - - {userSummary?.totalUSD?.netMarketReturnsUSD ? ( - <> - {userSummary?.totalUSD?.netMarketReturnsUSD.toFixed(2) < 0 ? ( - '-$' - ) : ( - '$' - )} - { - Math.abs( - userSummary?.totalUSD?.netMarketReturnsUSD - )?.toFixed(2) || '' - } - - ) : ( - '' - )} - - )} - -
-
- - Total Balance - - - - - - {gammaUserLoading ? ( - <> - - - ) : ( - <> - ${userSummary?.totalUSD?.currentUSD?.toFixed(2) || ''} - - )} - -
- + <> + +
+
-
- + + +
+ +
+
+ ) : (
diff --git a/src/components/vault/yield/YieldPerformanceModal.jsx b/src/components/vault/yield/YieldPerformanceModal.jsx new file mode 100644 index 0000000..0d6c71f --- /dev/null +++ b/src/components/vault/yield/YieldPerformanceModal.jsx @@ -0,0 +1,268 @@ +import Button from "../../ui/Button"; +import Modal from "../../ui/Modal"; +import Typography from "../../ui/Typography"; +import TokenIcon from "../../ui/TokenIcon"; + +const formatUSD = (value) => { + if (value) { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(Math.abs(value)); + } +}; + +const formatPercentage = (value) => { + if (value) { + const numValue = parseFloat(value.replace('%', '')); + return `${numValue >= 0 ? '+' : ''}${numValue.toFixed(2)}%`; + } +}; + +const YieldPerformanceModal = ({ + isOpen, + handleCloseModal, + yieldPair, + yieldQuantities, + yieldHypervisor, + gammaUser, + gammaReturns, + gammaStats, + openClaim, + gammaUserLoading, + gammaReturnsLoading, + gammaStatsLoading, +}) => { + + const positionUser = gammaUser?.[yieldHypervisor.toLowerCase()] || {}; + const positionStats = gammaStats?.find(item => item.hypervisor.toLowerCase() === yieldHypervisor.toLowerCase()); + const positionReturns = gammaReturns?.find((item) => item.hypervisor.toLowerCase() === yieldHypervisor.toLowerCase()); + + const initialUSD = positionUser?.returns?.initialTokenUSD || 0; + const initialTokenCurrentUSD = positionUser?.returns?.initialTokenCurrentUSD || 0; + const currentUSD = positionUser?.returns?.currentUSD || 0; + const hypervisorReturnsUSD = positionUser?.returns?.hypervisorReturnsUSD; + const hypervisorReturnsPercentage = positionUser?.returns?.hypervisorReturnsPercentage; + const netMarketReturnsUSD = positionUser?.returns?.netMarketReturnsUSD; + const netMarketReturnsPercentage = positionUser?.returns?.netMarketReturnsPercentage; + const tvlUSD = Number(positionStats?.tvlUSD) || 0; + + let showApy = '0'; + if (positionReturns?.feeApy) { + showApy = Number(positionReturns?.feeApy * 100).toFixed(2); + } + + const getMarketContext = () => { + const marketReturn = parseFloat(netMarketReturnsPercentage); + if (marketReturn > 0) { + return { + description: `Market movement has generated ${formatUSD(netMarketReturnsUSD)}`, + colorClass: "text-green-600", + message: `(+${Math.abs(marketReturn).toFixed(2)}%) in gains from holding these assets` + }; + } else { + return { + description: `Market movement would have resulted in a ${formatUSD(netMarketReturnsUSD)} loss`, + colorClass: "text-red-600", + message: `(${marketReturn.toFixed(2)}%) from simply holding these assets` + }; + } + }; + + const getStrategyContext = () => { + const strategyReturn = parseFloat(hypervisorReturnsPercentage); + if (strategyReturn > 0.5) { + return { + title: "Yield Pool Performance", + colorClass: "text-green-600", + description: `Generated ${formatUSD(hypervisorReturnsUSD)} in additional value`, + message: `(+${strategyReturn.toFixed(2)}%) through TheStandard's advanced yield strategies` + }; + } else if (strategyReturn > 0) { + return { + title: "Yield Pool Performance", + colorClass: "text-green-600", + description: `Earned ${formatUSD(hypervisorReturnsUSD)} in trading fees`, + message: `(+${strategyReturn.toFixed(2)}%) through automated yield generation` + }; + } else { + return { + title: "Yield Pool Performance", + colorClass: "text-amber-600", + description: `Currently ${formatUSD(hypervisorReturnsUSD)} below market returns`, + message: `(${strategyReturn.toFixed(2)}%) while building trading fee reserves` + }; + } + }; + const getAnalysisMessage = () => { + const marketReturn = parseFloat(netMarketReturnsPercentage); + const strategyReturn = parseFloat(hypervisorReturnsPercentage); + + if (marketReturn > 0 && strategyReturn > 0) { + return `Your position has gained ${formatUSD(netMarketReturnsUSD)} (${formatPercentage(netMarketReturnsPercentage)}) from market movement, plus an additional ${formatUSD(hypervisorReturnsUSD)} (${formatPercentage(hypervisorReturnsPercentage)}) through TheStandard's yield generation strategy.`; + } else if (marketReturn > 0 && strategyReturn <= 0) { + return `While the market has moved up ${formatUSD(netMarketReturnsUSD)} (${formatPercentage(netMarketReturnsPercentage)}), your position is temporarily ${formatUSD(hypervisorReturnsUSD)} (${formatPercentage(hypervisorReturnsPercentage)}) below holding returns while accumulating trading fees in TheStandard's yield pools.`; + } else if (marketReturn <= 0 && strategyReturn > 0) { + return `Despite a market decline of ${formatUSD(netMarketReturnsUSD)} (${formatPercentage(netMarketReturnsPercentage)}), TheStandard's yield strategy has generated ${formatUSD(hypervisorReturnsUSD)} (${formatPercentage(hypervisorReturnsPercentage)}) in additional returns through effective fee collection.`; + } else { + return `While the market decline of ${formatUSD(netMarketReturnsUSD)} (${formatPercentage(netMarketReturnsPercentage)}) has impacted overall value, TheStandard's yield pools have limited additional impact to just ${formatUSD(hypervisorReturnsUSD)} (${formatPercentage(hypervisorReturnsPercentage)}) through active fee generation.`; + } + }; + const marketContext = getMarketContext(); + const strategyContext = getStrategyContext(); + + return ( + <> + { + handleCloseModal(); + }} + closeModal={() => { + handleCloseModal(); + }} + wide={false} + > + <> +
+ +
+ + +
+ + {yieldPair[0] || ''}/{yieldPair[1] || ''} Yield Pool +
+
+
+ +
+
+ + TVL + + {gammaStatsLoading ? ( + <> + + + ) : ( + <> + + ${tvlUSD?.toFixed(2) || ''} + + + )} +
+
+ + APY + + {gammaReturnsLoading ? ( + <> + + + ) : ( + <> + + {showApy || ''}% + + + )} +
+
+ + Initial Deposit + + + ${initialUSD?.toFixed(2) || ''} + +
+
+ + Current Value + + + ${currentUSD?.toFixed(2) || ''} + +
+
+ +
+
+
+ + Market Impact + + + {formatPercentage(netMarketReturnsPercentage)} + +
+ + {marketContext.description}{' '} + + {marketContext.message} + + +
+
+
+ + {strategyContext.title} + + + {formatPercentage(hypervisorReturnsPercentage)} + +
+ + {strategyContext.description}{' '} + + {strategyContext.message} + + +
+
+
+
+ 💡 +
+
+ Position Analysis: + {getAnalysisMessage()} +
+
+
+
+ +
+ +
+ + +
+ +
+ + ) +}; + +export default YieldPerformanceModal; \ No newline at end of file diff --git a/src/components/vault/yield/YieldSummary.jsx b/src/components/vault/yield/YieldSummary.jsx new file mode 100644 index 0000000..19fbd92 --- /dev/null +++ b/src/components/vault/yield/YieldSummary.jsx @@ -0,0 +1,245 @@ +import Typography from "../../ui/Typography"; + +import { + PresentationChartLineIcon +} from '@heroicons/react/24/outline'; + +const formatUSD = (value) => { + if (value) { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(Math.abs(value)); + } +}; + +const formatPercentage = (value) => { + if (value) { + return `${value >= 0 ? '+' : ''}${value.toFixed(2)}%`; + } +}; + +const YieldSummary = ({ + gammaUserLoading, + userSummary +}) => { + + + let positions = []; + + if (userSummary && userSummary.hypervisors && userSummary.hypervisors.length) { + positions = userSummary.hypervisors; + } + + const calculateMetrics = (positions) => { + let totalBalance = 0; + let totalYieldEarned = 0; + let totalMarketYield = 0; + let weightedYieldSum = 0; + let weightedMarketYieldSum = 0; + + // First pass - calculate totals + positions.forEach(position => { + totalBalance += position.currentUSD; + totalYieldEarned += position.hypervisorReturnsUSD; + totalMarketYield += position.netMarketReturnsUSD; + }); + + // Second pass - calculate weighted percentages + positions.forEach(position => { + const weight = position.currentUSD / totalBalance; + weightedYieldSum += (parseFloat(position.hypervisorReturnsPercentage) * weight); + weightedMarketYieldSum += (parseFloat(position.netMarketReturnsPercentage) * weight); + }); + + return { + totalBalance, + totalYieldEarned, + totalMarketYield, + weightedAverageYieldAPY: weightedYieldSum, + weightedAverageMarketAPY: weightedMarketYieldSum + }; + }; + + const getPerformanceMessage = (yieldEarned, marketYield) => { + if (yieldEarned > 0 && yieldEarned > marketYield) { + return "Your assets are earning additional yield through TheStandard's smart pools! 🎉"; + } else if (yieldEarned > 0) { + return "Steadily earning yields while protecting your assets 📈"; + } else if (yieldEarned > marketYield) { + return "TheStandard's yield pools are helping protect your assets during market movement"; + } else { + return "Building trading fees to optimize your returns 🔄"; + } + }; + + const getAnalysisMessage = (metrics) => { + const { + totalYieldEarned, + totalMarketYield, + weightedAverageYieldAPY, + weightedAverageMarketAPY, + totalBalance + } = metrics; + + // Market is up, strategy outperforming + if (totalMarketYield > 0 && totalYieldEarned > totalMarketYield) { + return ( + + Your {formatUSD(totalBalance)} position has gained {formatUSD(totalMarketYield)} ({formatPercentage(weightedAverageMarketAPY)}) from market movement, plus an additional {formatUSD(totalYieldEarned - totalMarketYield)} through TheStandard's yield generation, bringing your total gains to {formatUSD(totalYieldEarned)} ({formatPercentage(weightedAverageYieldAPY)}). + + ); + } + + // Market is up, strategy underperforming but positive + if (totalMarketYield > 0 && totalYieldEarned > 0) { + return ( + + While the market has moved up {formatUSD(totalMarketYield)} ({formatPercentage(weightedAverageMarketAPY)}), your position has generated {formatUSD(totalYieldEarned)} ({formatPercentage(weightedAverageYieldAPY)}) in yields as TheStandard's pools continue accumulating trading fees. This difference typically balances out over time as more fees are collected. + + ); + } + + // Market is down, strategy positive + if (totalMarketYield < 0 && totalYieldEarned > 0) { + return ( + + Despite a market decline that would have resulted in a {formatUSD(totalMarketYield)} loss ({formatPercentage(weightedAverageMarketAPY)}), TheStandard's yield strategy has generated {formatUSD(totalYieldEarned)} ({formatPercentage(weightedAverageYieldAPY)}) in positive returns through effective fee collection, helping protect your {formatUSD(totalBalance)} position. + + ); + } + + // Market is down, strategy down but outperforming + if (totalMarketYield < 0 && totalYieldEarned > totalMarketYield) { + return ( + + During this market downturn that would have resulted in a {formatUSD(totalMarketYield)} loss ({formatPercentage(weightedAverageMarketAPY)}), TheStandard's yield pools have significantly reduced the impact to just {formatUSD(totalYieldEarned)} ({formatPercentage(weightedAverageYieldAPY)}) through active fee generation, protecting your {formatUSD(totalBalance)} position from {formatUSD(totalMarketYield - totalYieldEarned)} in additional losses. + + ); + } + + // Market is neutral/down, strategy building + return ( + + Your {formatUSD(totalBalance)} position is actively accumulating trading fees in TheStandard's yield pools. While the market impact is {formatUSD(totalMarketYield)} ({formatPercentage(weightedAverageMarketAPY)}), the strategy is building reserves through trading fees, currently at {formatUSD(totalYieldEarned)} ({formatPercentage(weightedAverageYieldAPY)}). This approach typically shows its strength over longer holding periods as fees accumulate. + + ); + }; + + const getYieldColor = (value) => value > 0 ? 'text-green-500' : 'text-amber-500'; + + const metrics = calculateMetrics(positions); + + return ( + <> + +
+
+ + + Yield Pool Summary + + + {getPerformanceMessage(metrics.totalYieldEarned, metrics.totalMarketYield)} + +
+
+ + Assets in Yield Pools + + {gammaUserLoading ? ( + <> + + + ) : ( + <> + + {formatUSD(metrics.totalBalance)} + + + )} +
+
+ + Yield Generated + +
+ {gammaUserLoading ? ( + <> + + + ) : ( + <> + + {formatUSD(metrics.totalYieldEarned)} + + + {formatPercentage(metrics.weightedAverageYieldAPY)} APY + + + )} +
+
+
+
+ + Market Movement + + + If held without yield pools + +
+
+ {gammaUserLoading ? ( + <> + + + ) : ( + <> + = 0 ? 'text-green-500' : 'text-red-400'}`} + > + {formatUSD(metrics.totalMarketYield)} + + = 0 ? 'text-green-500' : 'text-red-400'}`} + > + {formatPercentage(metrics.weightedAverageMarketAPY)} + + + )} +
+
+
+ +
+
+
+
+ 💡 +
+
+ Position Analysis: + {getAnalysisMessage(metrics)} +
+
+
+
+ + + ) +}; + +export default YieldSummary; \ No newline at end of file diff --git a/src/pages/legacy-pools/LegacyPools.jsx b/src/pages/legacy-pools/LegacyPools.jsx new file mode 100644 index 0000000..128d413 --- /dev/null +++ b/src/pages/legacy-pools/LegacyPools.jsx @@ -0,0 +1,64 @@ +import { useState } from "react"; + +import LiquidationPools from '../liquidation-pools/LiquidationPools'; +import StakingPool from '../staking-pool/StakingPool'; + +import Card from "../../components/ui/Card"; +import Typography from "../../components/ui/Typography"; +import Button from "../../components/ui/Button"; + +const LegacyPools = (props) => { + const [ showPool, setShowPool ] = useState('STAKE-EUROSTST'); + + return ( +
+ + +
+ + Legacy Pools + + + + These pools have been depreciated and will no longer be generating new rewards. + + + + We recommend collecting any outstanding rewards and withdrawing your staked tokens. + + +
+ + +
+ + +
+
+ +
+ {showPool === 'STAKE-EUROSTST' ? ( + + ) : (null)} + {showPool === 'LIQUIDITY-POOL' ? ( + + ) : (null)} +
+ +
+ ); +}; + +export default LegacyPools; \ No newline at end of file diff --git a/src/pages/liquidation-pools/LiquidationPools.jsx b/src/pages/liquidation-pools/LiquidationPools.jsx index 8f3af1c..ccadfdd 100644 --- a/src/pages/liquidation-pools/LiquidationPools.jsx +++ b/src/pages/liquidation-pools/LiquidationPools.jsx @@ -5,12 +5,7 @@ import { useChainId, useWatchBlockNumber } from "wagmi"; -import axios from "axios"; import { arbitrumSepolia } from "wagmi/chains"; -import { - ArrowTrendingUpIcon, - BanknotesIcon, -} from '@heroicons/react/24/outline'; import { useLiquidationPoolAbiStore, @@ -19,11 +14,6 @@ import { import StakedAssets from "../../components/liquidation-pools/StakedAssets"; import ClaimTokens from "../../components/liquidation-pools/ClaimTokens"; -import VolumeChart from "../../components/liquidation-pools/VolumeChart"; -import ValueChart from "../../components/liquidation-pools/ValueChart"; -import Card from "../../components/ui/Card"; -import Typography from "../../components/ui/Typography"; -import Button from "../../components/ui/Button"; const LiquidationPools = () => { const [chartData, setChartData] = useState(undefined); @@ -60,37 +50,8 @@ const LiquidationPools = () => { const pending = liquidationPool && liquidationPool[1]; const rewards = liquidationPool && liquidationPool[2]; - const getChartData = async () => { - try { - const response = await axios.get( - `https://smart-vault-api.thestandard.io/liquidation_pools/${address}` - ); - setChartData(response?.data); - } catch (error) { - console.log(error); - } - }; - - useEffect(() => { - getChartData(); - }, []); - return ( <> - -
-
- - Changes With Earning Fees & New Staking Pool - - - With the recent release of our new & improved Staking Pool, from 17th June '24, we will be moving all earnable fees over to it. -
- The existing Liquidation Pool will continue to exist past this date so you can withdraw your staked assets and claim any outstanding rewards. -
-
-
-
{ rewards={rewards || []} />
-
- -
- - {showValue ? ( - 'Liquidation Pool Asset Value' - ) : ( - 'Liquidation Pool Asset Totals' - )} - - - {showValue ? ( - <> - - - ) : ( - <> - - - )} -
-
-
); diff --git a/src/pages/staking-pool/StakingPool.jsx b/src/pages/staking-pool/StakingPool.jsx index 09380d4..dbdbcc3 100644 --- a/src/pages/staking-pool/StakingPool.jsx +++ b/src/pages/staking-pool/StakingPool.jsx @@ -7,26 +7,17 @@ import { useWatchBlockNumber } from "wagmi"; import { arbitrumSepolia } from "wagmi/chains"; -import { - ArrowTrendingUpIcon, - BanknotesIcon, -} from '@heroicons/react/24/outline'; import { useStakingPoolv2AbiStore, useStakingPoolv2AddressStore, } from "../../store/Store"; -import StakingIncrease from "../../components/staking-pool/StakingIncrease"; import StakingAssets from "../../components/staking-pool/StakingAssets"; import StakingRewards from "../../components/staking-pool/StakingRewards"; -import VolumeChart from "../../components/staking-pool/VolumeChart"; -import ValueChart from "../../components/staking-pool/ValueChart"; import Card from "../../components/ui/Card"; import CenterLoader from "../../components/ui/CenterLoader"; -import Typography from "../../components/ui/Typography"; -import Button from "../../components/ui/Button"; const StakingPool = (props) => { const { stakingPoolv2Abi } = useStakingPoolv2AbiStore(); @@ -111,52 +102,12 @@ const StakingPool = (props) => { return (
-
- -
-
- -
- - {showValue ? ( - 'Asset Value' - ) : ( - 'Asset Totals' - )} - - - {showValue ? ( - <> - - - ) : ( - <> - - - )} -
-
-
- -
- {poolRewardsLoading ? ( + {poolRewardsLoading ? (
@@ -174,7 +125,6 @@ const StakingPool = (props) => { /> )}
-
); }; diff --git a/src/pages/staking-pool/StakingPoolLegacy.jsx b/src/pages/staking-pool/StakingPoolLegacy.jsx new file mode 100644 index 0000000..09380d4 --- /dev/null +++ b/src/pages/staking-pool/StakingPoolLegacy.jsx @@ -0,0 +1,182 @@ +import { useState } from "react"; +import moment from 'moment'; +import { + useReadContract, + useAccount, + useChainId, + useWatchBlockNumber +} from "wagmi"; +import { arbitrumSepolia } from "wagmi/chains"; +import { + ArrowTrendingUpIcon, + BanknotesIcon, +} from '@heroicons/react/24/outline'; + +import { + useStakingPoolv2AbiStore, + useStakingPoolv2AddressStore, +} from "../../store/Store"; + +import StakingIncrease from "../../components/staking-pool/StakingIncrease"; +import StakingAssets from "../../components/staking-pool/StakingAssets"; +import StakingRewards from "../../components/staking-pool/StakingRewards"; +import VolumeChart from "../../components/staking-pool/VolumeChart"; +import ValueChart from "../../components/staking-pool/ValueChart"; + +import Card from "../../components/ui/Card"; +import CenterLoader from "../../components/ui/CenterLoader"; +import Typography from "../../components/ui/Typography"; +import Button from "../../components/ui/Button"; + +const StakingPool = (props) => { + const { stakingPoolv2Abi } = useStakingPoolv2AbiStore(); + const [showValue, setShowValue] = useState(false); + + const { + arbitrumSepoliaStakingPoolv2Address, + arbitrumStakingPoolv2Address, + } = useStakingPoolv2AddressStore(); + + const { address } = useAccount(); + const chainId = useChainId(); + + const stakingPoolv2Address = + chainId === arbitrumSepolia.id + ? arbitrumSepoliaStakingPoolv2Address + : arbitrumStakingPoolv2Address; + + const { data: poolPositions, refetch: refetchPositions } = useReadContract({ + address: stakingPoolv2Address, + abi: stakingPoolv2Abi, + functionName: "positions", + args: [address], + }); + + const { data: poolRewards, isLoading: poolRewardsLoading, refetch: refetchRewards } = useReadContract({ + address: stakingPoolv2Address, + abi: stakingPoolv2Abi, + functionName: "projectedEarnings", + args: [address], + }); + + const { data: dailyYield, refetch: refetchDailyReward } = useReadContract({ + address: stakingPoolv2Address, + abi: stakingPoolv2Abi, + functionName: "dailyYield", + args: [], + }); + + useWatchBlockNumber({ + onBlockNumber() { + refetchPositions(); + refetchRewards(); + refetchDailyReward(); + }, + }) + + const positions = poolPositions; + const rewards = poolRewards; + const dailyRewards = dailyYield; + + + let sEuroAmount = 0n; + let collaterals = []; + + if (rewards && rewards[0]) { + sEuroAmount = rewards[0] || 0n; + } + if (rewards && rewards[1]) { + collaterals = rewards[1] || []; + } + + let sEuroDaily = 0n; + let collatDaily = []; + + if (dailyRewards && dailyRewards[0]) { + sEuroDaily = dailyRewards[0] || 0n; + } + if (dailyRewards && dailyRewards[1]) { + collatDaily = dailyRewards[1] || []; + } + + let stakedSince = 0; + if (positions && positions[0]) { + stakedSince = positions[0] || 0; + } + + let useStakedSince; + if (stakedSince) { + useStakedSince = moment.unix(Number(stakedSince)).format('Do MMM YYYY'); + } + + return ( +
+
+ +
+ +
+ +
+ +
+ +
+ + {showValue ? ( + 'Asset Value' + ) : ( + 'Asset Totals' + )} + + + {showValue ? ( + <> + + + ) : ( + <> + + + )} +
+
+
+ +
+ {poolRewardsLoading ? ( + +
+ +
+
+ ) : ( + + )} +
+ +
+ ); +}; + +export default StakingPool; \ No newline at end of file diff --git a/src/pages/tst-staking/TstStaking.jsx b/src/pages/tst-staking/TstStaking.jsx new file mode 100644 index 0000000..1185a38 --- /dev/null +++ b/src/pages/tst-staking/TstStaking.jsx @@ -0,0 +1,158 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import moment from 'moment'; +import { + useReadContract, + useAccount, + useChainId, + useWatchBlockNumber +} from "wagmi"; +import { arbitrumSepolia } from "wagmi/chains"; +import { + ArrowTrendingUpIcon, + BanknotesIcon, + Square3Stack3DIcon, +} from '@heroicons/react/24/outline'; + +import { + useStakingPoolv3AbiStore, + useStakingPoolv3AddressStore, +} from "../../store/Store"; + +import StakingIncrease from "../../components/tst-staking/StakingIncrease"; +import StakingAssets from "../../components/tst-staking/StakingAssets"; +import StakingRewards from "../../components/tst-staking/StakingRewards"; + +import Card from "../../components/ui/Card"; +import CenterLoader from "../../components/ui/CenterLoader"; +import Typography from "../../components/ui/Typography"; +import Button from "../../components/ui/Button"; + +const TstStaking = (props) => { + const { stakingPoolv3Abi } = useStakingPoolv3AbiStore(); + const [showValue, setShowValue] = useState(false); + const navigate = useNavigate(); + + const { + arbitrumSepoliaStakingPoolv3Address, + arbitrumStakingPoolv3Address, + } = useStakingPoolv3AddressStore(); + + const { address } = useAccount(); + const chainId = useChainId(); + + const stakingPoolv3Address = + chainId === arbitrumSepolia.id + ? arbitrumSepoliaStakingPoolv3Address + : arbitrumStakingPoolv3Address; + + const { data: poolPositions, refetch: refetchPositions } = useReadContract({ + address: stakingPoolv3Address, + abi: stakingPoolv3Abi, + functionName: "positions", + args: [address], + }); + + const { data: poolRewards, isLoading: poolRewardsLoading, refetch: refetchRewards } = useReadContract({ + address: stakingPoolv3Address, + abi: stakingPoolv3Abi, + functionName: "projectedEarnings", + args: [address], + }); + + const { data: dailyYield, refetch: refetchDailyReward } = useReadContract({ + address: stakingPoolv3Address, + abi: stakingPoolv3Abi, + functionName: "dailyYield", + args: [], + }); + + useWatchBlockNumber({ + onBlockNumber() { + refetchPositions(); + refetchRewards(); + refetchDailyReward(); + }, + }) + + const positions = poolPositions; + const rewards = poolRewards; + const dailyRewards = dailyYield; + + let collaterals = []; + + if (rewards) { + collaterals = rewards || []; + } + + let collatDaily = []; + + if (dailyRewards) { + collatDaily = dailyRewards || []; + } + + let stakedSince = 0; + if (positions && positions[0]) { + stakedSince = positions[0] || 0; + } + + let useStakedSince; + if (stakedSince) { + useStakedSince = moment.unix(Number(stakedSince)).format('Do MMM YYYY'); + } + + return ( + <> + +
+ + + Staking Pool + + + + Stake TST to earn USDs & more tokens daily. + + + + If you're looking for our previous staking pools, navigate("/legacy-pools")}>they can be found here. + + +
+
+ +
+ +
+ +
+ +
+
+ +
+ {poolRewardsLoading ? ( + +
+ +
+
+ ) : ( + + )} + +
+ +
+ + + ); +}; + +export default TstStaking; \ No newline at end of file diff --git a/src/store/Store.jsx b/src/store/Store.jsx index 2eb3585..f85d169 100644 --- a/src/store/Store.jsx +++ b/src/store/Store.jsx @@ -7,6 +7,7 @@ import smartVaultV4ABI from "../abis/smartVaultV4"; import stakingAbi from "../abis/staking"; import liquidationPoolAbi from "../abis/liquidationPool"; import stakingPoolv2Abi from "../abis/stakingPoolV2"; +import stakingPoolv3Abi from "../abis/stakingPoolV3"; export const useCurrentWagmiConfig = create( (set) => ({ @@ -103,6 +104,15 @@ export const useStakingPoolv2AddressStore = create()( }) ); +export const useStakingPoolv3AddressStore = create()( + (set) => ({ + arbitrumStakingPoolv3Address: "0xA27A9F6Bac7f3C530EAF324Ae45F33Bc113c1E83", + arbitrumSepoliaStakingPoolv3Address: "0x9bfEADec553110AbB9e2fbE54ccD9AD903f21961", + getStakingPoolv3Address: (arbitrumStakingPoolv3Address) => + set(() => ({ stakingPoolv3Address: arbitrumStakingPoolv3Address })), + }) +); + export const useChainlinkAbiStore = create() ( (set) => ({ chainlinkAbi: chainlinkAbi, @@ -173,6 +183,14 @@ export const useStakingPoolv2AbiStore = create()( }) ); +export const useStakingPoolv3AbiStore = create()( + (set) => ({ + stakingPoolv3Abi: stakingPoolv3Abi, + getLiquidationPoolAbi: (stakingPoolv3Abi) => + set(() => ({ stakingPoolv3Abi: stakingPoolv3Abi })), + }) +); + export const useErc20AbiStore = create() ( (set) => ({ erc20Abi: erc20Abi,