Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/Refactor tx handlers #222

Merged
merged 10 commits into from
Jun 2, 2023
339 changes: 73 additions & 266 deletions apps/common/utils/actions.tsx

Large diffs are not rendered by default.

47 changes: 46 additions & 1 deletion apps/common/utils/toWagmiProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {captureException} from '@sentry/nextjs';
import {prepareWriteContract, waitForTransaction, writeContract} from '@wagmi/core';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
import {ETH_TOKEN_ADDRESS, ZERO_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {isTAddress} from '@yearn-finance/web-lib/utils/isTAddress';
import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {assert} from '@common/utils/assert';

import type {BaseError} from 'viem';
import type {Connector} from 'wagmi';
import type {TAddress} from '@yearn-finance/web-lib/types';
import type {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TTxResponse} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {GetWalletClientResult} from '@wagmi/core';

export type TWagmiProviderContract = {
Expand Down Expand Up @@ -38,3 +42,44 @@ export function assertAddress(addr: string | TAddress | undefined, name?: string
assert(toAddress(addr) !== ZERO_ADDRESS, `${name || 'Address'} is 0x0`);
assert(toAddress(addr) !== ETH_TOKEN_ADDRESS, `${name || 'Address'} is 0xE`);
}

type TPrepareWriteContractProp = Parameters<typeof prepareWriteContract>[0];

export async function handleTx(
args: TWriteTransaction,
props: TPrepareWriteContractProp
): Promise<TTxResponse> {
args.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(args.connector);

//Some extra assertions
Majorfi marked this conversation as resolved.
Show resolved Hide resolved
Majorfi marked this conversation as resolved.
Show resolved Hide resolved
assertAddress(props.address, 'contractAddress');
assertAddress(wagmiProvider.address, 'userAddress');
Majorfi marked this conversation as resolved.
Show resolved Hide resolved
try {
const {request} = await prepareWriteContract({
...wagmiProvider,
...props,
address: props.address
});
const {hash} = await writeContract(request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
args.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
args.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
if (process.env.NODE_ENV === 'production') {
captureException(errorAsBaseError);
}
args.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
args.statusHandler?.({...defaultTxStatus});
}, 3000);
}
}
241 changes: 44 additions & 197 deletions apps/vaults/utils/actions.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import {captureException} from '@sentry/nextjs';
import STAKING_REWARDS_ABI from '@vaults/utils/abi/stakingRewards.abi';
import STAKING_REWARDS_ZAP_ABI from '@vaults/utils/abi/stakingRewardsZap.abi';
import VAULT_FACTORY_ABI from '@vaults/utils/abi/vaultFactory.abi';
import ZAP_VE_CRV_ABI from '@vaults/utils/abi/zapVeCRV.abi';
import {prepareWriteContract, waitForTransaction, writeContract} from '@wagmi/core';
import {prepareWriteContract} from '@wagmi/core';
import {} from '@yearn-finance/web-lib/utils/address';
import {STAKING_REWARDS_ZAP_ADDRESS, VAULT_FACTORY_ADDRESS, ZAP_YEARN_VE_CRV_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {toBigInt} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {defaultTxStatus} from '@yearn-finance/web-lib/utils/web3/transaction';
import {assert} from '@common/utils/assert';
import {assertAddress, toWagmiProvider} from '@common/utils/toWagmiProvider';
import {assertAddress, handleTx, toWagmiProvider} from '@common/utils/toWagmiProvider';

import type {BaseError} from 'viem';
import type {TAddress} from '@yearn-finance/web-lib/types';
import type {TTxResponse} from '@yearn-finance/web-lib/utils/web3/transaction';
import type {TWriteTransaction} from '@common/utils/toWagmiProvider';
Expand All @@ -29,41 +26,16 @@ type TDepositAndStake = TWriteTransaction & {
amount: bigint;
};
export async function depositAndStake(props: TDepositAndStake): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');
assertAddress(STAKING_REWARDS_ZAP_ADDRESS, 'STAKING_REWARDS_ZAP_ADDRESS');
assertAddress(props.vaultAddress, 'vaultAddress');
assert(props.amount > 0n, 'Amount is 0');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: STAKING_REWARDS_ZAP_ADDRESS,
abi: STAKING_REWARDS_ZAP_ABI,
functionName: 'zapIn',
args: [props.vaultAddress, props.amount]
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}

return await handleTx(props, {
address: STAKING_REWARDS_ZAP_ADDRESS,
abi: STAKING_REWARDS_ZAP_ABI,
functionName: 'zapIn',
args: [props.vaultAddress, props.amount]
});
}

/* 🔵 - Yearn Finance **********************************************************
Expand All @@ -77,39 +49,14 @@ type TStake = TWriteTransaction & {
amount: bigint;
};
export async function stake(props: TStake): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');
assert(props.amount > 0n, 'Amount is 0');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'stake',
args: [props.amount]
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}
return await handleTx(props, {
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'stake',
args: [props.amount]
});
}

/* 🔵 - Yearn Finance **********************************************************
Expand All @@ -120,37 +67,11 @@ export async function stake(props: TStake): Promise<TTxResponse> {
******************************************************************************/
type TUnstake = TWriteTransaction;
export async function unstake(props: TUnstake): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'exit'
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}
return await handleTx(props, {
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'exit'
});
}

