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:
parent
9ea0369ab0
commit
8e251f4acc
|
@ -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}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue