bridge_ui: basic gas fees warning
Change-Id: I7bf78f45d475de9ef0a2a1f372790d2b43322f36
This commit is contained in:
parent
b39d72e32f
commit
01d84dc7ba
|
@ -15,6 +15,7 @@ import {
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
|
@ -68,6 +69,7 @@ function Source() {
|
||||||
onChange={handleAssetChange}
|
onChange={handleAssetChange}
|
||||||
disabled={shouldLockFields}
|
disabled={shouldLockFields}
|
||||||
/>
|
/>
|
||||||
|
<LowBalanceWarning chainId={sourceChain} />
|
||||||
<ButtonWithLoader
|
<ButtonWithLoader
|
||||||
disabled={!isSourceComplete}
|
disabled={!isSourceComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { MenuItem, TextField } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { incrementStep, setTargetChain } from "../../store/attestSlice";
|
import { incrementStep, setTargetChain } from "../../store/attestSlice";
|
||||||
|
@ -8,11 +9,20 @@ import {
|
||||||
selectAttestSourceChain,
|
selectAttestSourceChain,
|
||||||
selectAttestTargetChain,
|
selectAttestTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
alert: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
function Target() {
|
function Target() {
|
||||||
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectAttestSourceChain);
|
const sourceChain = useSelector(selectAttestSourceChain);
|
||||||
const chains = useMemo(
|
const chains = useMemo(
|
||||||
|
@ -47,6 +57,11 @@ function Target() {
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
<KeyAndBalance chainId={targetChain} />
|
<KeyAndBalance chainId={targetChain} />
|
||||||
|
<Alert severity="info" className={classes.alert}>
|
||||||
|
You will have to pay transaction fees on{" "}
|
||||||
|
{CHAINS_BY_ID[targetChain].name} to attest this token.
|
||||||
|
</Alert>
|
||||||
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
<ButtonWithLoader
|
<ButtonWithLoader
|
||||||
disabled={!isTargetComplete}
|
disabled={!isTargetComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
|
import { Typography } from "@material-ui/core";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
|
import { makeStyles } from "@material-ui/core";
|
||||||
|
import useTransactionFees from "../hooks/useTransactionFees";
|
||||||
|
import useIsWalletReady from "../hooks/useIsWalletReady";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
alert: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
function LowBalanceWarning({ chainId }: { chainId: ChainId }) {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { isReady } = useIsWalletReady(chainId);
|
||||||
|
const transactionFeeWarning = useTransactionFees(chainId);
|
||||||
|
const displayWarning =
|
||||||
|
isReady &&
|
||||||
|
transactionFeeWarning.balanceString &&
|
||||||
|
transactionFeeWarning.isSufficientBalance === false;
|
||||||
|
const warningMessage = `This wallet has a very low ${
|
||||||
|
chainId === CHAIN_ID_SOLANA ? "SOL" : chainId === CHAIN_ID_ETH ? "ETH" : ""
|
||||||
|
} balance and may not be able to pay for the upcoming transaction fees.`;
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<Alert severity="warning" className={classes.alert}>
|
||||||
|
<Typography variant="body1">{warningMessage}</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{"Current balance: " + transactionFeeWarning.balanceString}
|
||||||
|
</Typography>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
|
||||||
|
return displayWarning ? content : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LowBalanceWarning;
|
|
@ -29,6 +29,7 @@ import {
|
||||||
signSendAndConfirm,
|
signSendAndConfirm,
|
||||||
} from "../../utils/solana";
|
} from "../../utils/solana";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
import ShowTx from "../ShowTx";
|
import ShowTx from "../ShowTx";
|
||||||
import SolanaCreateAssociatedAddress, {
|
import SolanaCreateAssociatedAddress, {
|
||||||
useAssociatedAccountExistsState,
|
useAssociatedAccountExistsState,
|
||||||
|
@ -402,6 +403,7 @@ export default function Workflow({
|
||||||
<Divider className={classes.divider} />
|
<Divider className={classes.divider} />
|
||||||
|
|
||||||
<SolanaWalletKey />
|
<SolanaWalletKey />
|
||||||
|
<LowBalanceWarning chainId={CHAIN_ID_SOLANA} />
|
||||||
{fromTokenAccount && toTokenAccount && fromTokenAccountBalance ? (
|
{fromTokenAccount && toTokenAccount && fromTokenAccountBalance ? (
|
||||||
<>
|
<>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
|
|
|
@ -18,6 +18,7 @@ import KeyAndBalance from "../KeyAndBalance";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
|
@ -89,6 +90,7 @@ function Source({
|
||||||
<TokenSelector disabled={shouldLockFields} nft={true} />
|
<TokenSelector disabled={shouldLockFields} nft={true} />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
<LowBalanceWarning chainId={sourceChain} />
|
||||||
<ButtonWithLoader
|
<ButtonWithLoader
|
||||||
disabled={!isSourceComplete}
|
disabled={!isSourceComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
|
|
@ -18,15 +18,21 @@ import {
|
||||||
selectNFTTargetError,
|
selectNFTTargetError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
import { hexToNativeString } from "../../utils/array";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
marginTop: theme.spacing(5),
|
marginTop: theme.spacing(5),
|
||||||
},
|
},
|
||||||
|
alert: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Target() {
|
function Target() {
|
||||||
|
@ -104,6 +110,11 @@ function Target() {
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
<Alert severity="info" className={classes.alert}>
|
||||||
|
You will have to pay transaction fees on{" "}
|
||||||
|
{CHAINS_BY_ID[targetChain].name} to redeem your NFT.
|
||||||
|
</Alert>
|
||||||
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
<ButtonWithLoader
|
<ButtonWithLoader
|
||||||
disabled={!isTargetComplete} //|| !associatedAccountExists}
|
disabled={!isTargetComplete} //|| !associatedAccountExists}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
import { CHAINS, MIGRATION_ASSET_MAP } from "../../utils/consts";
|
import { CHAINS, MIGRATION_ASSET_MAP } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
import TokenBlacklistWarning from "./TokenBlacklistWarning";
|
import TokenBlacklistWarning from "./TokenBlacklistWarning";
|
||||||
|
@ -125,6 +126,7 @@ function Source({
|
||||||
tokenAddress={parsedTokenAccount?.mintKey}
|
tokenAddress={parsedTokenAccount?.mintKey}
|
||||||
symbol={parsedTokenAccount?.symbol}
|
symbol={parsedTokenAccount?.symbol}
|
||||||
/>
|
/>
|
||||||
|
<LowBalanceWarning chainId={sourceChain} />
|
||||||
{hasParsedTokenAccount ? (
|
{hasParsedTokenAccount ? (
|
||||||
<TextField
|
<TextField
|
||||||
label="Amount"
|
label="Amount"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||||
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||||
|
@ -17,9 +18,10 @@ import {
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
import { hexToNativeString } from "../../utils/array";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
import SolanaCreateAssociatedAddress, {
|
import SolanaCreateAssociatedAddress, {
|
||||||
useAssociatedAccountExistsState,
|
useAssociatedAccountExistsState,
|
||||||
} from "../SolanaCreateAssociatedAddress";
|
} from "../SolanaCreateAssociatedAddress";
|
||||||
|
@ -30,6 +32,10 @@ const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
marginTop: theme.spacing(5),
|
marginTop: theme.spacing(5),
|
||||||
},
|
},
|
||||||
|
alert: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function Target() {
|
function Target() {
|
||||||
|
@ -105,6 +111,11 @@ function Target() {
|
||||||
value={targetAsset || ""}
|
value={targetAsset || ""}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
|
<Alert severity="info" className={classes.alert}>
|
||||||
|
You will have to pay transaction fees on{" "}
|
||||||
|
{CHAINS_BY_ID[targetChain].name} to redeem your tokens.
|
||||||
|
</Alert>
|
||||||
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
<ButtonWithLoader
|
<ButtonWithLoader
|
||||||
disabled={!isTargetComplete || !associatedAccountExists}
|
disabled={!isTargetComplete || !associatedAccountExists}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
Bridge__factory,
|
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
|
@ -51,7 +50,6 @@ import {
|
||||||
} from "../store/transferSlice";
|
} from "../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
COVALENT_GET_TOKENS_URL,
|
COVALENT_GET_TOKENS_URL,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
WETH_ADDRESS,
|
WETH_ADDRESS,
|
||||||
WETH_DECIMALS,
|
WETH_DECIMALS,
|
||||||
|
@ -318,7 +316,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
||||||
);
|
);
|
||||||
const solanaWallet = useSolanaWallet();
|
const solanaWallet = useSolanaWallet();
|
||||||
const solPK = solanaWallet?.publicKey;
|
const solPK = solanaWallet?.publicKey;
|
||||||
const { provider, signer, signerAddress } = useEthereumProvider();
|
const { provider, signerAddress } = useEthereumProvider();
|
||||||
|
|
||||||
const [covalent, setCovalent] = useState<any>(undefined);
|
const [covalent, setCovalent] = useState<any>(undefined);
|
||||||
const [covalentLoading, setCovalentLoading] = useState(false);
|
const [covalentLoading, setCovalentLoading] = useState(false);
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
CHAIN_ID_TERRA,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
|
import { Provider } from "@certusone/wormhole-sdk/node_modules/@ethersproject/abstract-provider";
|
||||||
|
import { formatUnits } from "@ethersproject/units";
|
||||||
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
|
import { SOLANA_HOST } from "../utils/consts";
|
||||||
|
import { getMultipleAccountsRPC } from "../utils/solana";
|
||||||
|
import useIsWalletReady from "./useIsWalletReady";
|
||||||
|
|
||||||
|
//It's difficult to project how many fees the user will accrue during the
|
||||||
|
//workflow, as a variable number of transactions can be sent, and different
|
||||||
|
//execution paths can be hit in the smart contracts, altering gas used.
|
||||||
|
//As such, for the moment it is best to just check for a reasonable 'low balance' threshold.
|
||||||
|
//Still it would be good to calculate a reasonable value at runtime based off current gas prices,
|
||||||
|
//rather than a hardcoded value.
|
||||||
|
const SOLANA_THRESHOLD_LAMPORTS: bigint = BigInt(300000);
|
||||||
|
const ETHEREUM_THRESHOLD_WEI: bigint = BigInt(35000000000000000);
|
||||||
|
|
||||||
|
const isSufficientBalance = (chainId: ChainId, balance: bigint | undefined) => {
|
||||||
|
if (balance === undefined || !chainId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (CHAIN_ID_SOLANA === chainId) {
|
||||||
|
return balance > SOLANA_THRESHOLD_LAMPORTS;
|
||||||
|
}
|
||||||
|
if (CHAIN_ID_ETH === chainId) {
|
||||||
|
return balance > ETHEREUM_THRESHOLD_WEI;
|
||||||
|
}
|
||||||
|
if (CHAIN_ID_TERRA === chainId) {
|
||||||
|
//Terra is complicated because the fees can be paid in multiple currencies.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO move to more generic location
|
||||||
|
const getBalanceSolana = async (walletAddress: string) => {
|
||||||
|
const connection = new Connection(SOLANA_HOST);
|
||||||
|
return getMultipleAccountsRPC(connection, [
|
||||||
|
new PublicKey(walletAddress),
|
||||||
|
]).then(
|
||||||
|
(results) => {
|
||||||
|
if (results.length && results[0]) {
|
||||||
|
return BigInt(results[0].lamports);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return BigInt(0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBalanceEth = async (walletAddress: string, provider: Provider) => {
|
||||||
|
return provider.getBalance(walletAddress).then((result) => result.toBigInt());
|
||||||
|
};
|
||||||
|
|
||||||
|
const toBalanceString = (balance: bigint | undefined, chainId: ChainId) => {
|
||||||
|
if (!chainId || balance === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (chainId === CHAIN_ID_ETH) {
|
||||||
|
return formatUnits(balance, 18); //wei decimals
|
||||||
|
} else if (chainId === CHAIN_ID_SOLANA) {
|
||||||
|
return formatUnits(balance, 9); //lamports to sol decmals
|
||||||
|
} else return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useTransactionFees(chainId: ChainId) {
|
||||||
|
const { walletAddress, isReady } = useIsWalletReady(chainId);
|
||||||
|
const { provider } = useEthereumProvider();
|
||||||
|
const [balance, setBalance] = useState<bigint | undefined>(undefined);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
const loadStart = useCallback(() => {
|
||||||
|
setBalance(undefined);
|
||||||
|
setIsLoading(true);
|
||||||
|
setError("");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (chainId === CHAIN_ID_SOLANA && isReady && walletAddress) {
|
||||||
|
loadStart();
|
||||||
|
getBalanceSolana(walletAddress).then(
|
||||||
|
(result) => {
|
||||||
|
const adjustedresult =
|
||||||
|
result === undefined || result === null ? BigInt(0) : result;
|
||||||
|
setIsLoading(false);
|
||||||
|
setBalance(adjustedresult);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
setIsLoading(false);
|
||||||
|
setError("Cannot load wallet balance");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else if (chainId === CHAIN_ID_ETH && isReady && walletAddress) {
|
||||||
|
if (provider) {
|
||||||
|
loadStart();
|
||||||
|
getBalanceEth(walletAddress, provider).then(
|
||||||
|
(result) => {
|
||||||
|
const adjustedresult =
|
||||||
|
result === undefined || result === null ? BigInt(0) : result;
|
||||||
|
setIsLoading(false);
|
||||||
|
setBalance(adjustedresult);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
setIsLoading(false);
|
||||||
|
setError("Cannot load wallet balance");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [provider, walletAddress, isReady, chainId, loadStart]);
|
||||||
|
|
||||||
|
const results = useMemo(() => {
|
||||||
|
return {
|
||||||
|
isSufficientBalance: isSufficientBalance(chainId, balance),
|
||||||
|
balance,
|
||||||
|
balanceString: toBalanceString(balance, chainId),
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}, [balance, chainId, isLoading, error]);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
Loading…
Reference in New Issue