/* 🔵 - Yearn Finance **********************************************************
Expand All @@ -161,37 +82,11 @@ export async function unstake(props: TUnstake): Promise<TTxResponse> {
******************************************************************************/
type TClaim = TWriteTransaction;
export async function claim(props: TClaim): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'getReward'
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}
return await handleTx(props, {
address: props.contractAddress,
abi: STAKING_REWARDS_ABI,
functionName: 'getReward'
});
}

/* 🔵 - Yearn Finance **********************************************************
Expand All @@ -209,41 +104,17 @@ type TVeCRVZap = TWriteTransaction & {
amount: bigint;
};
export async function veCRVzap(props: TVeCRVZap): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');
assertAddress(ZAP_YEARN_VE_CRV_ADDRESS, 'ZAP_YEARN_VE_CRV_ADDRESS');
assertAddress(props.inputToken, 'inputToken');
assertAddress(props.outputToken, 'outputToken');
assert(props.amount > 0n, 'Amount must be greater than 0n');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: ZAP_YEARN_VE_CRV_ADDRESS,
abi: ZAP_VE_CRV_ABI,
functionName: 'zap',
args: [props.inputToken, props.outputToken, props.amount]
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}
return await handleTx(props, {
address: ZAP_YEARN_VE_CRV_ADDRESS,
abi: ZAP_VE_CRV_ABI,
functionName: 'zap',
args: [props.inputToken, props.outputToken, props.amount]
});
}

/* 🔵 - Yearn Finance **********************************************************
Expand All @@ -257,54 +128,30 @@ type TCreateNewVaultsAndStrategies = TWriteTransaction & {
gaugeAddress: TAddress | undefined;
};
export async function createNewVaultsAndStrategies(props: TCreateNewVaultsAndStrategies): Promise<TTxResponse> {
assertAddress(props.contractAddress, 'contractAddress');
assertAddress(VAULT_FACTORY_ADDRESS, 'VAULT_FACTORY_ADDRESS');
assertAddress(props.gaugeAddress, 'gaugeAddress');

props.statusHandler?.({...defaultTxStatus, pending: true});
const wagmiProvider = await toWagmiProvider(props.connector);

try {
const config = await prepareWriteContract({
...wagmiProvider,
address: VAULT_FACTORY_ADDRESS,
abi: VAULT_FACTORY_ABI,
functionName: 'createNewVaultsAndStrategies',
args: [props.gaugeAddress]
});
const {hash} = await writeContract(config.request);
const receipt = await waitForTransaction({chainId: wagmiProvider.chainId, hash});
if (receipt.status === 'success') {
props.statusHandler?.({...defaultTxStatus, success: true});
} else if (receipt.status === 'reverted') {
props.statusHandler?.({...defaultTxStatus, error: true});
}
return ({isSuccessful: receipt.status === 'success', receipt});
} catch (error) {
console.error(error);
const errorAsBaseError = error as BaseError;
captureException(errorAsBaseError);
props.statusHandler?.({...defaultTxStatus, error: true});
return ({isSuccessful: false, error: errorAsBaseError || ''});
} finally {
setTimeout((): void => {
props.statusHandler?.({...defaultTxStatus});
}, 3000);
}
return await handleTx(props, {
address: VAULT_FACTORY_ADDRESS,
abi: VAULT_FACTORY_ABI,
functionName: 'createNewVaultsAndStrategies',
args: [props.gaugeAddress]
});
}

/* 🔵 - Yearn Finance **********************************************************
** gasOfCreateNewVaultsAndStrategies is a _READ function that estimate the gas
** gasOfCreateNewVaultsAndStrategies is a _READ_ function that estimate the gas
** of the createNewVaultsAndStrategies function.
**
** @app - Vaults (veCRV)
** @param gaugeAddress - the base gauge address
******************************************************************************/
export async function gasOfCreateNewVaultsAndStrategies(props: TCreateNewVaultsAndStrategies): Promise<bigint> {
assertAddress(props.contractAddress, 'contractAddress');
assertAddress(props.gaugeAddress, 'gaugeAddress');

const wagmiProvider = await toWagmiProvider(props.connector);
try {
assertAddress(props.contractAddress, 'contractAddress');
assertAddress(props.gaugeAddress, 'gaugeAddress');

const wagmiProvider = await toWagmiProvider(props.connector);
const config = await prepareWriteContract({
...wagmiProvider,
address: VAULT_FACTORY_ADDRESS,
Expand Down
4 changes: 2 additions & 2 deletions apps/veyfi/components/ClaimTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useCallback, useState} from 'react';
import {formatUnits} from 'viem';
import {useVotingEscrow} from '@veYFI/contexts/useVotingEscrow';
import {withdrawUnlocked} from '@veYFI/utils/actions';
import {withdrawUnlockedVeYFI} 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';
Expand Down Expand Up @@ -31,7 +31,7 @@ function ClaimTab(): ReactElement {
}, [refreshVotingEscrow, refreshBalances]);

const onWithdrawUnlocked = useCallback(async (): Promise<void> => {
const result = await withdrawUnlocked({
const result = await withdrawUnlockedVeYFI({
connector: provider,
contractAddress: votingEscrow?.address,
statusHandler: set_withdrawUnlockedStatus
Expand Down
Loading