bridge_ui: evm token account fetch on selection

Change-Id: Ia9c9c54d61ab90b40616472628238ec1b5e00c0e
This commit is contained in:
Chase Moran 2021-11-19 00:19:14 -05:00 committed by Evan Gray
parent 5ed2b2a06d
commit b783a44fb0
2 changed files with 80 additions and 10 deletions

View File

@ -7,10 +7,15 @@ import {
import { WormholeAbi__factory } from "@certusone/wormhole-sdk/lib/ethers-contracts/abi"; import { WormholeAbi__factory } from "@certusone/wormhole-sdk/lib/ethers-contracts/abi";
import { getAddress as getEthAddress } from "@ethersproject/address"; import { getAddress as getEthAddress } from "@ethersproject/address";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useSelector } from "react-redux";
import { useEthereumProvider } from "../../contexts/EthereumProviderContext"; import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
import useIsWalletReady from "../../hooks/useIsWalletReady"; import useIsWalletReady from "../../hooks/useIsWalletReady";
import { DataWrapper } from "../../store/helpers"; import { DataWrapper } from "../../store/helpers";
import { NFTParsedTokenAccount } from "../../store/nftSlice"; import { NFTParsedTokenAccount } from "../../store/nftSlice";
import {
selectNFTSourceParsedTokenAccount,
selectTransferSourceParsedTokenAccount,
} from "../../store/selectors";
import { ParsedTokenAccount } from "../../store/transferSlice"; import { ParsedTokenAccount } from "../../store/transferSlice";
import { import {
getMigrationAssetMap, getMigrationAssetMap,
@ -60,6 +65,29 @@ export default function EvmTokenPicker(
} = props; } = props;
const { provider, signerAddress } = useEthereumProvider(); const { provider, signerAddress } = useEthereumProvider();
const { isReady } = useIsWalletReady(chainId); const { isReady } = useIsWalletReady(chainId);
const selectedTokenAccount: NFTParsedTokenAccount | undefined = useSelector(
nft
? selectNFTSourceParsedTokenAccount
: selectTransferSourceParsedTokenAccount
);
const shouldDisplayBalance = useCallback(
(tokenAccount: NFTParsedTokenAccount) => {
const selectedMintMatch =
selectedTokenAccount &&
selectedTokenAccount.mintKey.toLowerCase() ===
tokenAccount.mintKey.toLowerCase();
//added just in case we start displaying NFT balances again.
const selectedTokenIdMatch =
selectedTokenAccount &&
selectedTokenAccount.tokenId === tokenAccount.tokenId;
return !!(
tokenAccount.isNativeAsset || //The native asset amount isn't taken from covalent, so can be trusted.
(selectedMintMatch && selectedTokenIdMatch)
);
},
[selectedTokenAccount]
);
const isMigrationEligible = useCallback( const isMigrationEligible = useCallback(
(address: string) => { (address: string) => {
@ -118,23 +146,48 @@ export default function EvmTokenPicker(
} catch (e) { } catch (e) {
//For now, just swallow this one. //For now, just swallow this one.
} }
let newAccount = null;
try {
//Covalent balances tend to be stale, so we make an attempt to correct it at selection time.
if (!account.isNativeAsset) {
newAccount = await getAddress(account.mintKey, account.tokenId);
newAccount = { ...account, ...newAccount }; //We spread over the old account so we don't lose the logo, uri, or other useful info we got from covalent.
} else {
newAccount = account;
}
} catch (e) {
//swallow
console.log(e);
}
if (!newAccount) {
//Must reject otherwise downstream checks relying on the balance may fail.
//An error is thrown so that the code above us will display the message.
throw new Error(
"Unable to retrieve required information about this token. Ensure your wallet is connected, then refresh the list."
);
}
const migration = isMigrationEligible(account.publicKey); const migration = isMigrationEligible(account.publicKey);
if (v1 === true && !migration) { if (v1 === true && !migration) {
return Promise.reject( throw new Error(
"Wormhole v1 assets cannot be transferred with this bridge." "Wormhole v1 assets cannot be transferred with this bridge."
); );
} }
onChange(account); onChange(newAccount);
return Promise.resolve(); return Promise.resolve();
}, },
[chainId, onChange, provider, isMigrationEligible] [chainId, onChange, provider, isMigrationEligible, getAddress]
); );
const RenderComp = useCallback( const RenderComp = useCallback(
({ account }: { account: NFTParsedTokenAccount }) => { ({ account }: { account: NFTParsedTokenAccount }) => {
return BasicAccountRender(account, isMigrationEligible, nft || false); return BasicAccountRender(
account,
isMigrationEligible,
nft || false,
shouldDisplayBalance
);
}, },
[nft, isMigrationEligible] [nft, isMigrationEligible, shouldDisplayBalance]
); );
return ( return (

View File

@ -110,7 +110,8 @@ export const balancePretty = (uiString: string) => {
export const BasicAccountRender = ( export const BasicAccountRender = (
account: NFTParsedTokenAccount, account: NFTParsedTokenAccount,
isMigrationEligible: (address: string) => boolean, isMigrationEligible: (address: string) => boolean,
nft: boolean nft: boolean,
displayBalance?: (account: NFTParsedTokenAccount) => boolean
) => { ) => {
const classes = useStyles(); const classes = useStyles();
const mintPrettyString = shortenAddress(account.mintKey); const mintPrettyString = shortenAddress(account.mintKey);
@ -118,7 +119,7 @@ export const BasicAccountRender = (
const symbol = account.symbol || "Unknown"; const symbol = account.symbol || "Unknown";
const name = account.name || "Unknown"; const name = account.name || "Unknown";
const tokenId = account.tokenId; const tokenId = account.tokenId;
const balancePrettyString = balancePretty(account.uiAmountString); const shouldDisplayBalance = !displayBalance || displayBalance(account);
const nftContent = ( const nftContent = (
<div className={classes.tokenOverviewContainer}> <div className={classes.tokenOverviewContainer}>
@ -152,8 +153,16 @@ export const BasicAccountRender = (
} }
</div> </div>
<div> <div>
{shouldDisplayBalance ? (
<>
<Typography variant="body2">{"Balance"}</Typography> <Typography variant="body2">{"Balance"}</Typography>
<Typography variant="h6">{balancePrettyString}</Typography> <Typography variant="h6">
{balancePretty(account.uiAmountString)}
</Typography>
</>
) : (
<div />
)}
</div> </div>
</div> </div>
); );
@ -222,6 +231,7 @@ export default function TokenPicker({
const openDialog = useCallback(() => { const openDialog = useCallback(() => {
setHolderString(""); setHolderString("");
setSelectionError("");
setDialogIsOpen(true); setDialogIsOpen(true);
}, []); }, []);
@ -244,6 +254,13 @@ export default function TokenPicker({
[onChange, closeDialog] [onChange, closeDialog]
); );
const resetAccountsWrapper = useCallback(() => {
setHolderString("");
setTokenIdHolderString("");
setSelectionError("");
resetAccounts && resetAccounts();
}, [resetAccounts]);
const filteredOptions = useMemo(() => { const filteredOptions = useMemo(() => {
return options.filter((option: NFTParsedTokenAccount) => { return options.filter((option: NFTParsedTokenAccount) => {
if (!holderString) { if (!holderString) {
@ -359,7 +376,7 @@ export default function TokenPicker({
<Typography variant="h5">Select a token</Typography> <Typography variant="h5">Select a token</Typography>
<div className={classes.grower} /> <div className={classes.grower} />
<Tooltip title="Reload tokens"> <Tooltip title="Reload tokens">
<IconButton onClick={resetAccounts}> <IconButton onClick={resetAccountsWrapper}>
<RefreshIcon /> <RefreshIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>