Allow adding erc20 tokens via contract address

This commit is contained in:
Gary Wang 2020-09-17 23:55:14 -07:00
parent cfc57a0a37
commit 053a85da38
2 changed files with 83 additions and 25 deletions

View File

@ -30,6 +30,8 @@ import {
import Link from '@material-ui/core/Link'; import Link from '@material-ui/core/Link';
import CopyableDisplay from './CopyableDisplay'; import CopyableDisplay from './CopyableDisplay';
import DialogForm from './DialogForm'; import DialogForm from './DialogForm';
import { showSwapAddress } from '../utils/config';
import { swapApiRequest } from '../utils/swap/api';
const feeFormat = new Intl.NumberFormat(undefined, { const feeFormat = new Intl.NumberFormat(undefined, {
minimumFractionDigits: 6, minimumFractionDigits: 6,
@ -40,7 +42,6 @@ const useStyles = makeStyles((theme) => ({
tabs: { tabs: {
marginBottom: theme.spacing(1), marginBottom: theme.spacing(1),
borderBottom: `1px solid ${theme.palette.background.paper}`, borderBottom: `1px solid ${theme.palette.background.paper}`,
width: '100%',
}, },
})); }));
@ -52,15 +53,16 @@ export default function AddTokenDialog({ open, onClose }) {
); );
let classes = useStyles(); let classes = useStyles();
let updateTokenName = useUpdateTokenName(); let updateTokenName = useUpdateTokenName();
const [sendTransaction, sending] = useSendTransaction();
let [mintAddress, setMintAddress] = useState('');
let [tokenName, setTokenName] = useState('');
let [tokenSymbol, setTokenSymbol] = useState('');
let [sendTransaction, sending] = useSendTransaction();
const { endpoint } = useConnectionConfig(); const { endpoint } = useConnectionConfig();
const popularTokens = TOKENS[endpoint]; const popularTokens = TOKENS[endpoint];
const [walletAccounts] = useWalletTokenAccounts(); const [walletAccounts] = useWalletTokenAccounts();
const [tab, setTab] = useState(!!popularTokens ? 'popular' : 'manual'); const [tab, setTab] = useState(!!popularTokens ? 'popular' : 'manual');
const [mintAddress, setMintAddress] = useState('');
const [tokenName, setTokenName] = useState('');
const [tokenSymbol, setTokenSymbol] = useState('');
const [erc20Address, setErc20Address] = useState('');
useEffect(() => { useEffect(() => {
if (!popularTokens) { if (!popularTokens) {
@ -68,17 +70,46 @@ export default function AddTokenDialog({ open, onClose }) {
} }
}, [popularTokens]); }, [popularTokens]);
function onSubmit({ mintAddress, tokenName, tokenSymbol }) { function onSubmit(params) {
let mint = new PublicKey(mintAddress); if (tab === 'manual') {
sendTransaction(wallet.createTokenAccount(mint), { params = { mintAddress, tokenName, tokenSymbol };
} else if (tab === 'erc20') {
params = { erc20Address };
}
sendTransaction(addToken(params), {
onSuccess: () => { onSuccess: () => {
updateTokenName(mint, tokenName, tokenSymbol);
refreshWalletPublicKeys(wallet); refreshWalletPublicKeys(wallet);
onClose(); onClose();
}, },
}); });
} }
async function addToken({
mintAddress,
tokenName,
tokenSymbol,
erc20Address,
}) {
if (erc20Address) {
let tokenInfo = await swapApiRequest('POST', `coins/eth/${erc20Address}`);
mintAddress = tokenInfo.splMint;
tokenName = tokenInfo.name;
tokenSymbol = tokenInfo.ticker;
if (tokenInfo.blockchain !== 'sol') {
tokenName = 'Wrapped ' + tokenName;
}
}
let mint = new PublicKey(mintAddress);
updateTokenName(mint, tokenName, tokenSymbol);
return await wallet.createTokenAccount(mint);
}
let valid = true;
if (tab === 'erc20') {
valid = erc20Address.length === 42 && erc20Address.startsWith('0x');
}
return ( return (
<DialogForm open={open} onClose={onClose}> <DialogForm open={open} onClose={onClose}>
<DialogTitle>Add Token</DialogTitle> <DialogTitle>Add Token</DialogTitle>
@ -94,22 +125,14 @@ export default function AddTokenDialog({ open, onClose }) {
{!!popularTokens && ( {!!popularTokens && (
<Tabs <Tabs
value={tab} value={tab}
variant="standard"
textColor="primary" textColor="primary"
indicatorColor="primary" indicatorColor="primary"
className={classes.tabs} className={classes.tabs}
onChange={(e, value) => setTab(value)} onChange={(e, value) => setTab(value)}
> >
<Tab <Tab label="Popular Tokens" value="popular" />
label={'Popular Tokens'} {showSwapAddress ? <Tab label="ERC20 Token" value="erc20" /> : null}
value="popular" <Tab label="Manual Input" value="manual" />
style={{ textDecoration: 'none', width: '50%' }}
/>
<Tab
label={'Manual Input'}
value="manual"
style={{ textDecoration: 'none', width: '50%' }}
/>
</Tabs> </Tabs>
)} )}
{tab === 'manual' || !popularTokens ? ( {tab === 'manual' || !popularTokens ? (
@ -121,6 +144,8 @@ export default function AddTokenDialog({ open, onClose }) {
margin="normal" margin="normal"
value={mintAddress} value={mintAddress}
onChange={(e) => setMintAddress(e.target.value)} onChange={(e) => setMintAddress(e.target.value)}
autoFocus
disabled={sending}
/> />
<TextField <TextField
label="Token Name" label="Token Name"
@ -129,6 +154,7 @@ export default function AddTokenDialog({ open, onClose }) {
margin="normal" margin="normal"
value={tokenName} value={tokenName}
onChange={(e) => setTokenName(e.target.value)} onChange={(e) => setTokenName(e.target.value)}
disabled={sending}
/> />
<TextField <TextField
label="Token Symbol" label="Token Symbol"
@ -137,9 +163,10 @@ export default function AddTokenDialog({ open, onClose }) {
margin="normal" margin="normal"
value={tokenSymbol} value={tokenSymbol}
onChange={(e) => setTokenSymbol(e.target.value)} onChange={(e) => setTokenSymbol(e.target.value)}
disabled={sending}
/> />
</React.Fragment> </React.Fragment>
) : ( ) : tab === 'popular' ? (
<List disablePadding> <List disablePadding>
{popularTokens.map((token) => ( {popularTokens.map((token) => (
<TokenListItem <TokenListItem
@ -154,15 +181,37 @@ export default function AddTokenDialog({ open, onClose }) {
/> />
))} ))}
</List> </List>
)} ) : tab === 'erc20' ? (
<>
<TextField
label="ERC20 Contract Address"
fullWidth
variant="outlined"
margin="normal"
value={erc20Address}
onChange={(e) => setErc20Address(e.target.value.trim())}
autoFocus
disabled={sending}
/>
{erc20Address && valid ? (
<Link
href={`https://etherscan.io/token/${erc20Address}`}
target="_blank"
rel="noopener"
>
View on Etherscan
</Link>
) : null}
</>
) : null}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}>Cancel</Button> <Button onClick={onClose}>Cancel</Button>
{tab === 'manual' && ( {tab !== 'popular' && (
<Button <Button
type="submit" type="submit"
color="primary" color="primary"
disabled={sending} disabled={sending || !valid}
onClick={() => onSubmit({ tokenName, tokenSymbol, mintAddress })} onClick={() => onSubmit({ tokenName, tokenSymbol, mintAddress })}
> >
Add Add

View File

@ -124,6 +124,15 @@ export function useUpdateTokenName() {
const { endpoint } = useConnectionConfig(); const { endpoint } = useConnectionConfig();
return useCallback( return useCallback(
function updateTokenName(mint, name, symbol) { function updateTokenName(mint, name, symbol) {
if (!name || !symbol) {
if (name) {
symbol = name;
} else if (symbol) {
name = symbol;
} else {
return;
}
}
if (!customTokenNamesByNetwork[endpoint]) { if (!customTokenNamesByNetwork[endpoint]) {
customTokenNamesByNetwork[endpoint] = {}; customTokenNamesByNetwork[endpoint] = {};
} }