Skip to content

Commit

Permalink
feat: migrate to wagmi
Browse files Browse the repository at this point in the history
  • Loading branch information
Majorfi authored and karelianpie committed Jun 1, 2023
1 parent f4bc131 commit 4869401
Show file tree
Hide file tree
Showing 12 changed files with 476 additions and 302 deletions.
2 changes: 1 addition & 1 deletion apps/common/utils/toWagmiProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {GetWalletClientResult} from '@wagmi/core';
export type TWagmiProviderContract = {
walletClient: GetWalletClientResult,
chainId: number,
address: string,
address: TAddressWagmi,
}
export async function toWagmiProvider(connector: Connector | undefined): Promise<TWagmiProviderContract> {
assert(connector, 'Connector is not set');
Expand Down
3 changes: 1 addition & 2 deletions apps/veyfi/Wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';
import {AnimatePresence, motion} from 'framer-motion';
import {VotingEscrowContextApp} from '@veYFI/contexts/useVotingEscrow';
import Meta from '@common/components/Meta';
import {useCurrentApp} from '@common/hooks/useCurrentApp';
import {variants} from '@common/utils/animations';

import {VotingEscrowContextApp} from './contexts/useVotingEscrow';

import type {NextRouter} from 'next/router';
import type {ReactElement} from 'react';

Expand Down
41 changes: 27 additions & 14 deletions apps/veyfi/components/ClaimTab.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
import {useCallback, useState} from 'react';
import {formatUnits} from 'viem';
import {useVotingEscrow} from '@veYFI/contexts/useVotingEscrow';
import {useTransaction} from '@veYFI/hooks/useTransaction';
import * as VotingEscrowActions from '@veYFI/utils/actions/votingEscrow';
import {withdrawUnlocked} from '@veYFI/utils/actions';
import {validateNetwork} from '@veYFI/utils/validations';
import {Button} from '@yearn-finance/web-lib/components/Button';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {formatBN, formatUnits} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
import {toBigInt} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {getTimeUntil} from '@yearn-finance/web-lib/utils/time';
import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {AmountInput} from '@common/components/AmountInput';
import {useWallet} from '@common/contexts/useWallet';

import type {ReactElement} from 'react';
import type {TTxResponse} from '@yearn-finance/web-lib/utils/web3/transaction';

function ClaimTab(): ReactElement {
const {provider, address, isActive} = useWeb3();
const {safeChainID} = useChainID();
const {refresh: refreshBalances} = useWallet();
const {votingEscrow, positions, refresh: refreshVotingEscrow} = useVotingEscrow();
const refreshData = (): unknown => Promise.all([refreshVotingEscrow(), refreshBalances()]);
const [withdrawUnlocked, withdrawUnlockedStatus] = useTransaction(VotingEscrowActions.withdrawUnlocked, refreshData);

const hasLockedAmount = formatBN(positions?.deposit?.underlyingBalance).gt(0);
const [withdrawUnlockedStatus, set_withdrawUnlockedStatus] = useState(defaultTxStatus);
const hasLockedAmount = toBigInt(positions?.deposit?.underlyingBalance) > 0n;
const timeUntilUnlock = positions?.unlockTime ? getTimeUntil(positions?.unlockTime) : 0;
const isClaimable = hasLockedAmount && !timeUntilUnlock;
const claimableAmount = isClaimable ? positions?.deposit?.underlyingBalance : '0';

const {isValid: isValidNetwork} = validateNetwork({supportedNetwork: 1, walletNetwork: safeChainID});

const refreshData = useCallback(async (): Promise<void> => {
await Promise.all([refreshVotingEscrow(), refreshBalances()]);
}, [refreshVotingEscrow, refreshBalances]);

const onWithdrawUnlocked = useCallback(async (): Promise<void> => {
const result = await withdrawUnlocked({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.address),
statusHandler: set_withdrawUnlockedStatus
});
if (result.isSuccessful) {
refreshData();
}
}, [provider, refreshData, votingEscrow?.address]);

return (
<div className={'grid grid-cols-1 gap-6 md:grid-cols-2 md:gap-16'}>
<div className={'col-span-1 grid w-full gap-6'}>
Expand All @@ -46,13 +59,13 @@ function ClaimTab(): ReactElement {
<div className={'grid grid-cols-1 gap-6 md:mt-14 md:grid-cols-2'}>
<AmountInput
label={'Unlocked YFI'}
amount={formatUnits(claimableAmount, 18)}
amount={formatUnits(toBigInt(claimableAmount), 18)}
disabled />
<Button
className={'w-full md:mt-7'}
onClick={async (): Promise<TTxResponse> => withdrawUnlocked(provider, toAddress(address), toAddress(votingEscrow?.address))}
isBusy={withdrawUnlockedStatus.loading}
isDisabled={!isActive || !isValidNetwork || !isClaimable || withdrawUnlockedStatus.loading || !votingEscrow || !address}>
onClick={onWithdrawUnlocked}
isBusy={withdrawUnlockedStatus.pending}
isDisabled={!isActive || !isValidNetwork || !isClaimable || withdrawUnlockedStatus.pending || !votingEscrow || !address}>
{'Claim'}
</Button>
</div>
Expand Down
88 changes: 65 additions & 23 deletions apps/veyfi/components/LockTab.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import {useEffect, useMemo, useState} from 'react';
import {useCallback, useEffect, useMemo, useState} from 'react';
import {formatUnits} from 'viem';
import {useVotingEscrow} from '@veYFI/contexts/useVotingEscrow';
import {useTransaction} from '@veYFI/hooks/useTransaction';
import {getVotingPower} from '@veYFI/utils';
import * as VotingEscrowActions from '@veYFI/utils/actions/votingEscrow';
import {increaseLockAmount, lock} from '@veYFI/utils/actions';
import {MAX_LOCK_TIME, MIN_LOCK_AMOUNT, MIN_LOCK_TIME} from '@veYFI/utils/constants';
import {validateAllowance, validateAmount, validateNetwork} from '@veYFI/utils/validations';
import {Button} from '@yearn-finance/web-lib/components/Button';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {formatBN, formatUnits, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {toAddress, toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
import {toBigInt, toNormalizedBN} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {formatAmount} from '@yearn-finance/web-lib/utils/format.number';
import {handleInputChangeEventValue} from '@yearn-finance/web-lib/utils/handlers/handleInputChangeEventValue';
import {fromWeeks, getTimeUntil, toSeconds, toTime, toWeeks} from '@yearn-finance/web-lib/utils/time';
import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {AmountInput} from '@common/components/AmountInput';
import {useWallet} from '@common/contexts/useWallet';
import {useBalance} from '@common/hooks/useBalance';
import {approveERC20} from '@common/utils/actions';

import type {BigNumber} from 'ethers';
import type {ReactElement} from 'react';
import type {TMilliseconds} from '@yearn-finance/web-lib/utils/time';
import type {TTxResponse} from '@yearn-finance/web-lib/utils/web3/transaction';

function LockTab(): ReactElement {
const [lockAmount, set_lockAmount] = useState(toNormalizedBN(0));
Expand All @@ -30,23 +30,65 @@ function LockTab(): ReactElement {
const {refresh: refreshBalances} = useWallet();
const {votingEscrow, positions, allowances, isLoading: isLoadingVotingEscrow, refresh: refreshVotingEscrow} = useVotingEscrow();
const tokenBalance = useBalance(toAddress(votingEscrow?.token));
const clearLockAmount = (): void => set_lockAmount(toNormalizedBN(0));
const refreshData = (): unknown => Promise.all([refreshVotingEscrow(), refreshBalances()]);
const onTxSuccess = (): unknown => Promise.all([refreshData(), clearLockAmount()]);
const [approveLock, approveLockStatus] = useTransaction(VotingEscrowActions.approveLock, refreshData);
const [lock, lockStatus] = useTransaction(VotingEscrowActions.lock, onTxSuccess);
const [increaseLockAmount, increaseLockAmountStatus] = useTransaction(VotingEscrowActions.increaseLockAmount, onTxSuccess);

const hasLockedAmount = formatBN(positions?.deposit?.underlyingBalance).gt(0);
const hasLockedAmount = toBigInt(positions?.deposit?.underlyingBalance) > 0n;
const [approveLockStatus, set_approveLockStatus] = useState(defaultTxStatus);
const [lockStatus, set_lockStatus] = useState(defaultTxStatus);
const [increaseLockAmountStatus, set_increaseLockAmountStatus] = useState(defaultTxStatus);

const unlockTime = useMemo((): TMilliseconds => {
return positions?.unlockTime || Date.now() + fromWeeks(toTime(lockTime));
}, [positions?.unlockTime, lockTime]);

const votingPower = useMemo((): BigNumber => {
return getVotingPower(formatBN(positions?.deposit?.underlyingBalance).add(lockAmount.raw), unlockTime);
const votingPower = useMemo((): bigint => {
return getVotingPower(toBigInt(positions?.deposit?.underlyingBalance) + toBigInt(lockAmount.raw), unlockTime);
}, [positions?.deposit?.underlyingBalance, lockAmount, unlockTime]);

const refreshData = useCallback(async (): Promise<void> => {
await Promise.all([refreshVotingEscrow(), refreshBalances()]);
}, [refreshVotingEscrow, refreshBalances]);

const onTxSuccess = useCallback(async (): Promise<void> => {
await Promise.all([refreshData(), set_lockAmount(toNormalizedBN(0))]);
}, [refreshData]);

const onApproveLock = useCallback(async (): Promise<void> => {
const result = await approveERC20({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.token),
spenderAddress: toWagmiAddress(votingEscrow?.address),
statusHandler: set_approveLockStatus,
amount: lockAmount.raw
});
if (result.isSuccessful) {
refreshData();
}
}, [lockAmount.raw, provider, refreshData, votingEscrow?.address, votingEscrow?.token]);

const onLock = useCallback(async (): Promise<void> => {
const result = await lock({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.address),
amount: lockAmount.raw,
time: toBigInt(toSeconds(unlockTime)),
statusHandler: set_lockStatus
});
if (result.isSuccessful) {
onTxSuccess();
}
}, [provider, votingEscrow?.address, lockAmount.raw, unlockTime, onTxSuccess]);

const onIncreaseLockAmount = useCallback(async (): Promise<void> => {
const result = await increaseLockAmount({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.address),
amount: lockAmount.raw,
statusHandler: set_increaseLockAmountStatus
});
if (result.isSuccessful) {
onTxSuccess();
}
}, [provider, votingEscrow?.address, lockAmount.raw, onTxSuccess]);

useEffect((): void => {
if(!positions?.unlockTime) {
return;
Expand Down Expand Up @@ -82,21 +124,21 @@ function LockTab(): ReactElement {
const txAction = !isApproved
? {
label: 'Approve',
onAction: async (): Promise<TTxResponse> => approveLock(provider, toAddress(address), toAddress(votingEscrow?.token), toAddress(votingEscrow?.address)),
isLoading: approveLockStatus.loading,
onAction: onApproveLock,
isLoading: approveLockStatus.pending,
isDisabled: isApproveDisabled
}
: hasLockedAmount
? {
label: 'Lock',
onAction: async (): Promise<TTxResponse> => increaseLockAmount(provider, toAddress(address), toAddress(votingEscrow?.address), lockAmount.raw),
isLoading: increaseLockAmountStatus.loading,
onAction: onIncreaseLockAmount,
isLoading: increaseLockAmountStatus.pending,
isDisabled: isLockDisabled
}
: {
label: 'Lock',
onAction: async (): Promise<TTxResponse> => lock(provider, toAddress(address), toAddress(votingEscrow?.address), lockAmount.raw, toSeconds(unlockTime)),
isLoading: lockStatus.loading,
onAction: onLock,
isLoading: lockStatus.pending,
isDisabled: isLockDisabled
};

Expand Down
79 changes: 50 additions & 29 deletions apps/veyfi/components/ManageLockTab.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
import {useMemo, useState} from 'react';
import {useCallback, useMemo, useState} from 'react';
import {formatUnits} from 'viem';
import {useVotingEscrow} from '@veYFI/contexts/useVotingEscrow';
import {useTransaction} from '@veYFI/hooks/useTransaction';
import {getVotingPower} from '@veYFI/utils';
import * as VotingEscrowActions from '@veYFI/utils/actions/votingEscrow';
import {extendLockTime, withdrawLocked} from '@veYFI/utils/actions';
import {MAX_LOCK_TIME, MIN_LOCK_TIME} from '@veYFI/utils/constants';
import {validateAmount, validateNetwork} from '@veYFI/utils/validations';
import {Button} from '@yearn-finance/web-lib/components/Button';
import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {formatBN, formatUnits} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {toWagmiAddress} from '@yearn-finance/web-lib/utils/address';
import {toBigInt} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {fromWeeks, getTimeUntil, toSeconds, toTime, toWeeks} from '@yearn-finance/web-lib/utils/time';
import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {AmountInput} from '@common/components/AmountInput';
import {useWallet} from '@common/contexts/useWallet';

import {AmountInput} from '../../common/components/AmountInput';

import type {BigNumber} from 'ethers';
import type {ReactElement} from 'react';
import type {TTxResponse} from '@yearn-finance/web-lib/utils/web3/transaction';

function ManageLockTab(): ReactElement {
const [lockTime, set_lockTime] = useState('');
const {provider, address, isActive} = useWeb3();
const {safeChainID} = useChainID();
const {refresh: refreshBalances} = useWallet();
const {votingEscrow, positions, refresh: refreshVotingEscrow} = useVotingEscrow();
const clearLockTime = (): void => set_lockTime('');
const refreshData = (): unknown => Promise.all([refreshVotingEscrow(), refreshBalances()]);
const onTxSuccess = (): unknown => Promise.all([refreshData(), clearLockTime()]);
const [extendLockTime, extendLockTimeStatus] = useTransaction(VotingEscrowActions.extendLockTime, onTxSuccess);
const [withdrawLocked, withdrawLockedStatus] = useTransaction(VotingEscrowActions.withdrawLocked, onTxSuccess);

const hasLockedAmount = formatBN(positions?.deposit?.underlyingBalance).gt(0);
const willExtendLock = formatBN(lockTime).gt(0);
const hasLockedAmount = toBigInt(positions?.deposit?.underlyingBalance) > 0n;
const willExtendLock = toBigInt(lockTime) > 0n;
const timeUntilUnlock = positions?.unlockTime ? getTimeUntil(positions?.unlockTime) : undefined;
const weeksToUnlock = toWeeks(timeUntilUnlock);
const newUnlockTime = toTime(positions?.unlockTime) + fromWeeks(toTime(lockTime));
const hasPenalty = formatBN(positions?.penalty).gt(0);
const hasPenalty = toBigInt(positions?.penalty) > 0n;
const [extendLockTimeStatus, set_extendLockTimeStatus] = useState(defaultTxStatus);
const [withdrawLockedStatus, set_withdrawLockedStatus] = useState(defaultTxStatus);

const onTxSuccess = useCallback(async (): Promise<void> => {
await Promise.all([refreshVotingEscrow(), refreshBalances(), set_lockTime('')]);
}, [refreshBalances, refreshVotingEscrow]);

const onExtendLockTime = useCallback(async (): Promise<void> => {
const result = await extendLockTime({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.address),
time: toBigInt(toSeconds(newUnlockTime)),
statusHandler: set_extendLockTimeStatus
});
if (result.isSuccessful) {
onTxSuccess();
}
}, [newUnlockTime, onTxSuccess, provider, votingEscrow?.address]);

const onWithdrawLocked = useCallback(async (): Promise<void> => {
const result = await withdrawLocked({
connector: provider,
contractAddress: toWagmiAddress(votingEscrow?.address),
statusHandler: set_withdrawLockedStatus
});
if (result.isSuccessful) {
onTxSuccess();
}
}, [onTxSuccess, provider, votingEscrow?.address]);

const votingPower = useMemo((): BigNumber => {
const votingPower = useMemo((): bigint => {
if(!positions?.deposit || !newUnlockTime) {
return formatBN(0);
return 0n;
}
return willExtendLock ? getVotingPower(positions?.deposit?.underlyingBalance, newUnlockTime) : formatBN(positions?.deposit?.balance);
return willExtendLock ? getVotingPower(positions?.deposit?.underlyingBalance, newUnlockTime) : toBigInt(positions?.deposit?.balance);
}, [positions?.deposit, newUnlockTime, willExtendLock]);

const {isValid: isValidLockTime, error: lockTimeError} = validateAmount({
Expand Down Expand Up @@ -85,9 +106,9 @@ function ManageLockTab(): ReactElement {
disabled />
<Button
className={'w-full md:mt-7'}
onClick={async (): Promise<TTxResponse> => extendLockTime(provider, toAddress(address), toAddress(votingEscrow?.address), toSeconds(newUnlockTime))}
isBusy={extendLockTimeStatus.loading}
isDisabled={!isActive || !isValidNetwork || !isValidLockTime || extendLockTimeStatus.loading || !votingEscrow || !address}>
onClick={onExtendLockTime}
isBusy={extendLockTimeStatus.pending}
isDisabled={!isActive || !isValidNetwork || !isValidLockTime || extendLockTimeStatus.pending || !votingEscrow || !address}>
{'Extend'}
</Button>
</div>
Expand All @@ -105,7 +126,7 @@ function ManageLockTab(): ReactElement {
<div className={'grid grid-cols-1 gap-6 md:grid-cols-2 md:pb-5'}>
<AmountInput
label={'veYFI you have'}
amount={formatUnits(positions?.deposit?.balance, 18)}
amount={formatUnits(toBigInt(positions?.deposit?.balance), 18)}
disabled />
<AmountInput
label={'Current lock time (weeks)'}
Expand All @@ -115,14 +136,14 @@ function ManageLockTab(): ReactElement {
<div className={'grid grid-cols-1 gap-6 md:grid-cols-2'}>
<AmountInput
label={'YFI you get'}
amount={formatUnits(positions?.withdrawable, 18)}
amount={formatUnits(toBigInt(positions?.withdrawable), 18)}
legend={`Penalty: ${((positions?.penaltyRatio ?? 0) * 100).toFixed(2).toString()}%`}
disabled />
<Button
className={'w-full md:mt-7'}
onClick={async (): Promise<TTxResponse> => withdrawLocked(provider, toAddress(address), toAddress(votingEscrow?.address))}
isBusy={withdrawLockedStatus.loading}
isDisabled={!isActive || !isValidNetwork || !hasPenalty || withdrawLockedStatus.loading || !votingEscrow || !address}>
onClick={onWithdrawLocked}
isBusy={withdrawLockedStatus.pending}
isDisabled={!isActive || !isValidNetwork || !hasPenalty || withdrawLockedStatus.pending || !votingEscrow || !address}>
{'Exit'}
</Button>
</div>
Expand Down
Loading

0 comments on commit 4869401

Please sign in to comment.