diff --git a/apps/govern/common-util/functions/requests.ts b/apps/govern/common-util/functions/requests.ts index d56ae7e5..f4d3de8b 100644 --- a/apps/govern/common-util/functions/requests.ts +++ b/apps/govern/common-util/functions/requests.ts @@ -1,6 +1,6 @@ import { readContract, readContracts } from '@wagmi/core'; import { ethers } from 'ethers'; -import { AbiFunction, TransactionReceipt } from 'viem'; +import { AbiFunction, TransactionReceipt, parseUnits } from 'viem'; import { Address } from 'viem'; import { mainnet } from 'viem/chains'; @@ -118,61 +118,61 @@ export const checkLockExpired = async (account: Address) => { /** * Approve amount of OLAS to be used */ -export const approveOlasByOwner = ({ account, amount }: { account: Address; amount: bigint }) => - new Promise((resolve, reject) => { +export const approveOlasByOwner = async ({ + account, + amount, +}: { + account: Address; + amount: bigint; +}) => { + try { const contract = getOlasContract(); const spender = (VE_OLAS.addresses as Record)[mainnet.id]; const fn = contract.methods.approve(spender, amount).send({ from: account }); - sendTransaction(fn, account, { + const response = await sendTransaction(fn, account, { supportedChains: SUPPORTED_CHAINS, rpcUrls: RPC_URLS, - }) - .then((response) => { - resolve(response); - }) - .catch((e) => { - window.console.log('Error occurred on approving OLAS by owner'); - reject(e); - }); - }); + }); + + return response; + } catch (error) { + window.console.log('Error occurred on approving OLAS by owner'); + throw error; + } +}; /** * Check if `Approve` button can be clicked; `allowance` should be greater than or equal to the amount */ -export const hasSufficientTokensRequest = ({ +export const hasSufficientTokensRequest = async ({ account, amount, }: { account: Address; amount: number; -}) => - new Promise((resolve, reject) => { +}) => { + try { const contract = getOlasContract(); const spender = (VE_OLAS.addresses as Record)[mainnet.id]; - contract.methods - .allowance(account, spender) - .call() - .then((response: bigint) => { - const responseInBg = ethers.toBigInt(response); - - // Resolve false if the response amount is zero - if (responseInBg === ethers.toBigInt(0)) { - resolve(false); - return; - } - - const amountBN = ethers.parseUnits(`${amount}`); - - // check if the allowance is greater than or equal to the amount input - resolve(responseInBg >= amountBN); - }) - .catch((e: Error) => { - window.console.log('Error occurred on calling `allowance` method'); - reject(e); - }); - }); + const response = await contract.methods.allowance(account, spender).call(); + const responseInBg = ethers.toBigInt(response); + + // Resolve false if the response amount is zero + if (responseInBg === ethers.toBigInt(0)) { + return false; + } + + const amountBN = parseUnits(`${amount}`, 18); + + // Check if the allowance is greater than or equal to the amount input + return responseInBg >= amountBN; + } catch (error) { + window.console.log('Error occurred on calling `allowance` method'); + throw error; + } +}; /** * Create lock for veOLAS diff --git a/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx b/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx index ebcb9b5d..0f47e42e 100644 --- a/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx +++ b/apps/govern/components/Contracts/MyVotingWeight/MyVotingWeight.tsx @@ -1,13 +1,12 @@ import { LoadingOutlined, TableOutlined, WalletOutlined } from '@ant-design/icons'; import { Button, Card as CardAntd, Flex, Spin, Typography } from 'antd'; import Image from 'next/image'; +import { useRouter } from 'next/router'; import { Dispatch, SetStateAction, useMemo } from 'react'; import styled from 'styled-components'; import { Allocation } from 'types'; import { useAccount } from 'wagmi'; -import { MEMBER_URL } from 'libs/util-constants/src'; - import { LoginV2 } from 'components/Login'; import { useVotingPower } from 'hooks/index'; import { useAppSelector } from 'store/index'; @@ -54,6 +53,11 @@ const ConnectWallet = () => { }; const GetVeOlas = () => { + const router = useRouter(); + + const handleOpenVeOlas = () => { + router.push('/veolas'); + }; return ( Olas @@ -61,7 +65,8 @@ const GetVeOlas = () => { Only veOLAS holders can vote on staking contracts.
Please lock OLAS for veOLAS to get started. -
diff --git a/apps/govern/components/Layout/Balance.tsx b/apps/govern/components/Layout/Balance.tsx index 5a8f24f0..2bedb5c6 100644 --- a/apps/govern/components/Layout/Balance.tsx +++ b/apps/govern/components/Layout/Balance.tsx @@ -1,9 +1,10 @@ import { InfoCircleOutlined } from '@ant-design/icons'; import { Button, Tooltip, Typography } from 'antd'; +import Link from 'next/link'; import { useAccount } from 'wagmi'; import { COLOR } from 'libs/ui-theme/src/lib/ui-theme'; -import { MEMBER_URL, UNICODE_SYMBOLS } from 'libs/util-constants/src'; +import { UNICODE_SYMBOLS } from 'libs/util-constants/src'; import { formatWeiNumber } from 'common-util/functions'; import { useVotingPower } from 'hooks/index'; @@ -24,9 +25,7 @@ export const Balance = () => { title={ <> veOLAS gives you voting power in Autonolas governance. - - Lock OLAS for veOLAS {UNICODE_SYMBOLS.EXTERNAL_LINK} - + Lock OLAS for veOLAS {UNICODE_SYMBOLS.EXTERNAL_LINK} } > diff --git a/apps/govern/components/VeOlas/CreateLockModal.tsx b/apps/govern/components/VeOlas/CreateLockModal.tsx index 9118f33c..cd0de8e3 100644 --- a/apps/govern/components/VeOlas/CreateLockModal.tsx +++ b/apps/govern/components/VeOlas/CreateLockModal.tsx @@ -42,27 +42,33 @@ export const CreateLockModal = ({ isModalVisible, setIsModalVisible }: CreateLoc setIsModalVisible(false); }; - const onCreateLock = async () => { + const handleCreateLock = async () => { if (!account) return; - setIsLoading(true); + try { + setIsLoading(true); - const txHash = await createLockRequest({ - amount: ethers.parseUnits(`${amountInEth}`, 18).toString(), - unlockTime: getRemainingTimeInSeconds(unlockTime), - account, - }); + const txHash = await createLockRequest({ + amount: ethers.parseUnits(`${amountInEth}`, 18).toString(), + unlockTime: getRemainingTimeInSeconds(unlockTime), + account, + }); - notifySuccess('Lock created successfully!', `Transaction Hash: ${txHash}`); + notifySuccess('Lock created successfully!', `Transaction Hash: ${txHash}`); - // once the lock is created, refetch the data - refetch(); + // once the lock is created, refetch the data + refetch(); - handleClose(); - setIsLoading(false); + handleClose(); + } catch (error) { + window.console.error(error); + notifyError(); + } finally { + setIsLoading(false); + } }; - const onFinish = async ({ amount }: FormValues) => { + const handleFinish = async ({ amount }: FormValues) => { if (!account) return; try { @@ -79,7 +85,7 @@ export const CreateLockModal = ({ isModalVisible, setIsModalVisible }: CreateLoc return; } - await onCreateLock(); + await handleCreateLock(); } catch (error) { window.console.error(error); notifyError(); @@ -104,7 +110,7 @@ export const CreateLockModal = ({ isModalVisible, setIsModalVisible }: CreateLoc autoComplete="off" name="create-lock-form" requiredMark={false} - onFinish={onFinish} + onFinish={handleFinish} >
@@ -159,7 +165,7 @@ export const CreateLockModal = ({ isModalVisible, setIsModalVisible }: CreateLoc isModalVisible={isApproveModalVisible} setIsModalVisible={setIsApproveModalVisible} amountInEth={amountInEth} - onApprove={onCreateLock} + onApprove={handleCreateLock} /> ); diff --git a/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseAmount.tsx b/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseAmount.tsx index 197ce5ed..10fc9749 100644 --- a/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseAmount.tsx +++ b/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseAmount.tsx @@ -11,7 +11,7 @@ import { ApproveOlasModal } from '../ApproveOlasModal'; import { MaxButton } from '../MaxButton'; import { OlasAmountInput } from '../OlasAmountInput'; import { ProjectedVeOlas } from '../ProjectedVeOlas'; -import { useVeolasComponents } from '../useVeolasComponents'; +import { LockedAmountComponent } from '../VeOlasComponents'; type IncreaseAmountProps = { closeModal: () => void; @@ -23,34 +23,46 @@ type FormValues = { export const IncreaseAmount = ({ closeModal }: IncreaseAmountProps) => { const [form] = Form.useForm(); - const { account, lockedEnd, olasBalance, veOlasBalance, refetch } = useFetchBalances(); - const { getLockedAmountComponent } = useVeolasComponents(); + const { + isLoading: isBalancesLoading, + account, + lockedEnd, + olasBalance, + veOlasBalance, + refetch, + } = useFetchBalances(); const [isLoading, setIsLoading] = useState(false); const [isApproveModalVisible, setIsApproveModalVisible] = useState(false); const amountInEth = Form.useWatch('amount', form); - const onIncreaseAmount = async () => { + const handleIncreaseAmount = async () => { if (!account) return; - setIsLoading(true); + try { + setIsLoading(true); - const txHash = await updateIncreaseAmount({ - amount: ethers.parseUnits(`${amountInEth}`, 18).toString(), - account, - }); + const txHash = await updateIncreaseAmount({ + amount: ethers.parseUnits(`${amountInEth}`, 18).toString(), + account, + }); - notifySuccess('Amount increased successfully!', `Transaction Hash: ${txHash}`); + notifySuccess('Amount increased successfully!', `Transaction Hash: ${txHash}`); - // once the amount is increased, refetch the data - refetch(); + // once the amount is increased, refetch the data + refetch(); - closeModal(); - setIsLoading(false); + closeModal(); + } catch (error) { + window.console.error(error); + notifyError(); + } finally { + setIsLoading(false); + } }; - const onFinish = async ({ amount }: FormValues) => { + const handleFinish = async ({ amount }: FormValues) => { if (!account) return; if (!veOlasBalance) return; @@ -69,7 +81,7 @@ export const IncreaseAmount = ({ closeModal }: IncreaseAmountProps) => { return; } - await onIncreaseAmount(); + await handleIncreaseAmount(); } catch (error) { window.console.error(error); notifyError(); @@ -92,9 +104,9 @@ export const IncreaseAmount = ({ closeModal }: IncreaseAmountProps) => { autoComplete="off" name="increase-amount-form" requiredMark={false} - onFinish={onFinish} + onFinish={handleFinish} > - {getLockedAmountComponent()} + @@ -137,7 +149,7 @@ export const IncreaseAmount = ({ closeModal }: IncreaseAmountProps) => { isModalVisible={isApproveModalVisible} setIsModalVisible={setIsApproveModalVisible} amountInEth={amountInEth} - onApprove={onIncreaseAmount} + onApprove={handleIncreaseAmount} /> ); diff --git a/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseUnlockTime.tsx b/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseUnlockTime.tsx index bbbee2fb..21ece576 100644 --- a/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseUnlockTime.tsx +++ b/apps/govern/components/VeOlas/IncreaseLockModal/IncreaseUnlockTime.tsx @@ -12,7 +12,7 @@ import { useFetchBalances } from 'hooks/useFetchBalances'; import { ProjectedVeOlas } from '../ProjectedVeOlas'; import { UnlockTimeInput } from '../UnlockTimeInput'; -import { useVeolasComponents } from '../useVeolasComponents'; +import { UnlockTimeComponent } from '../VeOlasComponents'; type IncreaseUnlockTimeProps = { closeModal: () => void; @@ -24,14 +24,20 @@ type FormValues = { export const IncreaseUnlockTime = ({ closeModal }: IncreaseUnlockTimeProps) => { const [form] = Form.useForm(); - const { account, lockedEnd, olasBalance, veOlasBalance, refetch } = useFetchBalances(); - const { getUnlockTimeComponent } = useVeolasComponents(); + const { + isLoading: isBalancesLoading, + account, + lockedEnd, + olasBalance, + veOlasBalance, + refetch, + } = useFetchBalances(); const [isLoading, setIsLoading] = useState(false); const unlockTime = dateInMs(Form.useWatch('unlockTime', form)); - const onFinish = async ({ unlockTime }: FormValues) => { + const handleFinish = async ({ unlockTime }: FormValues) => { if (!account) return; if (!veOlasBalance) return; @@ -71,9 +77,9 @@ export const IncreaseUnlockTime = ({ closeModal }: IncreaseUnlockTimeProps) => { autoComplete="off" name="increase-unlock-time-form" requiredMark={false} - onFinish={onFinish} + onFinish={handleFinish} > - {getUnlockTimeComponent()} + diff --git a/apps/govern/components/VeOlas/ProjectedVeOlas.tsx b/apps/govern/components/VeOlas/ProjectedVeOlas.tsx index f6c78e39..11bfebe2 100644 --- a/apps/govern/components/VeOlas/ProjectedVeOlas.tsx +++ b/apps/govern/components/VeOlas/ProjectedVeOlas.tsx @@ -1,7 +1,14 @@ +import { useMemo } from 'react'; + + + import { getCommaSeparatedNumber } from 'common-util/functions'; + + import { InfoCard } from './InfoCard'; + const SECONDS_IN_A_YEAR = 31536000; type ProjectedVeOlasProps = { @@ -14,7 +21,7 @@ export const ProjectedVeOlas = ({ olasAmount, unlockTime }: ProjectedVeOlasProps * @returns projected veOLAS amount as per the formula. * formula = veOLAS = OLAS * lockDuration / maxLockDuration */ - const getProjectedVeOlas = () => { + const projectedVeOlas = useMemo(() => { if (!olasAmount) return 0; if (!unlockTime) return 0; @@ -29,13 +36,13 @@ export const ProjectedVeOlas = ({ olasAmount, unlockTime }: ProjectedVeOlasProps } return getCommaSeparatedNumber(projectedVeOlas.toFixed(2).toString()); - }; + }, [olasAmount, unlockTime]); return ( ); -}; +}; \ No newline at end of file diff --git a/apps/govern/components/VeOlas/UnlockTimeInput.tsx b/apps/govern/components/VeOlas/UnlockTimeInput.tsx index 3fb220a0..c26968ca 100644 --- a/apps/govern/components/VeOlas/UnlockTimeInput.tsx +++ b/apps/govern/components/VeOlas/UnlockTimeInput.tsx @@ -1,6 +1,5 @@ import { DatePicker, DatePickerProps, Form, Typography } from 'antd'; import range from 'lodash/range'; -import React from 'react'; const { Text } = Typography; diff --git a/apps/govern/components/VeOlas/VeOlasComponents.tsx b/apps/govern/components/VeOlas/VeOlasComponents.tsx new file mode 100644 index 00000000..d2ed804e --- /dev/null +++ b/apps/govern/components/VeOlas/VeOlasComponents.tsx @@ -0,0 +1,121 @@ +import { + formatWeiNumber, + getCommaSeparatedNumber, + getFormattedDate, + getFullFormattedDate, +} from 'common-util/functions'; + +import { InfoCard } from './InfoCard'; + +const getTotalVotesPercentage = ( + votingPower: string | undefined, + totalSupply: string | undefined, +) => { + if (votingPower && totalSupply) { + const votingPowerInPercentage = ((Number(votingPower) / Number(totalSupply)) * 100).toFixed(2); + return formatWeiNumber({ value: votingPowerInPercentage }); + } + + return null; +}; + +export const BalanceComponent = ({ + isLoading, + olasBalance, +}: { + isLoading: boolean; + olasBalance: string | undefined; +}) => ( + +); + +export const VotingPowerComponent = ({ + isLoading, + votingPower, +}: { + isLoading: boolean; + votingPower: string | undefined; +}) => ( + +); + +export const VotingPercentComponent = ({ + isLoading, + votingPower, + totalSupply, +}: { + isLoading: boolean; + votingPower: string | undefined; + totalSupply: string | undefined; +}) => ( + +); + +export const LockedAmountComponent = ({ + isLoading, + veOlasBalance, +}: { + isLoading: boolean; + veOlasBalance: string | undefined; +}) => ( + +); + +export const UnlockTimeComponent = ({ + isLoading, + lockedEnd, +}: { + isLoading: boolean; + lockedEnd: number | undefined; +}) => ( + +); + +export const UnlockedAmountComponent = ({ + isLoading, + veOlasBalance, + canWithdrawVeolas, +}: { + isLoading: boolean; + veOlasBalance: string | undefined; + canWithdrawVeolas: boolean; +}) => { + // if the user has no locked OLAS, then don't show the component + if (!canWithdrawVeolas) return null; + return ( + + ); +}; diff --git a/apps/govern/components/VeOlas/VeOlasManage.tsx b/apps/govern/components/VeOlas/VeOlasManage.tsx index 15581e2b..cd6b6640 100644 --- a/apps/govern/components/VeOlas/VeOlasManage.tsx +++ b/apps/govern/components/VeOlas/VeOlasManage.tsx @@ -1,25 +1,32 @@ import { Button, Col, Row } from 'antd'; import { useState } from 'react'; -import { notifySuccess } from '@autonolas/frontend-library'; - -import { notifyError } from 'libs/util-functions/src'; +import { notifyError, notifySuccess } from 'libs/util-functions/src'; import { withdrawVeolasRequest } from 'common-util/functions'; import { useFetchBalances } from 'hooks/index'; -import { useVeolasComponents } from './useVeolasComponents'; +import { + BalanceComponent, + LockedAmountComponent, + UnlockTimeComponent, + UnlockedAmountComponent, + VotingPercentComponent, + VotingPowerComponent, +} from './VeOlasComponents'; export const VeOlasManage = () => { - const { isLoading, canWithdrawVeolas, account, refetch } = useFetchBalances(); const { - getBalanceComponent, - getVotingPowerComponent, - getVotingPercentComponent, - getLockedAmountComponent, - getUnlockTimeComponent, - getUnlockedAmountComponent, - } = useVeolasComponents(); + isLoading, + olasBalance, + votingPower, + totalSupply, + veOlasBalance, + lockedEnd, + canWithdrawVeolas, + account, + refetch, + } = useFetchBalances(); const [isWithdrawLoading, setIsWithdrawLoading] = useState(false); @@ -43,16 +50,28 @@ export const VeOlasManage = () => { return ( <> - {getBalanceComponent()} + + + - {getVotingPowerComponent()} + + + - {getVotingPercentComponent()} + + + - {getLockedAmountComponent()} + + + - {getUnlockTimeComponent()} + {!isLoading && canWithdrawVeolas && (