Skip to content

Commit

Permalink
bugfix/transfer eth apporal
Browse files Browse the repository at this point in the history
  • Loading branch information
brucedonovan committed Sep 26, 2023
1 parent 26921c6 commit ad06c11
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 66 deletions.
16 changes: 7 additions & 9 deletions src/components/cactiComponents/ActionResponse.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useEffect, useMemo, useState } from 'react';
import Skeleton from 'react-loading-skeleton';
import { AddressZero } from '@ethersproject/constants';
import { CheckCircleIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { formatUnits } from 'ethers/lib/utils.js';
import tw from 'tailwind-styled-components';
import { TransactionReceipt, TransactionRequestBase } from 'viem';
import { TransactionReceipt, TransactionRequestBase, formatUnits, zeroAddress } from 'viem';
import { UsePrepareContractWriteConfig, useAccount } from 'wagmi';
import useToken from '@/hooks/useToken';
import { cleanValue } from '@/utils';
Expand Down Expand Up @@ -49,9 +47,9 @@ type Action = {
};

export type ActionResponseProps = {
txParams: UsePrepareContractWriteConfig | undefined;
approvalParams: ApprovalBasicParams | undefined;
sendParams?: TransactionRequestBase | undefined;
txParams?: UsePrepareContractWriteConfig;
approvalParams?: ApprovalBasicParams;
sendParams?: TransactionRequestBase;
label?: string; // label to show on button
description?: string; // tx description (for wallet )
disabled?: boolean;
Expand Down Expand Up @@ -81,15 +79,15 @@ export const ActionResponse = ({
const _approvalParams = useMemo<ApprovalBasicParams>(
() =>
approvalParams || {
tokenAddress: AddressZero,
spender: AddressZero,
tokenAddress: zeroAddress,
spender: zeroAddress,
approvalAmount: BigInt(0),
skipApproval: true, // NOTE: approval is skipped if no approval params are passed in
},
[approvalParams]
);
const { data: token } = useToken(undefined, _approvalParams.tokenAddress);
const amountFmt = formatUnits(_approvalParams.approvalAmount.toString(), token?.decimals);
const amountFmt = formatUnits(_approvalParams.approvalAmount, token!.decimals);

/** Check for the approval. If no approvalParams, hasAllowance === true and approveTx == undefined */
const {
Expand Down
21 changes: 12 additions & 9 deletions src/components/cactiComponents/hooks/useApproval.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useMemo } from 'react';
import { useQuery } from 'react-query';
import { AddressZero } from '@ethersproject/constants';
import { erc20ABI, useContractWrite, useWaitForTransaction } from 'wagmi';
import { erc20ABI, useContractWrite, usePrepareContractWrite, useWaitForTransaction } from 'wagmi';
import { prepareWriteContract } from 'wagmi/actions';
import useChainId from '@/hooks/useChainId';
import useToken from '@/hooks/useToken';
import { cleanValue } from '@/utils';
import useAllowance from './useAllowance';
import { zeroAddress } from 'viem';

export type ApprovalBasicParams = {
approvalAmount: bigint;
Expand All @@ -16,13 +16,15 @@ export type ApprovalBasicParams = {
};

const validateAddress = (addr: `0x${string}`): `0x${string}` | undefined =>
addr === AddressZero ? undefined : addr;
addr === zeroAddress ? undefined : addr;

const useApproval = (params: ApprovalBasicParams) => {
const chainId = useChainId();
const { approvalAmount, tokenAddress: _tokenAddress, spender: _spender } = params;

const tokenAddress = validateAddress(_tokenAddress);
const spender = validateAddress(_spender);

const { data: token } = useToken(undefined, tokenAddress); // get token data from address (zero address === ETH)

// cleanup the bignumber and convert back to a bignumber to avoid underlow errors;
Expand All @@ -39,15 +41,16 @@ const useApproval = (params: ApprovalBasicParams) => {
// Prepare the approval transaction - doesn't run if address or spender is undefined
const { data: config, isError: isPrepareError } = useQuery({
queryKey: ['prepareApprove', tokenAddress, spender, chainId],
queryFn: async () => {
queryFn: async () => {
// case: invalid spender
if (!spender) {
console.error(`Spender not found for approval`);
return {};
console.warn(`Spender not found for approval`);
return;
}

// is eth
// case: Is eth ( null token address )
if (!tokenAddress) {
return {};
console.warn(`Null token address - no approval needed`);
return;
}

const {request} = await prepareWriteContract({
Expand Down
6 changes: 3 additions & 3 deletions src/components/cactiComponents/hooks/useBalance.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
import { BigNumber } from 'ethers';
import { useAccount } from 'wagmi';
import { fetchBalance, readContract } from 'wagmi/actions';
import erc1155ABI from '@/abi/erc1155ABI';
import useChainId from '@/hooks/useChainId';
import { zeroAddress } from 'viem';

/**
* @description gets the balance of a an account for a token address, or if no address is specified, get's eth balance
Expand All @@ -26,8 +26,8 @@ const useBalance = (
return;
}

// fetch native balance
if (!tokenAddress) {
// fetch native balsance
if (!tokenAddress || tokenAddress === zeroAddress) {
return (
await fetchBalance({
address: account,
Expand Down
4 changes: 2 additions & 2 deletions src/components/cactiComponents/hooks/useSubmitTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const useSubmitTx = (
/* prepare a write transaction */
const { config: writeConfig } = usePrepareContractWrite({
...params,
onError: (e) => console.log('prepare contract write error', e),
onError: (e) => console.log('Prepare contract write error', e),
});

/* prepare a send transaction if the fnName matches the SEND_TRANSACTION unique id */
Expand All @@ -57,7 +57,7 @@ const useSubmitTx = (
value: writeConfig.request.value,
}
: sendParams),
onError: (e) => console.log('prepare send error', e),
onError: (e) => console.log('Prepare send error', e),
});

/* usePrepped data to run write or send transactions */
Expand Down
1 change: 0 additions & 1 deletion src/components/current/containers/SingleStepContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { UnsignedTransaction } from 'ethers';
import { Address, TransactionRequestBase } from 'viem';
import useEnsAvatar from '@/components/cactiComponents/hooks/useEnsAvatar';
import useEnsName from '@/components/cactiComponents/hooks/useEnsName';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useMemo } from 'react';
import { parseUnits } from 'ethers/lib/utils.js';
import { parseEther } from 'viem';
import { UsePrepareContractWriteConfig } from 'wagmi';
import rETHAbi from '@/abi/rETH';
Expand Down
52 changes: 19 additions & 33 deletions src/components/current/widgets/transfer/Transfer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { parseUnits, zeroAddress } from 'viem';
import { useMemo } from 'react';
import { parseUnits} from 'viem';
import { erc20ABI, useAccount, useEnsAddress } from 'wagmi';
import { ActionResponse, HeaderResponse } from '@/components/cactiComponents';
import { SEND_ETH_FNNAME } from '@/components/cactiComponents/hooks/useSubmitTx';
import useToken from '@/hooks/useToken';
import { ConnectFirst } from '../helpers/ConnectFirst';

Expand All @@ -16,35 +16,25 @@ const Transfer = ({ tokenSymbol, amtString, receiver }: TransferWidgetProps) =>
const { isETH, data: token } = useToken(tokenSymbol);
const amount = parseUnits(amtString, token?.decimals!);

// Resolve ENS name
const { data: receiverAddress } = useEnsAddress({
name: receiver,
// Resolve ENS name, if needed
const { data: addressFromName } = useEnsAddress({
name: receiver.trimStart().slice(2) === '0x' ? undefined : receiver, // only run if string doesnt start with 0x
});

if (!isETH && !token) return null; // if not eth, and there is no token - abort.
const receiverAddress = useMemo(() => {
if (addressFromName) return addressFromName;
return receiver as `0x${string}`;
}, [addressFromName, receiver]);

const approval = {
approvalAmount: amount,
tokenAddress: token!.address as `0x${string}`,
spender: zeroAddress,
skipApproval: true,
};
if (!isETH && !token) return null; // if not eth, and there is no token - abort.

/* tx parameters to transfer ETH */
const tx = isETH
? {
address: undefined,
abi: undefined,
functionName: SEND_ETH_FNNAME,
args: [receiverAddress ? receiverAddress : (receiver as `0x${string}`), amount],
}
: /* tx parameters to transfer an ERC20 token */
{
address: token!.address as `0x${string}`,
abi: erc20ABI,
functionName: 'transfer',
args: [receiverAddress ? receiverAddress : (receiver as `0x${string}`), amount],
};
const tx = {
address: token!.address as `0x${string}`,
abi: erc20ABI,
functionName: 'transfer',
args: [receiverAddress, amount],
};

/* TODO Transfer NFT */
/* TODO Transfer ERC721 */
Expand All @@ -57,13 +47,9 @@ const Transfer = ({ tokenSymbol, amtString, receiver }: TransferWidgetProps) =>
/>
<ActionResponse
label={`Transfer ${amtString || ''} ${tokenSymbol}`}
txParams={tx}
approvalParams={approval}
sendParams={
isETH && receiverAddress
? { to: receiverAddress, value: amount, from: account! }
: undefined
}
txParams={ isETH ? undefined : tx}
approvalParams={undefined} // approval shouldn't be required?
sendParams={isETH ? { to: receiverAddress, value: amount, from: account! } : undefined}
/>
</ConnectFirst>
);
Expand Down
2 changes: 1 addition & 1 deletion src/contexts/ConnectionWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ConnectionWrapper = ({ children, useSiwe = true }: any) => {

const mainnetFork = {
id: 1,
name: 'Mainnet Fork',
name: 'mainnet',
network: 'mainnet',
nativeCurrency: {
decimals: 18,
Expand Down
1 change: 0 additions & 1 deletion src/hooks/useSigner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useContext } from 'react';
import { Signer } from 'ethers';
import { useWalletClient } from 'wagmi';
import SettingsContext from '@/contexts/SettingsContext';
import useForkTools from '@/hooks/useForkTools';
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/useToken.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useMemo } from 'react';
import { ethers } from 'ethers';
import { zeroAddress } from 'viem';
import { Address } from 'wagmi';
import useChainId from '@/hooks/useChainId';
import { Token } from '@/types';
Expand All @@ -10,15 +10,15 @@ const useToken = (tokenSymbol?: string, tokenAddress?: Address) => {

const getTokenIsETH = useCallback(
(tokenSymbol?: string, tokenAddress?: string) =>
tokenSymbol === 'ETH' || tokenAddress === ethers.constants.AddressZero,
tokenSymbol === 'ETH' || tokenAddress === zeroAddress,
[]
);

const getToken = useCallback(
(tokenSymbol?: string, tokenAddress?: Address): Token | undefined => {
if (getTokenIsETH(tokenSymbol, tokenAddress))
return {
address: ethers.constants.AddressZero,
address: zeroAddress,
symbol: 'ETH',
decimals: 18,
logoURI:
Expand Down
6 changes: 3 additions & 3 deletions src/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { utils } from 'ethers';
import projectListJson from '@/utils/ProjectList.json';
import tokenListJson from '@/utils/TokenList.json';
import { Project } from '../types';
import { formatEther, parseEther } from 'viem';

export const shortenAddress = (address: string) => address.slice(0, 6) + '...' + address.slice(-4);

Expand All @@ -13,8 +13,8 @@ export const findTokenByAddress = (address: string, chainId: number) =>
tokenListJson.tokens.find(
(token) => token.address.toLowerCase() === address.toLowerCase() && token.chainId === chainId
);
export const formatToEther = (amount: string) => utils.formatEther(amount);
export const formatToWei = (amount: string) => utils.parseEther(amount).toString();
export const formatToEther = (amount: string) => formatEther(BigInt(amount));
export const formatToWei = (amount: string) => parseEther(amount).toString();

export const findProjectByName = (name: string): Project | undefined => {
// Project/Protocol list derived from Defillama - https://api.llama.fi/protocols
Expand Down

0 comments on commit ad06c11

Please sign in to comment.