bridge_ui: reset button in picker for tokens

fixes https://github.com/certusone/wormhole/issues/362

fixes https://github.com/certusone/wormhole/issues/394

Change-Id: Ib3619ac95e1bfda4b5d1b58840304d867f81305e
This commit is contained in:
chase-45 2021-09-08 15:57:35 -04:00 committed by Evan Gray
parent 9ea0369ab0
commit 8e251f4acc
6 changed files with 265 additions and 124 deletions

View File

@ -1,3 +1,4 @@
import { WormholeAbi__factory } from "@certusone/wormhole-sdk/lib/ethers-contracts/abi";
import { import {
CircularProgress, CircularProgress,
createStyles, createStyles,
@ -11,6 +12,7 @@ import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
import { CovalentData } from "../../hooks/useGetSourceParsedTokenAccounts"; import { CovalentData } from "../../hooks/useGetSourceParsedTokenAccounts";
import { DataWrapper } from "../../store/helpers"; import { DataWrapper } from "../../store/helpers";
import { ParsedTokenAccount } from "../../store/transferSlice"; import { ParsedTokenAccount } from "../../store/transferSlice";
import { WORMHOLE_V1_ETH_ADDRESS } from "../../utils/consts";
import { import {
ethNFTToNFTParsedTokenAccount, ethNFTToNFTParsedTokenAccount,
ethTokenToParsedTokenAccount, ethTokenToParsedTokenAccount,
@ -21,11 +23,10 @@ import {
} from "../../utils/ethereum"; } from "../../utils/ethereum";
import { shortenAddress } from "../../utils/solana"; import { shortenAddress } from "../../utils/solana";
import OffsetButton from "./OffsetButton"; import OffsetButton from "./OffsetButton";
import { WormholeAbi__factory } from "@certusone/wormhole-sdk/lib/ethers-contracts/abi";
import { WORMHOLE_V1_ETH_ADDRESS } from "../../utils/consts";
import { NFTParsedTokenAccount } from "../../store/nftSlice"; import { NFTParsedTokenAccount } from "../../store/nftSlice";
import NFTViewer from "./NFTViewer"; import NFTViewer from "./NFTViewer";
import { useDebounce } from "use-debounce/lib"; import { useDebounce } from "use-debounce/lib";
import RefreshButtonWrapper from "./RefreshButtonWrapper";
const useStyles = makeStyles(() => const useStyles = makeStyles(() =>
createStyles({ createStyles({
@ -56,6 +57,7 @@ type EthereumSourceTokenSelectorProps = {
covalent: DataWrapper<CovalentData[]> | undefined; covalent: DataWrapper<CovalentData[]> | undefined;
tokenAccounts: DataWrapper<ParsedTokenAccount[]> | undefined; tokenAccounts: DataWrapper<ParsedTokenAccount[]> | undefined;
disabled: boolean; disabled: boolean;
resetAccounts: (() => void) | undefined;
nft?: boolean; nft?: boolean;
}; };
@ -116,7 +118,15 @@ const renderNFTAccount = (
export default function EthereumSourceTokenSelector( export default function EthereumSourceTokenSelector(
props: EthereumSourceTokenSelectorProps props: EthereumSourceTokenSelectorProps
) { ) {
const { value, onChange, covalent, tokenAccounts, disabled, nft } = props; const {
value,
onChange,
covalent,
tokenAccounts,
disabled,
resetAccounts,
nft,
} = props;
const classes = useStyles(); const classes = useStyles();
const [advancedMode, setAdvancedMode] = useState(false); const [advancedMode, setAdvancedMode] = useState(false);
const [advancedModeLoading, setAdvancedModeLoading] = useState(false); const [advancedModeLoading, setAdvancedModeLoading] = useState(false);
@ -139,6 +149,14 @@ export default function EthereumSourceTokenSelector(
// const wrappedTestToken = "0x8bf3c393b588bb6ad021e154654493496139f06d"; // const wrappedTestToken = "0x8bf3c393b588bb6ad021e154654493496139f06d";
// const notWrappedTestToken = "0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d"; // const notWrappedTestToken = "0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d";
const resetAccountWrapper = useCallback(() => {
setAdvancedModeHolderString("");
setAutocompleteHolder(null);
setAdvancedModeError("");
setAutocompleteError("");
resetAccounts && resetAccounts();
}, [resetAccounts]);
useEffect(() => { useEffect(() => {
//If we receive a push from our parent, usually on component mount, we set our internal value to synchronize //If we receive a push from our parent, usually on component mount, we set our internal value to synchronize
//This also kicks off the metadata load. //This also kicks off the metadata load.
@ -394,6 +412,8 @@ export default function EthereumSourceTokenSelector(
setAdvancedModeHolderString(""); setAdvancedModeHolderString("");
setAdvancedModeError(""); setAdvancedModeError("");
setAdvancedModeSymbol(""); setAdvancedModeSymbol("");
setAutocompleteHolder(null);
setAutocompleteError("");
setAdvancedMode(!advancedMode); setAdvancedMode(!advancedMode);
}; };
@ -412,7 +432,7 @@ export default function EthereumSourceTokenSelector(
autoSelect autoSelect
blurOnSelect blurOnSelect
clearOnBlur clearOnBlur
fullWidth={false} fullWidth={true}
filterOptions={nft ? filterConfigNFT : filterConfig} filterOptions={nft ? filterConfigNFT : filterConfig}
value={autocompleteHolder} value={autocompleteHolder}
onChange={(event, newValue) => { onChange={(event, newValue) => {
@ -454,15 +474,18 @@ export default function EthereumSourceTokenSelector(
})`; })`;
}} }}
/> />
{autocompleteError && (
<Typography color="error">{autocompleteError}</Typography>
)}
</> </>
); );
const advancedModeToggleButton = ( const advancedModeToggleButton = (
<OffsetButton onClick={toggleAdvancedMode} disabled={disabled}> <OffsetButton onClick={toggleAdvancedMode} disabled={disabled}>
{advancedMode ? "Toggle Token Picker" : "Toggle Override"} {advancedMode ? "Toggle Token Picker" : "Toggle Manual Entry"}
</OffsetButton>
);
const clearButton = (
<OffsetButton onClick={handleClick} disabled={disabled}>
Clear
</OffsetButton> </OffsetButton>
); );
@ -473,16 +496,12 @@ export default function EthereumSourceTokenSelector(
{nft ? ( {nft ? (
<NFTViewer symbol={symbol} value={value} /> <NFTViewer symbol={symbol} value={value} />
) : ( ) : (
<Typography>{(symbol ? symbol + " " : "") + value.mintKey}</Typography> <RefreshButtonWrapper callback={resetAccountWrapper}>
<Typography>
{(symbol ? symbol + " " : "") + value.mintKey}
</Typography>
</RefreshButtonWrapper>
)} )}
<OffsetButton onClick={handleClick} disabled={disabled}>
Clear
</OffsetButton>
{!advancedMode && autocompleteError ? (
<Typography color="error">{autocompleteError}</Typography>
) : advancedMode && advancedModeError ? (
<Typography color="error">{advancedModeError}</Typography>
) : null}
</> </>
) : advancedMode ? ( ) : advancedMode ? (
<> <>
@ -496,7 +515,11 @@ export default function EthereumSourceTokenSelector(
!isValidEthereumAddress(advancedModeHolderString)) || !isValidEthereumAddress(advancedModeHolderString)) ||
!!advancedModeError !!advancedModeError
} }
helperText={advancedModeError === "" ? undefined : advancedModeError} helperText={
advancedModeHolderString &&
!isValidEthereumAddress(advancedModeHolderString) &&
"Invalid Ethereum address"
}
disabled={disabled || advancedModeLoading} disabled={disabled || advancedModeLoading}
/> />
{nft ? ( {nft ? (
@ -515,13 +538,20 @@ export default function EthereumSourceTokenSelector(
{nft ? "Loading (this may take a while)..." : "Loading..."} {nft ? "Loading (this may take a while)..." : "Loading..."}
</Typography> </Typography>
) : ( ) : (
autoComplete <RefreshButtonWrapper callback={resetAccountWrapper}>
{autoComplete}
</RefreshButtonWrapper>
); );
return ( return (
<React.Fragment> <>
{content} {content}
{!value && advancedModeToggleButton} {!advancedMode && autocompleteError ? (
</React.Fragment> <Typography color="error">{autocompleteError}</Typography>
) : advancedMode && advancedModeError ? (
<Typography color="error">{advancedModeError}</Typography>
) : null}
{value ? clearButton : advancedModeToggleButton}
</>
); );
} }

View File

@ -0,0 +1,45 @@
import {
createStyles,
IconButton,
makeStyles,
Tooltip,
} from "@material-ui/core";
import RefreshIcon from "@material-ui/icons/Refresh";
const useStyles = makeStyles(() =>
createStyles({
inlineContentWrapper: {
display: "inline-block",
flexGrow: 1,
},
flexWrapper: {
"& > *": {
margin: ".5rem",
},
display: "flex",
},
})
);
export default function RefreshButtonWrapper({
children,
callback,
}: {
children: JSX.Element;
callback: () => any;
}) {
const classes = useStyles();
const refreshWrapper = (
<div className={classes.flexWrapper}>
<div className={classes.inlineContentWrapper}>{children}</div>
<Tooltip title="Reload Tokens">
<IconButton onClick={callback}>
<RefreshIcon />
</IconButton>
</Tooltip>
</div>
);
return refreshWrapper;
}

View File

@ -9,6 +9,7 @@ import { ParsedTokenAccount } from "../../store/transferSlice";
import { WORMHOLE_V1_MINT_AUTHORITY } from "../../utils/consts"; import { WORMHOLE_V1_MINT_AUTHORITY } from "../../utils/consts";
import { Metadata } from "../../utils/metaplex"; import { Metadata } from "../../utils/metaplex";
import { shortenAddress } from "../../utils/solana"; import { shortenAddress } from "../../utils/solana";
import RefreshButtonWrapper from "./RefreshButtonWrapper";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@ -33,15 +34,18 @@ type SolanaSourceTokenSelectorProps = {
metaplexData: any; //DataWrapper<(Metadata | undefined)[]> | undefined | null; metaplexData: any; //DataWrapper<(Metadata | undefined)[]> | undefined | null;
disabled: boolean; disabled: boolean;
mintAccounts: DataWrapper<Map<String, string | null>> | undefined; mintAccounts: DataWrapper<Map<String, string | null>> | undefined;
resetAccounts: (() => void) | undefined;
nft?: boolean; nft?: boolean;
}; };
export default function SolanaSourceTokenSelector( export default function SolanaSourceTokenSelector(
props: SolanaSourceTokenSelectorProps props: SolanaSourceTokenSelectorProps
) { ) {
const { value, onChange, disabled, nft } = props; const { value, onChange, disabled, resetAccounts, nft } = props;
const classes = useStyles(); const classes = useStyles();
const resetAccountWrapper = resetAccounts || (() => {}); //This should never happen.
const memoizedTokenMap: Map<String, TokenInfo> = useMemo(() => { const memoizedTokenMap: Map<String, TokenInfo> = useMemo(() => {
const output = new Map<String, TokenInfo>(); const output = new Map<String, TokenInfo>();
@ -245,9 +249,15 @@ export default function SolanaSourceTokenSelector(
/> />
); );
const wrappedContent = (
<RefreshButtonWrapper callback={resetAccountWrapper}>
{autoComplete}
</RefreshButtonWrapper>
);
return ( return (
<React.Fragment> <React.Fragment>
{isLoading ? <CircularProgress /> : autoComplete} {isLoading ? <CircularProgress /> : wrappedContent}
{error && <Typography color="error">{error}</Typography>} {error && <Typography color="error">{error}</Typography>}
</React.Fragment> </React.Fragment>
); );

View File

@ -91,6 +91,7 @@ export const TokenSelector = (props: TokenSelectorProps) => {
solanaTokenMap={maps?.tokenMap} solanaTokenMap={maps?.tokenMap}
metaplexData={maps?.metaplex} metaplexData={maps?.metaplex}
mintAccounts={maps?.mintAccounts} mintAccounts={maps?.mintAccounts}
resetAccounts={maps?.resetAccounts}
nft={nft} nft={nft}
/> />
) : lookupChain === CHAIN_ID_ETH ? ( ) : lookupChain === CHAIN_ID_ETH ? (
@ -99,7 +100,8 @@ export const TokenSelector = (props: TokenSelectorProps) => {
disabled={disabled} disabled={disabled}
onChange={handleOnChange} onChange={handleOnChange}
covalent={maps?.covalent || undefined} covalent={maps?.covalent || undefined}
tokenAccounts={maps?.tokenAccounts} //TODO standardize tokenAccounts={maps?.tokenAccounts}
resetAccounts={maps?.resetAccounts}
nft={nft} nft={nft}
/> />
) : lookupChain === CHAIN_ID_TERRA ? ( ) : lookupChain === CHAIN_ID_TERRA ? (
@ -108,6 +110,7 @@ export const TokenSelector = (props: TokenSelectorProps) => {
disabled={disabled} disabled={disabled}
onChange={handleOnChange} onChange={handleOnChange}
tokenMap={maps?.terraTokenMap} tokenMap={maps?.terraTokenMap}
resetAccounts={maps?.resetAccounts}
/> />
) : ( ) : (
<TextField <TextField

View File

@ -1,4 +1,5 @@
import { import {
CircularProgress,
createStyles, createStyles,
makeStyles, makeStyles,
TextField, TextField,
@ -22,6 +23,7 @@ import { ParsedTokenAccount } from "../../store/transferSlice";
import { TERRA_HOST } from "../../utils/consts"; import { TERRA_HOST } from "../../utils/consts";
import { shortenAddress } from "../../utils/solana"; import { shortenAddress } from "../../utils/solana";
import OffsetButton from "./OffsetButton"; import OffsetButton from "./OffsetButton";
import RefreshButtonWrapper from "./RefreshButtonWrapper";
const useStyles = makeStyles(() => const useStyles = makeStyles(() =>
createStyles({ createStyles({
@ -43,6 +45,7 @@ type TerraSourceTokenSelectorProps = {
onChange: (newValue: ParsedTokenAccount | null) => void; onChange: (newValue: ParsedTokenAccount | null) => void;
disabled: boolean; disabled: boolean;
tokenMap: DataWrapper<TerraTokenMap> | undefined; //TODO better type tokenMap: DataWrapper<TerraTokenMap> | undefined; //TODO better type
resetAccounts: (() => void) | undefined;
}; };
//TODO move elsewhere //TODO move elsewhere
@ -87,12 +90,28 @@ export default function TerraSourceTokenSelector(
props: TerraSourceTokenSelectorProps props: TerraSourceTokenSelectorProps
) { ) {
const classes = useStyles(); const classes = useStyles();
const { onChange, value, disabled, tokenMap } = props; const { onChange, value, disabled, tokenMap, resetAccounts } = props;
const [advancedMode, setAdvancedMode] = useState(false); const [advancedMode, setAdvancedMode] = useState(false);
const [advancedModeHolderString, setAdvancedModeHolderString] = useState(""); const [advancedModeHolderString, setAdvancedModeHolderString] = useState("");
const [advancedModeError, setAdvancedModeError] = useState(""); const [advancedModeError, setAdvancedModeError] = useState("");
const terraWallet = useConnectedWallet(); const terraWallet = useConnectedWallet();
const [autocompleteString, setAutocompleteString] = useState("");
const handleAutocompleteChange = useCallback(
(event) => {
setAutocompleteString(event?.target?.value);
},
[setAutocompleteString]
);
const resetAccountWrapper = useCallback(() => {
setAdvancedModeHolderString("");
setAdvancedModeError("");
setAutocompleteString("");
resetAccounts && resetAccounts();
}, [resetAccounts]);
const isLoading = tokenMap?.isFetching || false; const isLoading = tokenMap?.isFetching || false;
const terraTokenArray = useMemo(() => { const terraTokenArray = useMemo(() => {
@ -102,7 +121,7 @@ export default function TerraSourceTokenSelector(
}, [props.tokenMap]); }, [props.tokenMap]);
const valueToOption = (fromProps: ParsedTokenAccount | undefined | null) => { const valueToOption = (fromProps: ParsedTokenAccount | undefined | null) => {
if (!fromProps) return undefined; if (!fromProps) return null;
else { else {
return terraTokenArray.find((x) => x.token === fromProps.mintKey); return terraTokenArray.find((x) => x.token === fromProps.mintKey);
} }
@ -113,7 +132,7 @@ export default function TerraSourceTokenSelector(
}, [onChange]); }, [onChange]);
const handleOnChange = useCallback( const handleOnChange = useCallback(
(event) => setAdvancedModeHolderString(event.target.value), (event) => setAdvancedModeHolderString(event?.target?.value),
[] []
); );
@ -128,7 +147,7 @@ export default function TerraSourceTokenSelector(
onChange(result); onChange(result);
}, },
(error) => { (error) => {
setAdvancedModeError("Unable to retrieve this address."); setAdvancedModeError("Unable to retrieve that address.");
} }
); );
setAdvancedModeError(""); setAdvancedModeError("");
@ -172,10 +191,12 @@ export default function TerraSourceTokenSelector(
const advancedModeToggleButton = ( const advancedModeToggleButton = (
<OffsetButton onClick={toggleAdvancedMode} disabled={disabled}> <OffsetButton onClick={toggleAdvancedMode} disabled={disabled}>
{advancedMode ? "Toggle Token Picker" : "Toggle Override"} {advancedMode ? "Toggle Token Picker" : "Toggle Manual Entry"}
</OffsetButton> </OffsetButton>
); );
const selectedValue = valueToOption(value);
const autoComplete = ( const autoComplete = (
<> <>
<Autocomplete <Autocomplete
@ -186,10 +207,12 @@ export default function TerraSourceTokenSelector(
clearOnBlur clearOnBlur
fullWidth={false} fullWidth={false}
filterOptions={filterConfig} filterOptions={filterConfig}
value={valueToOption(value)} value={selectedValue}
onChange={(event, newValue) => { onChange={(event, newValue) => {
handleConfirm(newValue?.token); handleConfirm(newValue?.token);
}} }}
inputValue={autocompleteString}
onInputChange={handleAutocompleteChange}
disabled={disabled} disabled={disabled}
noOptionsText={"No CW20 tokens found at the moment."} noOptionsText={"No CW20 tokens found at the moment."}
options={terraTokenArray} options={terraTokenArray}
@ -199,18 +222,18 @@ export default function TerraSourceTokenSelector(
renderOption={renderOption} renderOption={renderOption}
getOptionLabel={renderOptionLabel} getOptionLabel={renderOptionLabel}
/> />
{advancedModeError && (
<Typography color="error">{advancedModeError}</Typography>
)}
</> </>
); );
const clearButton = (
<OffsetButton onClick={handleClick} disabled={disabled}>
Clear
</OffsetButton>
);
const content = value ? ( const content = value ? (
<> <>
<Typography>{value.mintKey}</Typography> <Typography>{value.mintKey}</Typography>
<OffsetButton onClick={handleClick} disabled={disabled}>
Clear
</OffsetButton>
</> </>
) : !advancedMode ? ( ) : !advancedMode ? (
autoComplete autoComplete
@ -223,21 +246,37 @@ export default function TerraSourceTokenSelector(
onChange={handleOnChange} onChange={handleOnChange}
disabled={disabled} disabled={disabled}
error={advancedModeHolderString !== "" && !!advancedModeError} error={advancedModeHolderString !== "" && !!advancedModeError}
helperText={advancedModeError === "" ? undefined : advancedModeError}
/> />
<OffsetButton
onClick={() => handleConfirm(advancedModeHolderString)}
disabled={disabled}
>
Confirm
</OffsetButton>
</> </>
); );
const wrappedContent = (
<RefreshButtonWrapper callback={resetAccountWrapper}>
{content}
</RefreshButtonWrapper>
);
const confirmButton = (
<OffsetButton
onClick={() => handleConfirm(advancedModeHolderString)}
disabled={disabled}
>
Confirm
</OffsetButton>
);
return ( return (
<React.Fragment> <React.Fragment>
{content} {isLoading && <CircularProgress />}
{!value && !isLoading && advancedModeToggleButton} {wrappedContent}
{advancedModeError && (
<Typography color="error">{advancedModeError}</Typography>
)}
<div>
{advancedMode && !value && confirmButton}
{!value && !isLoading && advancedModeToggleButton}
{value && clearButton}
</div>
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -14,7 +14,7 @@ import {
} from "@solana/web3.js"; } from "@solana/web3.js";
import axios from "axios"; import axios from "axios";
import { formatUnits } from "ethers/lib/utils"; import { formatUnits } from "ethers/lib/utils";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext";
@ -381,6 +381,28 @@ function useGetAvailableTokens(nft: boolean = false) {
? solPK?.toString() ? solPK?.toString()
: undefined; : undefined;
const resetSourceAccounts = useCallback(() => {
dispatch(
nft
? setSourceWalletAddressNFT(undefined)
: setSourceWalletAddress(undefined)
);
dispatch(
nft
? setSourceParsedTokenAccountNFT(undefined)
: setSourceParsedTokenAccount(undefined)
);
dispatch(
nft
? setSourceParsedTokenAccountsNFT(undefined)
: setSourceParsedTokenAccounts(undefined)
);
!nft && dispatch(setAmount(""));
setCovalent(undefined); //These need to be included in the reset because they have balances on them.
setCovalentLoading(false);
setCovalentError("");
}, [setCovalent, dispatch, nft]);
//TODO this useEffect could be somewhere else in the codebase //TODO this useEffect could be somewhere else in the codebase
//It resets the SourceParsedTokens accounts when the wallet changes //It resets the SourceParsedTokens accounts when the wallet changes
useEffect(() => { useEffect(() => {
@ -389,26 +411,16 @@ function useGetAvailableTokens(nft: boolean = false) {
currentSourceWalletAddress !== undefined && currentSourceWalletAddress !== undefined &&
currentSourceWalletAddress !== selectedSourceWalletAddress currentSourceWalletAddress !== selectedSourceWalletAddress
) { ) {
dispatch( resetSourceAccounts();
nft
? setSourceWalletAddressNFT(undefined)
: setSourceWalletAddress(undefined)
);
dispatch(
nft
? setSourceParsedTokenAccountNFT(undefined)
: setSourceParsedTokenAccount(undefined)
);
dispatch(
nft
? setSourceParsedTokenAccountsNFT(undefined)
: setSourceParsedTokenAccounts(undefined)
);
!nft && dispatch(setAmount(""));
return; return;
} else { } else {
} }
}, [selectedSourceWalletAddress, currentSourceWalletAddress, dispatch, nft]); }, [
selectedSourceWalletAddress,
currentSourceWalletAddress,
dispatch,
resetSourceAccounts,
]);
// Solana metaplex load // Solana metaplex load
useEffect(() => { useEffect(() => {
@ -526,69 +538,68 @@ function useGetAvailableTokens(nft: boolean = false) {
// const nftTestWallet2 = "0x98ed231428088eb440e8edb5cc8d66dcf913b86e"; // const nftTestWallet2 = "0x98ed231428088eb440e8edb5cc8d66dcf913b86e";
let cancelled = false; let cancelled = false;
const walletAddress = signerAddress; const walletAddress = signerAddress;
if (!walletAddress || lookupChain !== CHAIN_ID_ETH) { if (walletAddress && lookupChain === CHAIN_ID_ETH && !tokenAccounts.data) {
return; //TODO less cancel
} !cancelled && setCovalentLoading(true);
//TODO less cancel !cancelled &&
!cancelled && setCovalentLoading(true); dispatch(
!cancelled && nft
dispatch( ? fetchSourceParsedTokenAccountsNFT()
nft : fetchSourceParsedTokenAccounts()
? fetchSourceParsedTokenAccountsNFT() );
: fetchSourceParsedTokenAccounts() getEthereumAccountsCovalent(walletAddress, nft).then(
); (accounts) => {
getEthereumAccountsCovalent(walletAddress, nft).then( !cancelled && setCovalentLoading(false);
(accounts) => { !cancelled && setCovalentError(undefined);
!cancelled && setCovalentLoading(false); !cancelled && setCovalent(accounts);
!cancelled && setCovalentError(undefined); !cancelled &&
!cancelled && setCovalent(accounts); dispatch(
!cancelled && nft
dispatch( ? receiveSourceParsedTokenAccountsNFT(
nft accounts.reduce((arr, current) => {
? receiveSourceParsedTokenAccountsNFT( if (current.nft_data) {
accounts.reduce((arr, current) => { current.nft_data.forEach((x) =>
if (current.nft_data) { arr.push(
current.nft_data.forEach((x) => createNFTParsedTokenAccountFromCovalent(
arr.push( walletAddress,
createNFTParsedTokenAccountFromCovalent( current,
walletAddress, x
current, )
x
) )
) );
); }
} return arr;
return arr; }, [] as NFTParsedTokenAccount[])
}, [] as NFTParsedTokenAccount[])
)
: receiveSourceParsedTokenAccounts(
accounts.map((x) =>
createParsedTokenAccountFromCovalent(walletAddress, x)
) )
) : receiveSourceParsedTokenAccounts(
); accounts.map((x) =>
}, createParsedTokenAccountFromCovalent(walletAddress, x)
() => { )
!cancelled && )
dispatch( );
nft },
? errorSourceParsedTokenAccountsNFT( () => {
"Cannot load your Ethereum NFTs at the moment." !cancelled &&
) dispatch(
: errorSourceParsedTokenAccounts( nft
"Cannot load your Ethereum tokens at the moment." ? errorSourceParsedTokenAccountsNFT(
) "Cannot load your Ethereum NFTs at the moment."
); )
!cancelled && : errorSourceParsedTokenAccounts(
setCovalentError("Cannot load your Ethereum tokens at the moment."); "Cannot load your Ethereum tokens at the moment."
!cancelled && setCovalentLoading(false); )
} );
); !cancelled &&
setCovalentError("Cannot load your Ethereum tokens at the moment.");
!cancelled && setCovalentLoading(false);
}
);
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [lookupChain, provider, signerAddress, dispatch, nft]); }
}, [lookupChain, provider, signerAddress, dispatch, nft, tokenAccounts.data]);
//Terra accounts load //Terra accounts load
//At present, we don't have any mechanism for doing this. //At present, we don't have any mechanism for doing this.
@ -640,6 +651,7 @@ function useGetAvailableTokens(nft: boolean = false) {
error: solanaMintAccountsError, error: solanaMintAccountsError,
receivedAt: null, //TODO receivedAt: null, //TODO
}, },
resetAccounts: resetSourceAccounts,
} }
: lookupChain === CHAIN_ID_ETH : lookupChain === CHAIN_ID_ETH
? { ? {
@ -650,10 +662,12 @@ function useGetAvailableTokens(nft: boolean = false) {
error: covalentError, error: covalentError,
receivedAt: null, //TODO receivedAt: null, //TODO
}, },
resetAccounts: resetSourceAccounts,
} }
: lookupChain === CHAIN_ID_TERRA : lookupChain === CHAIN_ID_TERRA
? { ? {
terraTokenMap: terraTokenMap, terraTokenMap: terraTokenMap,
resetAccounts: resetSourceAccounts,
} }
: undefined; : undefined;
} }