Use @solana/spl-token-registry (#141)
This commit is contained in:
parent
324b98d6ed
commit
4a601d8355
|
@ -7,6 +7,7 @@
|
|||
"@material-ui/core": "^4.11.2",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@project-serum/serum": "^0.13.24",
|
||||
"@solana/spl-token-registry": "^0.2.1",
|
||||
"@solana/web3.js": "^0.87.2",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
|
|
|
@ -11,6 +11,7 @@ import { ConnectionProvider } from './utils/connection';
|
|||
import WalletPage from './pages/WalletPage';
|
||||
import { useWallet, WalletProvider } from './utils/wallet';
|
||||
import { ConnectedWalletsProvider } from './utils/connected-wallets';
|
||||
import { TokenRegistryProvider } from './utils/tokens/names';
|
||||
import LoadingIndicator from './components/LoadingIndicator';
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import PopupPage from './pages/PopupPage';
|
||||
|
@ -62,9 +63,11 @@ export default function App() {
|
|||
<CssBaseline />
|
||||
|
||||
<ConnectionProvider>
|
||||
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
|
||||
<WalletProvider>{appElement}</WalletProvider>
|
||||
</SnackbarProvider>
|
||||
<TokenRegistryProvider>
|
||||
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
|
||||
<WalletProvider>{appElement}</WalletProvider>
|
||||
</SnackbarProvider>
|
||||
</TokenRegistryProvider>
|
||||
</ConnectionProvider>
|
||||
</ThemeProvider>
|
||||
</Suspense>
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
useWalletTokenAccounts,
|
||||
} from '../utils/wallet';
|
||||
import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
|
||||
import { TOKENS, useUpdateTokenName } from '../utils/tokens/names';
|
||||
import { useUpdateTokenName, usePopularTokens } from '../utils/tokens/names';
|
||||
import { useAsyncData } from '../utils/fetch-loop';
|
||||
import LoadingIndicator from './LoadingIndicator';
|
||||
import { makeStyles, Tab, Tabs } from '@material-ui/core';
|
||||
|
@ -24,10 +24,7 @@ import { abbreviateAddress } from '../utils/utils';
|
|||
import ExpandLess from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
import {
|
||||
useConnectionConfig,
|
||||
useSolanaExplorerUrlSuffix,
|
||||
} from '../utils/connection';
|
||||
import { useSolanaExplorerUrlSuffix } from '../utils/connection';
|
||||
import Link from '@material-ui/core/Link';
|
||||
import CopyableDisplay from './CopyableDisplay';
|
||||
import DialogForm from './DialogForm';
|
||||
|
@ -56,10 +53,9 @@ export default function AddTokenDialog({ open, onClose }) {
|
|||
let classes = useStyles();
|
||||
let updateTokenName = useUpdateTokenName();
|
||||
const [sendTransaction, sending] = useSendTransaction();
|
||||
const { endpoint } = useConnectionConfig();
|
||||
const popularTokens = TOKENS[endpoint];
|
||||
const [walletAccounts] = useWalletTokenAccounts();
|
||||
|
||||
const [walletAccounts] = useWalletTokenAccounts();
|
||||
const popularTokens = usePopularTokens();
|
||||
const [tab, setTab] = useState(!!popularTokens ? 'popular' : 'manual');
|
||||
const [mintAddress, setMintAddress] = useState('');
|
||||
const [tokenName, setTokenName] = useState('');
|
||||
|
@ -171,20 +167,18 @@ export default function AddTokenDialog({ open, onClose }) {
|
|||
</React.Fragment>
|
||||
) : tab === 'popular' ? (
|
||||
<List disablePadding>
|
||||
{popularTokens
|
||||
.filter((token) => !token.deprecated)
|
||||
.map((token) => (
|
||||
<TokenListItem
|
||||
key={token.mintAddress}
|
||||
{...token}
|
||||
existingAccount={(walletAccounts || []).find(
|
||||
(account) =>
|
||||
account.parsed.mint.toBase58() === token.mintAddress,
|
||||
)}
|
||||
onSubmit={onSubmit}
|
||||
disalbed={sending}
|
||||
/>
|
||||
))}
|
||||
{popularTokens.map((tokenInfo) => (
|
||||
<TokenListItem
|
||||
key={tokenInfo.address}
|
||||
tokenInfo={tokenInfo}
|
||||
existingAccount={(walletAccounts || []).find(
|
||||
(account) =>
|
||||
account.parsed.mint.toBase58() === tokenInfo.address,
|
||||
)}
|
||||
onSubmit={onSubmit}
|
||||
disabled={sending}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
) : tab === 'erc20' ? (
|
||||
<>
|
||||
|
@ -227,24 +221,21 @@ export default function AddTokenDialog({ open, onClose }) {
|
|||
);
|
||||
}
|
||||
|
||||
function TokenListItem({
|
||||
tokenName,
|
||||
icon,
|
||||
tokenSymbol,
|
||||
mintAddress,
|
||||
onSubmit,
|
||||
disabled,
|
||||
existingAccount,
|
||||
}) {
|
||||
function TokenListItem({ tokenInfo, onSubmit, disabled, existingAccount }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const urlSuffix = useSolanaExplorerUrlSuffix();
|
||||
const alreadyExists = !!existingAccount;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ display: 'flex' }} key={tokenName}>
|
||||
<div style={{ display: 'flex' }} key={tokenInfo.name}>
|
||||
<ListItem button onClick={() => setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<TokenIcon url={icon} tokenName={tokenName} size={20} />
|
||||
<TokenIcon
|
||||
url={tokenInfo.logoUri}
|
||||
tokenName={tokenInfo.name}
|
||||
size={20}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
|
@ -252,12 +243,12 @@ function TokenListItem({
|
|||
target="_blank"
|
||||
rel="noopener"
|
||||
href={
|
||||
`https://explorer.solana.com/account/${mintAddress}` +
|
||||
`https://explorer.solana.com/account/${tokenInfo.address}` +
|
||||
urlSuffix
|
||||
}
|
||||
>
|
||||
{tokenName ?? abbreviateAddress(mintAddress)}
|
||||
{tokenSymbol ? ` (${tokenSymbol})` : null}
|
||||
{tokenInfo.name ?? abbreviateAddress(tokenInfo.address)}
|
||||
{tokenInfo.symbol ? ` (${tokenInfo.symbol})` : null}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
|
@ -267,15 +258,21 @@ function TokenListItem({
|
|||
type="submit"
|
||||
color="primary"
|
||||
disabled={disabled || alreadyExists}
|
||||
onClick={() => onSubmit({ tokenName, tokenSymbol, mintAddress })}
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
tokenName: tokenInfo.name,
|
||||
tokenSymbol: tokenInfo.symbol,
|
||||
mintAddress: tokenInfo.address,
|
||||
})
|
||||
}
|
||||
>
|
||||
{alreadyExists ? 'Added' : 'Add'}
|
||||
</Button>
|
||||
</div>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<CopyableDisplay
|
||||
value={mintAddress}
|
||||
label={`${tokenSymbol} Mint Address`}
|
||||
value={tokenInfo.address}
|
||||
label={`${tokenInfo.symbol} Mint Address`}
|
||||
/>
|
||||
</Collapse>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -363,7 +363,7 @@ export function BalanceListItem({ publicKey, expandable, setUsdValue }) {
|
|||
return <LoadingIndicator delay={0} />;
|
||||
}
|
||||
|
||||
let { amount, decimals, mint, tokenName, tokenSymbol } = balanceInfo;
|
||||
let { amount, decimals, mint, tokenName, tokenSymbol, tokenLogoUri } = balanceInfo;
|
||||
tokenName = tokenName ?? abbreviateAddress(mint);
|
||||
let displayName;
|
||||
if (isExtensionWidth) {
|
||||
|
@ -450,7 +450,7 @@ export function BalanceListItem({ publicKey, expandable, setUsdValue }) {
|
|||
<>
|
||||
<ListItem button onClick={() => expandable && setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<TokenIcon mint={mint} tokenName={tokenName} size={28} />
|
||||
<TokenIcon mint={mint} tokenName={tokenName} url={tokenLogoUri} size={28} />
|
||||
</ListItemIcon>
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
<ListItemText
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
findAssociatedTokenAddress,
|
||||
} from '../utils/tokens';
|
||||
import { sleep } from '../utils/utils';
|
||||
import { getTokenName } from '../utils/tokens/names';
|
||||
import { useTokenInfos, getTokenInfo } from '../utils/tokens/names';
|
||||
|
||||
export default function MergeAccountsDialog({ open, onClose }) {
|
||||
const [publicKeys] = useWalletPublicKeys();
|
||||
|
@ -33,6 +33,7 @@ export default function MergeAccountsDialog({ open, onClose }) {
|
|||
const { enqueueSnackbar } = useSnackbar();
|
||||
const [isMerging, setIsMerging] = useState(false);
|
||||
const [mergeCheck, setMergeCheck] = useState('');
|
||||
const tokenInfos = useTokenInfos();
|
||||
|
||||
// Merging accounts is a destructive operation that, for each mint,
|
||||
//
|
||||
|
@ -98,8 +99,14 @@ export default function MergeAccountsDialog({ open, onClose }) {
|
|||
assocTokAddr.equals(mintGroup[0].publicKey)
|
||||
)
|
||||
) {
|
||||
const name = getTokenName(mint, connection._rpcEndpoint);
|
||||
const symbol = name.symbol ? name.symbol : mint.toString();
|
||||
const tokenInfo = getTokenInfo(
|
||||
mint,
|
||||
connection._rpcEndpoint,
|
||||
tokenInfos,
|
||||
);
|
||||
const symbol = tokenInfo.symbol
|
||||
? tokenInfo.symbol
|
||||
: mint.toString();
|
||||
console.log(`Merging ${symbol}`);
|
||||
enqueueSnackbar(`Merging ${symbol}`, {
|
||||
variant: 'info',
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { useConnectionConfig, MAINNET_URL } from '../utils/connection';
|
||||
import { useConnectionConfig } from '../utils/connection';
|
||||
import { CLUSTERS, clusterForEndpoint } from '../utils/clusters';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import { clusterApiUrl } from '@solana/web3.js';
|
||||
import { useWalletSelector } from '../utils/wallet';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
|
@ -184,22 +184,10 @@ function ConnectionsButton() {
|
|||
|
||||
function NetworkSelector() {
|
||||
const { endpoint, setEndpoint } = useConnectionConfig();
|
||||
const cluster = useMemo(() => clusterForEndpoint(endpoint), [endpoint])
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const classes = useStyles();
|
||||
|
||||
const networks = [
|
||||
MAINNET_URL,
|
||||
clusterApiUrl('devnet'),
|
||||
clusterApiUrl('testnet'),
|
||||
'http://localhost:8899',
|
||||
];
|
||||
|
||||
const networkLabels = {
|
||||
[MAINNET_URL]: 'Mainnet Beta',
|
||||
[clusterApiUrl('devnet')]: 'Devnet',
|
||||
[clusterApiUrl('testnet')]: 'Testnet',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hidden xsDown>
|
||||
|
@ -208,7 +196,7 @@ function NetworkSelector() {
|
|||
onClick={(e) => setAnchorEl(e.target)}
|
||||
className={classes.button}
|
||||
>
|
||||
{networkLabels[endpoint] ?? 'Network'}
|
||||
{cluster?.label ?? 'Network'}
|
||||
</Button>
|
||||
</Hidden>
|
||||
<Hidden smUp>
|
||||
|
@ -228,19 +216,19 @@ function NetworkSelector() {
|
|||
}}
|
||||
getContentAnchorEl={null}
|
||||
>
|
||||
{networks.map((network) => (
|
||||
{CLUSTERS.map((cluster) => (
|
||||
<MenuItem
|
||||
key={network}
|
||||
key={cluster.apiUrl}
|
||||
onClick={() => {
|
||||
setAnchorEl(null);
|
||||
setEndpoint(network);
|
||||
setEndpoint(cluster.apiUrl);
|
||||
}}
|
||||
selected={network === endpoint}
|
||||
selected={cluster.apiUrl === endpoint}
|
||||
>
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
{network === endpoint ? <CheckIcon fontSize="small" /> : null}
|
||||
{cluster.apiUrl === endpoint ? <CheckIcon fontSize="small" /> : null}
|
||||
</ListItemIcon>
|
||||
{network}
|
||||
{cluster.apiUrl}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
import { useConnectionConfig } from '../utils/connection';
|
||||
import { TOKENS } from '../utils/tokens/names';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function TokenIcon({ mint, url, tokenName, size = 20 }) {
|
||||
const { endpoint } = useConnectionConfig();
|
||||
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
if (!url) {
|
||||
if (mint === null) {
|
||||
url =
|
||||
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/solana/info/logo.png';
|
||||
} else {
|
||||
url = TOKENS?.[endpoint]?.find(
|
||||
(token) => token.mintAddress === mint?.toBase58(),
|
||||
)?.icon;
|
||||
}
|
||||
if (!url && mint === null) {
|
||||
url = 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/solana/info/logo.png';
|
||||
}
|
||||
|
||||
if (hasError || !url) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { clusterApiUrl } from '@solana/web3.js';
|
||||
import { MAINNET_URL } from '../utils/connection';
|
||||
|
||||
export const CLUSTERS = [
|
||||
{
|
||||
name: 'mainnet-beta',
|
||||
apiUrl: MAINNET_URL,
|
||||
label: 'Mainnet Beta'
|
||||
},
|
||||
{
|
||||
name: 'devnet',
|
||||
apiUrl: clusterApiUrl('devnet'),
|
||||
label: 'Devnet'
|
||||
},
|
||||
{
|
||||
name: 'testnet',
|
||||
apiUrl: clusterApiUrl('testnet'),
|
||||
label: 'Testnet'
|
||||
},
|
||||
{
|
||||
name: 'localnet',
|
||||
apiUrl: 'http://localhost:8899',
|
||||
label: null
|
||||
}
|
||||
];
|
||||
|
||||
export function clusterForEndpoint(endpoint) {
|
||||
return CLUSTERS.find(({ apiUrl }) => apiUrl === endpoint);
|
||||
}
|
|
@ -1,9 +1,20 @@
|
|||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import EventEmitter from 'events';
|
||||
import { useConnectionConfig, MAINNET_URL } from '../connection';
|
||||
import { useListener } from '../utils';
|
||||
import { clusterForEndpoint } from '../clusters';
|
||||
import { useCallback } from 'react';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { TokenListProvider } from '@solana/spl-token-registry';
|
||||
|
||||
export const TOKENS = {
|
||||
// This list is used for deciding what to display in the popular tokens list
|
||||
// in the `AddTokenDialog`.
|
||||
//
|
||||
// Icons, names, and symbols are fetched not from here, but from the
|
||||
// @solana/spl-token-registry. To add an icon or token name to the wallet,
|
||||
// add the mints to that package. To add a token to the `AddTokenDialog`,
|
||||
// add the `mintAddress` here. The rest of the fields are not used.
|
||||
const POPULAR_TOKENS = {
|
||||
[MAINNET_URL]: [
|
||||
{
|
||||
mintAddress: 'SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt',
|
||||
|
@ -214,62 +225,6 @@ export const TOKENS = {
|
|||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-LEGACY-USDT',
|
||||
mintAddress: 'CzPDyvotTcxNqtPne32yUiEVQ6jk42HZi1Y3hUu7qf7f',
|
||||
tokenName: 'Raydium Legacy USDT Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-LEGACY-USDC',
|
||||
mintAddress: 'FgmBnsF5Qrnv8X9bomQfEtQTQjNNiBCWRKGpzPnE5BDg',
|
||||
tokenName: 'Raydium Legacy USDC Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-LEGACY-SRM',
|
||||
mintAddress: '5QXBMXuCL7zfAk39jEVVEvcrz1AvBGgT9wAhLLHLyyUJ',
|
||||
tokenName: 'Raydium Legacy Serum Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-ETH',
|
||||
mintAddress: 'Q6MKy5Yxb9vG1mWzppMtMb2nrhNuCRNUkJTeiE3fuwD',
|
||||
tokenName: 'Raydium ETH Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-SOL',
|
||||
mintAddress: 'F5PPQHGcznZ2FxD9JaxJMXaf7XkaFFJ6zzTBcW8osQjw',
|
||||
tokenName: 'Raydium SOL Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-SRM',
|
||||
mintAddress: 'DSX5E21RE9FB9hM8Nh8xcXQfPK6SzRaJiywemHBSsfup',
|
||||
tokenName: 'Raydium SRM Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-USDT',
|
||||
mintAddress: 'FdhKXYjCou2jQfgKWcNY7jb8F2DPLU1teTTTRfLBD2v1',
|
||||
tokenName: 'Raydium USDT Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'RAY-USDC',
|
||||
mintAddress: 'BZFGfXMrjG2sS7QT2eiCDEevPFnkYYF7kzJpWfYxPbcx',
|
||||
tokenName: 'Raydium USDC Liquidity Pool',
|
||||
icon:
|
||||
'https://raw.githubusercontent.com/raydium-io/media-assets/master/logo.svg',
|
||||
},
|
||||
{
|
||||
tokenSymbol: 'OXY',
|
||||
mintAddress: 'z3dn17yLaGMKffVogeFHQ9zWVcXgqgf3PQnDsNs2g6M',
|
||||
|
@ -280,6 +235,39 @@ export const TOKENS = {
|
|||
],
|
||||
};
|
||||
|
||||
const TokenListContext = React.createContext({});
|
||||
|
||||
export function useTokenInfos() {
|
||||
const { tokenInfos } = useContext(TokenListContext);
|
||||
return tokenInfos;
|
||||
}
|
||||
|
||||
export function TokenRegistryProvider(props) {
|
||||
const { endpoint } = useConnectionConfig();
|
||||
const [tokenInfos, setTokenInfos] = useState(null);
|
||||
useEffect(() => {
|
||||
const tokenListProvider = new TokenListProvider();
|
||||
tokenListProvider.resolve().then((tokenListContainer) => {
|
||||
const cluster = clusterForEndpoint(endpoint);
|
||||
|
||||
const filteredTokenListContainer = tokenListContainer?.filterByClusterSlug(
|
||||
cluster?.name,
|
||||
);
|
||||
const tokenInfos =
|
||||
tokenListContainer !== filteredTokenListContainer
|
||||
? filteredTokenListContainer?.getList()
|
||||
: null; // Workaround for filter return all on unknown slug
|
||||
setTokenInfos(tokenInfos);
|
||||
});
|
||||
}, [endpoint]);
|
||||
|
||||
return (
|
||||
<TokenListContext.Provider value={{ tokenInfos }}>
|
||||
{props.children}
|
||||
</TokenListContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const customTokenNamesByNetwork = JSON.parse(
|
||||
localStorage.getItem('tokenNames') ?? '{}',
|
||||
);
|
||||
|
@ -287,25 +275,32 @@ const customTokenNamesByNetwork = JSON.parse(
|
|||
const nameUpdated = new EventEmitter();
|
||||
nameUpdated.setMaxListeners(100);
|
||||
|
||||
export function useTokenName(mint) {
|
||||
export function useTokenInfo(mint) {
|
||||
const { endpoint } = useConnectionConfig();
|
||||
useListener(nameUpdated, 'update');
|
||||
return getTokenName(mint, endpoint);
|
||||
const tokenInfos = useTokenInfos();
|
||||
return getTokenInfo(mint, endpoint, tokenInfos);
|
||||
}
|
||||
|
||||
export function getTokenName(mint, endpoint) {
|
||||
export function getTokenInfo(mint, endpoint, tokenInfos) {
|
||||
if (!mint) {
|
||||
return { name: null, symbol: null };
|
||||
}
|
||||
|
||||
let info = customTokenNamesByNetwork?.[endpoint]?.[mint.toBase58()];
|
||||
let match = TOKENS?.[endpoint]?.find(
|
||||
(token) => token.mintAddress === mint.toBase58(),
|
||||
let match = tokenInfos?.find(
|
||||
(tokenInfo) => tokenInfo.address === mint.toBase58(),
|
||||
);
|
||||
if (match && (!info || match.deprecated)) {
|
||||
info = { name: match.tokenName, symbol: match.tokenSymbol };
|
||||
if (match) {
|
||||
if (!info) {
|
||||
info = { ...match, logoUri: match.logoURI };
|
||||
}
|
||||
// The user has overridden a name locally.
|
||||
else {
|
||||
info = { ...info, logoUri: match.logoURI };
|
||||
}
|
||||
}
|
||||
return { name: info?.name, symbol: info?.symbol };
|
||||
return { ...info };
|
||||
}
|
||||
|
||||
export function useUpdateTokenName() {
|
||||
|
@ -334,3 +329,14 @@ export function useUpdateTokenName() {
|
|||
[endpoint],
|
||||
);
|
||||
}
|
||||
// Returns tokenInfos for the popular tokens list.
|
||||
export function usePopularTokens() {
|
||||
const tokenInfos = useTokenInfos();
|
||||
const { endpoint } = useConnectionConfig();
|
||||
return (!POPULAR_TOKENS[endpoint]
|
||||
? []
|
||||
: POPULAR_TOKENS[endpoint]
|
||||
).map((tok) =>
|
||||
getTokenInfo(new PublicKey(tok.mintAddress), endpoint, tokenInfos),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@ import {
|
|||
transferTokens,
|
||||
transferAndClose,
|
||||
} from './tokens';
|
||||
import { TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT } from './tokens/instructions';
|
||||
import { TOKEN_PROGRAM_ID } from './tokens/instructions';
|
||||
import {
|
||||
ACCOUNT_LAYOUT,
|
||||
parseMintData,
|
||||
parseTokenAccountData,
|
||||
} from './tokens/data';
|
||||
import { useListener, useLocalStorageState, useRefEqual } from './utils';
|
||||
import { useTokenName } from './tokens/names';
|
||||
import { useTokenInfo } from './tokens/names';
|
||||
import { refreshCache, useAsyncData } from './fetch-loop';
|
||||
import { getUnlockedMnemonicAndSeed, walletSeedChanged } from './wallet-seed';
|
||||
import { WalletProviderFactory } from './walletProvider/factory';
|
||||
|
@ -419,24 +419,12 @@ export function useBalanceInfo(publicKey) {
|
|||
? parseTokenAccountData(accountInfo.data)
|
||||
: {};
|
||||
let [mintInfo, mintInfoLoaded] = useAccountInfo(mint);
|
||||
let { name, symbol } = useTokenName(mint);
|
||||
let { name, symbol, logoUri } = useTokenInfo(mint);
|
||||
|
||||
if (!accountInfoLoaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mint && mint.equals(WRAPPED_SOL_MINT)) {
|
||||
return {
|
||||
amount,
|
||||
decimals: 9,
|
||||
mint,
|
||||
owner,
|
||||
tokenName: 'Wrapped SOL',
|
||||
tokenSymbol: 'SOL',
|
||||
valid: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (mint && mintInfoLoaded) {
|
||||
try {
|
||||
let { decimals } = parseMintData(mintInfo.data);
|
||||
|
@ -447,6 +435,7 @@ export function useBalanceInfo(publicKey) {
|
|||
owner,
|
||||
tokenName: name,
|
||||
tokenSymbol: symbol,
|
||||
tokenLogoUri: logoUri,
|
||||
valid: true,
|
||||
};
|
||||
} catch (e) {
|
||||
|
@ -457,6 +446,7 @@ export function useBalanceInfo(publicKey) {
|
|||
owner,
|
||||
tokenName: 'Invalid',
|
||||
tokenSymbol: 'INVALID',
|
||||
tokenLogoUri: null,
|
||||
valid: false,
|
||||
};
|
||||
}
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -1918,6 +1918,13 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@solana/spl-token-registry@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.1.tgz#79b3a8c3f3a12b7956b6ecc08052438972da5ce3"
|
||||
integrity sha512-JzvezgnPftowZXwAzUR7ywm16G6eWb9E7h3iUfeMmzZStBdqB/16TE8+x09yJoYtW5B6bTa80tELB7EfBzZ15w==
|
||||
dependencies:
|
||||
cross-fetch "^3.0.6"
|
||||
|
||||
"@solana/web3.js@^0.87.2":
|
||||
version "0.87.2"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-0.87.2.tgz#92c8d344695c6113d4e0eb3339117fbc6b22d0d2"
|
||||
|
@ -4456,6 +4463,13 @@ create-hmac@1.1.7, create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
|||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
cross-fetch@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||
integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==
|
||||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
|
@ -9085,7 +9099,7 @@ node-addon-api@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
|
||||
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
|
||||
|
||||
node-fetch@^2.2.0:
|
||||
node-fetch@2.6.1, node-fetch@^2.2.0:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
|
Loading…
Reference in New Issue