feat: add solana-token-registry to explorer (#15496)
* feat: add solana-token-registry to explorer * feat: bump version for token-registry * fix: ensure tokenName and tokenSymbol exist in incoming json
This commit is contained in:
parent
5b54aed1c0
commit
2483a05786
|
@ -2598,6 +2598,14 @@
|
|||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@solana/spl-token-registry": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@solana/spl-token-registry/-/spl-token-registry-0.1.4.tgz",
|
||||
"integrity": "sha512-gJ7dW8G3sYy/kgrYykjt5+01Tpl3jGxOg+5cmLAcgrFIt9zbD22gXdINxznObIaviahiFCfpLPiTfqHmW9fvEA==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.0.6"
|
||||
}
|
||||
},
|
||||
"@solana/web3.js": {
|
||||
"version": "0.92.2",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.92.2.tgz",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"@project-serum/serum": "^0.13.23",
|
||||
"@react-hook/debounce": "^3.0.0",
|
||||
"@sentry/react": "^6.2.0",
|
||||
"@solana/spl-token-registry": "^0.1.4",
|
||||
"@solana/web3.js": "^0.92.2",
|
||||
"@testing-library/jest-dom": "^5.11.9",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
|
|
|
@ -10,14 +10,16 @@ import {
|
|||
LoaderName,
|
||||
SEARCHABLE_PROGRAMS,
|
||||
} from "utils/tx";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { Cluster, useCluster } from "providers/cluster";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
||||
|
||||
export function SearchBar() {
|
||||
const [search, setSearch] = React.useState("");
|
||||
const selectRef = React.useRef<StateManager<any> | null>(null);
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const { cluster } = useCluster();
|
||||
|
||||
const onChange = (
|
||||
|
@ -41,7 +43,7 @@ export function SearchBar() {
|
|||
<div className="col">
|
||||
<Select
|
||||
ref={(ref) => (selectRef.current = ref)}
|
||||
options={buildOptions(search, cluster)}
|
||||
options={buildOptions(search, cluster, tokenRegistry)}
|
||||
noOptionsMessage={() => "No Results"}
|
||||
placeholder="Search for blocks, accounts, transactions, programs, and tokens"
|
||||
value={resetValue}
|
||||
|
@ -138,13 +140,17 @@ function buildSysvarOptions(search: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function buildTokenOptions(search: string, cluster: Cluster) {
|
||||
const matchedTokens = Object.entries(TokenRegistry.all(cluster)).filter(
|
||||
function buildTokenOptions(
|
||||
search: string,
|
||||
cluster: Cluster,
|
||||
tokenRegistry: KnownTokenMap
|
||||
) {
|
||||
const matchedTokens = Array.from(tokenRegistry.entries()).filter(
|
||||
([address, details]) => {
|
||||
const searchLower = search.toLowerCase();
|
||||
return (
|
||||
details.name.toLowerCase().includes(searchLower) ||
|
||||
details.symbol.toLowerCase().includes(searchLower) ||
|
||||
details.tokenName.toLowerCase().includes(searchLower) ||
|
||||
details.tokenSymbol.toLowerCase().includes(searchLower) ||
|
||||
address.includes(search)
|
||||
);
|
||||
}
|
||||
|
@ -154,15 +160,19 @@ function buildTokenOptions(search: string, cluster: Cluster) {
|
|||
return {
|
||||
label: "Tokens",
|
||||
options: matchedTokens.map(([id, details]) => ({
|
||||
label: details.name,
|
||||
value: [details.name, details.symbol, id],
|
||||
label: details.tokenName,
|
||||
value: [details.tokenName, details.tokenSymbol, id],
|
||||
pathname: "/address/" + id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function buildOptions(rawSearch: string, cluster: Cluster) {
|
||||
function buildOptions(
|
||||
rawSearch: string,
|
||||
cluster: Cluster,
|
||||
tokenRegistry: KnownTokenMap
|
||||
) {
|
||||
const search = rawSearch.trim();
|
||||
if (search.length === 0) return [];
|
||||
|
||||
|
@ -183,7 +193,7 @@ function buildOptions(rawSearch: string, cluster: Cluster) {
|
|||
options.push(sysvarOptions);
|
||||
}
|
||||
|
||||
const tokenOptions = buildTokenOptions(search, cluster);
|
||||
const tokenOptions = buildTokenOptions(search, cluster, tokenRegistry);
|
||||
if (tokenOptions) {
|
||||
options.push(tokenOptions);
|
||||
}
|
||||
|
|
|
@ -9,11 +9,10 @@ import {
|
|||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
import { LoadingCard } from "components/common/LoadingCard";
|
||||
import { Address } from "components/common/Address";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { useQuery } from "utils/url";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Location } from "history";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
type Display = "summary" | "detail" | null;
|
||||
|
||||
|
@ -90,15 +89,14 @@ export function OwnedTokensCard({ pubkey }: { pubkey: PublicKey }) {
|
|||
|
||||
function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
||||
const detailsList: React.ReactNode[] = [];
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const showLogos = tokens.some(
|
||||
(t) =>
|
||||
TokenRegistry.get(t.info.mint.toBase58(), cluster)?.icon !== undefined
|
||||
(t) => tokenRegistry.get(t.info.mint.toBase58())?.icon !== undefined
|
||||
);
|
||||
tokens.forEach((tokenAccount) => {
|
||||
const address = tokenAccount.pubkey.toBase58();
|
||||
const mintAddress = tokenAccount.info.mint.toBase58();
|
||||
const tokenDetails = TokenRegistry.get(mintAddress, cluster);
|
||||
const tokenDetails = tokenRegistry.get(mintAddress);
|
||||
detailsList.push(
|
||||
<tr key={address}>
|
||||
{showLogos && (
|
||||
|
@ -120,7 +118,7 @@ function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||
</td>
|
||||
<td>
|
||||
{tokenAccount.info.tokenAmount.uiAmount}{" "}
|
||||
{tokenDetails && tokenDetails.symbol}
|
||||
{tokenDetails && tokenDetails.tokenSymbol}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
@ -146,7 +144,7 @@ function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||
}
|
||||
|
||||
function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const mappedTokens = new Map<string, number>();
|
||||
for (const { info: token } of tokens) {
|
||||
const mintAddress = token.mint.toBase58();
|
||||
|
@ -162,11 +160,10 @@ function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||
|
||||
const detailsList: React.ReactNode[] = [];
|
||||
const showLogos = tokens.some(
|
||||
(t) =>
|
||||
TokenRegistry.get(t.info.mint.toBase58(), cluster)?.icon !== undefined
|
||||
(t) => tokenRegistry.get(t.info.mint.toBase58())?.icon !== undefined
|
||||
);
|
||||
mappedTokens.forEach((totalByMint, mintAddress) => {
|
||||
const tokenDetails = TokenRegistry.get(mintAddress, cluster);
|
||||
const tokenDetails = tokenRegistry.get(mintAddress);
|
||||
detailsList.push(
|
||||
<tr key={mintAddress}>
|
||||
{showLogos && (
|
||||
|
@ -184,7 +181,7 @@ function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||
<Address pubkey={new PublicKey(mintAddress)} link />
|
||||
</td>
|
||||
<td>
|
||||
{totalByMint} {tokenDetails && tokenDetails.symbol}
|
||||
{totalByMint} {tokenDetails && tokenDetails.tokenSymbol}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
|
|
@ -10,11 +10,11 @@ import { coerce } from "superstruct";
|
|||
import { TableCardBody } from "components/common/TableCardBody";
|
||||
import { Address } from "components/common/Address";
|
||||
import { UnknownAccountCard } from "./UnknownAccountCard";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { normalizeTokenAmount } from "utils";
|
||||
import { addressLabel } from "utils/tx";
|
||||
import { reportError } from "utils/sentry";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
export function TokenAccountSection({
|
||||
account,
|
||||
|
@ -53,12 +53,12 @@ function MintAccountCard({
|
|||
account: Account;
|
||||
info: MintAccountInfo;
|
||||
}) {
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const mintAddress = account.pubkey.toBase58();
|
||||
const fetchInfo = useFetchAccountInfo();
|
||||
const refresh = () => fetchInfo(account.pubkey);
|
||||
|
||||
const tokenInfo = TokenRegistry.get(mintAddress, cluster);
|
||||
const tokenInfo = tokenRegistry.get(mintAddress);
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
|
@ -143,8 +143,8 @@ function TokenAccountCard({
|
|||
}) {
|
||||
const refresh = useFetchAccountInfo();
|
||||
const { cluster } = useCluster();
|
||||
|
||||
const label = addressLabel(account.pubkey.toBase58(), cluster);
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const label = addressLabel(account.pubkey.toBase58(), cluster, tokenRegistry);
|
||||
|
||||
let unit, balance;
|
||||
if (info.isNative) {
|
||||
|
@ -161,7 +161,7 @@ function TokenAccountCard({
|
|||
);
|
||||
} else {
|
||||
balance = <>{info.tokenAmount.uiAmount}</>;
|
||||
unit = TokenRegistry.get(info.mint.toBase58(), cluster)?.symbol || "tokens";
|
||||
unit = tokenRegistry.get(info.mint.toBase58())?.tokenSymbol || "tokens";
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -50,6 +50,8 @@ import { useCluster, Cluster } from "providers/cluster";
|
|||
import { Link } from "react-router-dom";
|
||||
import { Location } from "history";
|
||||
import { useQuery } from "utils/url";
|
||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
const TRUNCATE_TOKEN_LENGTH = 10;
|
||||
const ALL_TOKENS = "";
|
||||
|
@ -299,6 +301,7 @@ function TokenHistoryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||
|
||||
const FilterDropdown = ({ filter, toggle, show, tokens }: FilterProps) => {
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
|
||||
const buildLocation = (location: Location, filter: string) => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
|
@ -319,7 +322,7 @@ const FilterDropdown = ({ filter, toggle, show, tokens }: FilterProps) => {
|
|||
tokens.forEach((token) => {
|
||||
const pubkey = token.info.mint.toBase58();
|
||||
filterOptions.push(pubkey);
|
||||
nameLookup[pubkey] = formatTokenName(pubkey, cluster);
|
||||
nameLookup[pubkey] = formatTokenName(pubkey, cluster, tokenRegistry);
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -349,7 +352,7 @@ const FilterDropdown = ({ filter, toggle, show, tokens }: FilterProps) => {
|
|||
>
|
||||
{filterOption === ALL_TOKENS
|
||||
? "All Tokens"
|
||||
: formatTokenName(filterOption, cluster)}
|
||||
: formatTokenName(filterOption, cluster, tokenRegistry)}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
|
@ -602,8 +605,12 @@ function InstructionDetails({
|
|||
);
|
||||
}
|
||||
|
||||
function formatTokenName(pubkey: string, cluster: Cluster): string {
|
||||
let display = displayAddress(pubkey, cluster);
|
||||
function formatTokenName(
|
||||
pubkey: string,
|
||||
cluster: Cluster,
|
||||
tokenRegistry: KnownTokenMap
|
||||
): string {
|
||||
let display = displayAddress(pubkey, cluster, tokenRegistry);
|
||||
|
||||
if (display === pubkey) {
|
||||
display = display.slice(0, TRUNCATE_TOKEN_LENGTH) + "\u2026";
|
||||
|
|
|
@ -9,10 +9,9 @@ import {
|
|||
TokenAccountBalancePairWithOwner,
|
||||
} from "providers/mints/largest";
|
||||
import { FetchStatus } from "providers/cache";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useMintAccountInfo } from "providers/accounts";
|
||||
import { normalizeTokenAmount } from "utils";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
|
||||
const mintAddress = pubkey.toBase58();
|
||||
|
@ -23,8 +22,8 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
|
|||
pubkey,
|
||||
fetchLargestAccounts,
|
||||
]);
|
||||
const { cluster } = useCluster();
|
||||
const unit = TokenRegistry.get(mintAddress, cluster)?.symbol;
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const unit = tokenRegistry.get(mintAddress)?.tokenSymbol;
|
||||
const unitLabel = unit ? `(${unit})` : "";
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
|
@ -5,13 +5,15 @@ import { TableCardBody } from "components/common/TableCardBody";
|
|||
import { Address } from "components/common/Address";
|
||||
import { addressLabel } from "utils/tx";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
export function UnknownAccountCard({ account }: { account: Account }) {
|
||||
const { details, lamports } = account;
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
if (lamports === undefined) return null;
|
||||
|
||||
const label = addressLabel(account.pubkey.toBase58(), cluster);
|
||||
const label = addressLabel(account.pubkey.toBase58(), cluster, tokenRegistry);
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
|
|
|
@ -5,6 +5,7 @@ import { clusterPath } from "utils/url";
|
|||
import { displayAddress } from "utils/tx";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { Copyable } from "./Copyable";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
type Props = {
|
||||
pubkey: PublicKey;
|
||||
|
@ -24,9 +25,13 @@ export function Address({
|
|||
truncateUnknown,
|
||||
}: Props) {
|
||||
const address = pubkey.toBase58();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const { cluster } = useCluster();
|
||||
|
||||
if (truncateUnknown && address === displayAddress(address, cluster)) {
|
||||
if (
|
||||
truncateUnknown &&
|
||||
address === displayAddress(address, cluster, tokenRegistry)
|
||||
) {
|
||||
truncate = true;
|
||||
}
|
||||
|
||||
|
@ -38,11 +43,11 @@ export function Address({
|
|||
className={truncate ? "text-truncate address-truncate" : ""}
|
||||
to={clusterPath(`/address/${address}`)}
|
||||
>
|
||||
{raw ? address : displayAddress(address, cluster)}
|
||||
{raw ? address : displayAddress(address, cluster, tokenRegistry)}
|
||||
</Link>
|
||||
) : (
|
||||
<span className={truncate ? "text-truncate address-truncate" : ""}>
|
||||
{raw ? address : displayAddress(address, cluster)}
|
||||
{raw ? address : displayAddress(address, cluster, tokenRegistry)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
|
|
@ -24,8 +24,7 @@ import {
|
|||
} from "providers/accounts";
|
||||
import { normalizeTokenAmount } from "utils";
|
||||
import { reportError } from "utils/sentry";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
type DetailsProps = {
|
||||
tx: ParsedTransaction;
|
||||
|
@ -92,7 +91,7 @@ function TokenInstruction(props: InfoProps) {
|
|||
const tokenInfo = useTokenAccountInfo(tokenAddress);
|
||||
const mintAddress = infoMintAddress || tokenInfo?.mint.toBase58();
|
||||
const mintInfo = useMintAccountInfo(mintAddress);
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const fetchAccountInfo = useFetchAccountInfo();
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -116,10 +115,10 @@ function TokenInstruction(props: InfoProps) {
|
|||
}
|
||||
|
||||
if (mintAddress) {
|
||||
const tokenDetails = TokenRegistry.get(mintAddress, cluster);
|
||||
const tokenDetails = tokenRegistry.get(mintAddress);
|
||||
|
||||
if (tokenDetails && "symbol" in tokenDetails) {
|
||||
tokenSymbol = tokenDetails.symbol;
|
||||
if (tokenDetails) {
|
||||
tokenSymbol = tokenDetails.tokenSymbol;
|
||||
}
|
||||
|
||||
attributes.push(
|
||||
|
|
|
@ -9,9 +9,8 @@ import { BigNumber } from "bignumber.js";
|
|||
import { Address } from "components/common/Address";
|
||||
import { BalanceDelta } from "components/common/BalanceDelta";
|
||||
import { SignatureProps } from "pages/TransactionDetailsPage";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useTransactionDetails } from "providers/transactions";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
type TokenBalanceRow = {
|
||||
account: PublicKey;
|
||||
|
@ -23,7 +22,7 @@ type TokenBalanceRow = {
|
|||
|
||||
export function TokenBalancesCard({ signature }: SignatureProps) {
|
||||
const details = useTransactionDetails(signature);
|
||||
const { cluster } = useCluster();
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
|
||||
if (!details) {
|
||||
return null;
|
||||
|
@ -51,7 +50,7 @@ export function TokenBalancesCard({ signature }: SignatureProps) {
|
|||
|
||||
const accountRows = rows.map(({ account, delta, balance, mint }) => {
|
||||
const key = account.toBase58() + mint;
|
||||
const units = TokenRegistry.get(mint, cluster)?.symbol || "tokens";
|
||||
const units = tokenRegistry.get(mint)?.tokenSymbol || "tokens";
|
||||
|
||||
return (
|
||||
<tr key={key}>
|
||||
|
|
|
@ -19,7 +19,6 @@ import { OwnedTokensCard } from "components/account/OwnedTokensCard";
|
|||
import { TransactionHistoryCard } from "components/account/TransactionHistoryCard";
|
||||
import { TokenHistoryCard } from "components/account/TokenHistoryCard";
|
||||
import { TokenLargestAccountsCard } from "components/account/TokenLargestAccountsCard";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { VoteAccountSection } from "components/account/VoteAccountSection";
|
||||
import { NonceAccountSection } from "components/account/NonceAccountSection";
|
||||
import { VotesCard } from "components/account/VotesCard";
|
||||
|
@ -30,6 +29,7 @@ import { BlockhashesCard } from "components/account/BlockhashesCard";
|
|||
import { ConfigAccountSection } from "components/account/ConfigAccountSection";
|
||||
import { useFlaggedAccounts } from "providers/accounts/flagged-accounts";
|
||||
import { UpgradeableProgramSection } from "components/account/UpgradeableProgramSection";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
|
||||
const TABS_LOOKUP: { [id: string]: Tab } = {
|
||||
"spl-token:mint": {
|
||||
|
@ -92,16 +92,16 @@ export function AccountDetailsPage({ address, tab }: Props) {
|
|||
}
|
||||
|
||||
export function AccountHeader({ address }: { address: string }) {
|
||||
const { cluster } = useCluster();
|
||||
const tokenDetails = TokenRegistry.get(address, cluster);
|
||||
const { tokenRegistry } = useTokenRegistry();
|
||||
const tokenDetails = tokenRegistry.get(address);
|
||||
if (tokenDetails) {
|
||||
return (
|
||||
<div className="row align-items-end">
|
||||
{tokenDetails.logo && (
|
||||
{tokenDetails.icon && (
|
||||
<div className="col-auto">
|
||||
<div className="avatar avatar-lg header-avatar-top">
|
||||
<img
|
||||
src={tokenDetails.logo}
|
||||
src={tokenDetails.icon}
|
||||
alt="token logo"
|
||||
className="avatar-img rounded-circle border border-4 border-body"
|
||||
/>
|
||||
|
@ -111,7 +111,7 @@ export function AccountHeader({ address }: { address: string }) {
|
|||
|
||||
<div className="col mb-3 ml-n3 ml-md-n2">
|
||||
<h6 className="header-pretitle">Token</h6>
|
||||
<h2 className="header-title">{tokenDetails.name}</h2>
|
||||
<h2 className="header-title">{tokenDetails.tokenName}</h2>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import React from "react";
|
||||
import { LargestAccountsProvider } from "./largest";
|
||||
import { TokenRegistryProvider } from "./token-registry";
|
||||
|
||||
type ProviderProps = { children: React.ReactNode };
|
||||
export function MintsProvider({ children }: ProviderProps) {
|
||||
return <LargestAccountsProvider>{children}</LargestAccountsProvider>;
|
||||
return (
|
||||
<TokenRegistryProvider>
|
||||
<LargestAccountsProvider>{children}</LargestAccountsProvider>
|
||||
</TokenRegistryProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TokenListProvider,
|
||||
KnownToken,
|
||||
KnownTokenMap,
|
||||
} from "@solana/spl-token-registry";
|
||||
import { clusterSlug, useCluster } from "providers/cluster";
|
||||
|
||||
const TokenRegistryContext = React.createContext<KnownTokenMap>(new Map());
|
||||
|
||||
type ProviderProps = { children: React.ReactNode };
|
||||
|
||||
export function TokenRegistryProvider({ children }: ProviderProps) {
|
||||
const [tokenRegistry, setTokenRegistry] = React.useState<KnownTokenMap>(
|
||||
new Map()
|
||||
);
|
||||
const { cluster } = useCluster();
|
||||
|
||||
React.useEffect(() => {
|
||||
new TokenListProvider()
|
||||
.resolve(clusterSlug(cluster))
|
||||
.then((tokens: KnownToken[]) => {
|
||||
setTokenRegistry(
|
||||
tokens.reduce((map: KnownTokenMap, item: KnownToken) => {
|
||||
if (item.tokenName && item.tokenSymbol) {
|
||||
map.set(item.mintAddress, item);
|
||||
}
|
||||
return map;
|
||||
}, new Map())
|
||||
);
|
||||
});
|
||||
}, [cluster]);
|
||||
|
||||
return (
|
||||
<TokenRegistryContext.Provider value={tokenRegistry}>
|
||||
{children}
|
||||
</TokenRegistryContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTokenRegistry() {
|
||||
const tokenRegistry = React.useContext(TokenRegistryContext);
|
||||
|
||||
if (!tokenRegistry) {
|
||||
throw new Error(`useTokenRegistry must be used within a MintsProvider`);
|
||||
}
|
||||
|
||||
return { tokenRegistry };
|
||||
}
|
|
@ -1,296 +0,0 @@
|
|||
import { Cluster } from "providers/cluster";
|
||||
|
||||
export type TokenDetails = {
|
||||
name: string;
|
||||
symbol: string;
|
||||
logo?: string;
|
||||
icon?: string;
|
||||
website?: string;
|
||||
};
|
||||
|
||||
function get(address: string, cluster: Cluster): TokenDetails | undefined {
|
||||
if (cluster === Cluster.MainnetBeta) return MAINNET_TOKENS[address];
|
||||
}
|
||||
|
||||
function all(cluster: Cluster) {
|
||||
if (cluster === Cluster.MainnetBeta) return MAINNET_TOKENS;
|
||||
return {};
|
||||
}
|
||||
|
||||
export const TokenRegistry = {
|
||||
get,
|
||||
all,
|
||||
};
|
||||
|
||||
const MAINNET_TOKENS: { [key: string]: TokenDetails } = {
|
||||
SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt: {
|
||||
name: "Serum",
|
||||
symbol: "SRM",
|
||||
logo: "/tokens/serum-64.png",
|
||||
icon: "/tokens/serum-32.png",
|
||||
website: "https://projectserum.com",
|
||||
},
|
||||
MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L: {
|
||||
name: "MegaSerum",
|
||||
symbol: "MSRM",
|
||||
logo: "/tokens/serum-64.png",
|
||||
icon: "/tokens/serum-32.png",
|
||||
website: "https://projectserum.com",
|
||||
},
|
||||
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: {
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
logo: "/tokens/usdc.svg",
|
||||
icon: "/tokens/usdc.svg",
|
||||
website: "https://www.centre.io/",
|
||||
},
|
||||
"9S4t2NEAiJVMvPdRYKVrfJpBafPBLtvbvyS3DecojQHw": {
|
||||
symbol: "FRONT",
|
||||
name: "Wrapped FRONT",
|
||||
logo: "/tokens/front.svg",
|
||||
icon: "/tokens/front.svg",
|
||||
website: "https://frontier.xyz/",
|
||||
},
|
||||
"9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E": {
|
||||
symbol: "BTC",
|
||||
name: "Wrapped Bitcoin",
|
||||
logo: "/tokens/bitcoin.svg",
|
||||
icon: "/tokens/bitcoin.svg",
|
||||
},
|
||||
"2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk": {
|
||||
symbol: "ETH",
|
||||
name: "Wrapped Ethereum",
|
||||
logo: "/tokens/ethereum.svg",
|
||||
icon: "/tokens/ethereum.svg",
|
||||
},
|
||||
AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3: {
|
||||
symbol: "FTT",
|
||||
name: "Wrapped FTT",
|
||||
logo: "/tokens/ftt.svg",
|
||||
icon: "/tokens/ftt.svg",
|
||||
},
|
||||
"3JSf5tPeuscJGtaCp5giEiDhv51gQ4v3zWg8DGgyLfAB": {
|
||||
symbol: "YFI",
|
||||
name: "Wrapped YFI",
|
||||
logo: "/tokens/yfi.svg",
|
||||
icon: "/tokens/yfi.svg",
|
||||
},
|
||||
CWE8jPTUYhdCTZYWPTe1o5DFqfdjzWKc9WKz6rSjQUdG: {
|
||||
symbol: "LINK",
|
||||
name: "Wrapped Chainlink",
|
||||
logo: "/tokens/link.svg",
|
||||
icon: "/tokens/link.svg",
|
||||
},
|
||||
Ga2AXHpfAF6mv2ekZwcsJFqu7wB4NV331qNH7fW9Nst8: {
|
||||
symbol: "XRP",
|
||||
name: "Wrapped XRP",
|
||||
logo: "/tokens/xrp.svg",
|
||||
icon: "/tokens/xrp.svg",
|
||||
},
|
||||
BQcdHdAQW1hczDbBi9hiegXAR7A98Q9jx3X3iBBBDiq4: {
|
||||
symbol: "USDT",
|
||||
name: "Wrapped USDT",
|
||||
logo: "/tokens/usdt.svg",
|
||||
icon: "/tokens/usdt.svg",
|
||||
},
|
||||
BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW: {
|
||||
symbol: "USDC",
|
||||
name: "Wrapped USDC",
|
||||
},
|
||||
So11111111111111111111111111111111111111112: {
|
||||
symbol: "SOL",
|
||||
name: "Wrapped SOL",
|
||||
},
|
||||
SF3oTvfWzEP3DTwGSvUXRrGTvr75pdZNnBLAH9bzMuX: {
|
||||
symbol: "SXP",
|
||||
name: "Wrapped Swipe",
|
||||
logo: "/tokens/sxp.svg",
|
||||
icon: "/tokens/sxp.svg",
|
||||
},
|
||||
BtZQfWqDGbk9Wf2rXEiWyQBdBY1etnUUn6zEphvVS7yN: {
|
||||
symbol: "HGET",
|
||||
name: "Wrapped Hedget",
|
||||
},
|
||||
"873KLxCbz7s9Kc4ZzgYRtNmhfkQrhfyWGZJBmyCbC3ei": {
|
||||
symbol: "UBXT",
|
||||
name: "Wrapped Upbots",
|
||||
},
|
||||
CsZ5LZkDS7h9TDKjrbL7VAwQZ9nsRu8vJLhRYfmGaN8K: {
|
||||
symbol: "ALEPH",
|
||||
name: "Wrapped Aleph",
|
||||
},
|
||||
"5Fu5UUgbjpUvdBveb3a1JTNirL8rXtiYeSMWvKjtUNQv": {
|
||||
symbol: "CREAM",
|
||||
name: "Wrapped Cream Finance",
|
||||
logo: "/tokens/cream.svg",
|
||||
icon: "/tokens/cream.svg",
|
||||
},
|
||||
HqB7uswoVg4suaQiDP3wjxob1G5WdZ144zhdStwMCq7e: {
|
||||
symbol: "HNT",
|
||||
name: "Wrapped Helium",
|
||||
},
|
||||
AR1Mtgh7zAtxuxGd2XPovXPVjcSdY3i4rQYisNadjfKy: {
|
||||
symbol: "SUSHI",
|
||||
name: "Wrapped Sushi",
|
||||
},
|
||||
AcstFzGGawvvdVhYV9bftr7fmBHbePUjhv53YK1W3dZo: {
|
||||
symbol: "LSD",
|
||||
name: "LSD",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"91fSFQsPzMLat9DHwLdQacW3i3EGnWds5tA5mt7yLiT9": {
|
||||
symbol: "Unlimited Energy",
|
||||
name: "Unlimited Energy",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"29PEpZeuqWf9tS2gwCjpeXNdXLkaZSMR2s1ibkvGsfnP": {
|
||||
symbol: "Need for Speed",
|
||||
name: "Need for Speed",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
HsY8PNar8VExU335ZRYzg89fX7qa4upYu6vPMPFyCDdK: {
|
||||
symbol: "ADOR OPENS",
|
||||
name: "ADOR OPENS",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
EDP8TpLJ77M3KiDgFkZW4v4mhmKJHZi9gehYXenfFZuL: {
|
||||
symbol: "CMS - Rare",
|
||||
name: "CMS - Rare",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
BrUKFwAABkExb1xzYU4NkRWzjBihVQdZ3PBz4m5S8if3: {
|
||||
symbol: "Tesla",
|
||||
name: "Tesla",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"9CmQwpvVXRyixjiE3LrbSyyopPZohNDN1RZiTk8rnXsQ": {
|
||||
symbol: "DeceFi",
|
||||
name: "DeceFi",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
F6ST1wWkx2PeH45sKmRxo1boyuzzWCfpnvyKL4BGeLxF: {
|
||||
symbol: "Power User",
|
||||
name: "Power User",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
dZytJ7iPDcCu9mKe3srL7bpUeaR3zzkcVqbtqsmxtXZ: {
|
||||
symbol: "VIP Member",
|
||||
name: "VIP Member",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"8T4vXgwZUWwsbCDiptHFHjdfexvLG9UP8oy1psJWEQdS": {
|
||||
symbol: "Uni Christmas",
|
||||
name: "Uni Christmas",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
EjFGGJSyp9UDS8aqafET5LX49nsG326MeNezYzpiwgpQ: {
|
||||
symbol: "BNB",
|
||||
name: "BNB",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
FkmkTr4en8CXkfo9jAwEMov6PVNLpYMzWr3Udqf9so8Z: {
|
||||
symbol: "Seldom",
|
||||
name: "Seldom",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"2gn1PJdMAU92SU5inLSp4Xp16ZC5iLF6ScEi7UBvp8ZD": {
|
||||
symbol: "Satoshi Closeup",
|
||||
name: "Satoshi Closeup",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"7mhZHtPL4GFkquQR4Y6h34Q8hNkQvGc1FaNtyE43NvUR": {
|
||||
symbol: "Satoshi GB",
|
||||
name: "Satoshi GB",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"8RoKfLx5RCscbtVh8kYb81TF7ngFJ38RPomXtUREKsT2": {
|
||||
symbol: "Satoshi OG",
|
||||
name: "Satoshi OG",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"9rw5hyDngBQ3yDsCRHqgzGHERpU2zaLh1BXBUjree48J": {
|
||||
symbol: "Satoshi BTC",
|
||||
name: "Satoshi BTC",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
AiD7J6D5Hny5DJB1MrYBc2ePQqy2Yh4NoxWwYfR7PzxH: {
|
||||
symbol: "Satoshi GB",
|
||||
name: "Satoshi GB",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
bxiA13fpU1utDmYuUvxvyMT8odew5FEm96MRv7ij3eb: {
|
||||
symbol: "Satoshi",
|
||||
name: "Satoshi",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
GoC24kpj6TkvjzspXrjSJC2CVb5zMWhLyRcHJh9yKjRF: {
|
||||
symbol: "Satoshi Closeup",
|
||||
name: "Satoshi Closeup",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
oCUduD44ETuZ65bpWdPzPDSnAdreg1sJrugfwyFZVHV: {
|
||||
symbol: "Satoshi BTC",
|
||||
name: "Satoshi BTC",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"9Vvre2DxBB9onibwYDHeMsY1cj6BDKtEDccBPWRN215E": {
|
||||
symbol: "Satoshi Nakamoto",
|
||||
name: "Satoshi Nakamoto",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"7RpFk44cMTAUt9CcjEMWnZMypE9bYQsjBiSNLn5qBvhP": {
|
||||
symbol: "Charles Hoskinson",
|
||||
name: "Charles Hoskinson",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
GyRkPAxpd9XrMHcBF6fYHVRSZQvQBwAGKAGQeBPSKzMq: {
|
||||
symbol: "SBF",
|
||||
name: "SBF",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
AgdBQN2Sy2abiZ2KToWeUsQ9PHdCv95wt6kVWRf5zDkx: {
|
||||
symbol: "Bitcoin Tram",
|
||||
name: "Bitcoin Tram",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
"7TRzvCqXN8KSXggbSyeEG2Z9YBBhEFmbtmv6FLbd4mmd": {
|
||||
symbol: "SRM tee-shirt",
|
||||
name: "SRM tee-shirt",
|
||||
website: "https://solible.com",
|
||||
},
|
||||
EchesyfXePKdLtoiZSL8pBe8Myagyy8ZRqsACNCFGnvp: {
|
||||
symbol: "FIDA",
|
||||
name: "FIDA",
|
||||
logo: "/tokens/fida.svg",
|
||||
icon: "/tokens/fida.svg",
|
||||
website: "https://bonfida.com",
|
||||
},
|
||||
kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6: {
|
||||
symbol: "KIN",
|
||||
name: "KIN",
|
||||
logo: "/tokens/kin.svg",
|
||||
icon: "/tokens/kin.svg",
|
||||
website: "https://kin.org",
|
||||
},
|
||||
FtgGSFADXBtroxq8VCausXRr2of47QBf5AS1NtZCu4GD: {
|
||||
symbol: "BRZ",
|
||||
name: "BRZ",
|
||||
logo: "/tokens/brz.png",
|
||||
icon: "/tokens/brz.png",
|
||||
website: "https://brztoken.io",
|
||||
},
|
||||
MAPS41MDahZ9QdKXhVa4dWB9RuyfV4XqhyAZ8XcYepb: {
|
||||
symbol: "MAPS",
|
||||
name: "MAPS",
|
||||
logo: "/tokens/maps.svg",
|
||||
icon: "/tokens/maps.svg",
|
||||
website: "https://maps.me/",
|
||||
},
|
||||
Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: {
|
||||
symbol: "USDT",
|
||||
name: "USDT",
|
||||
logo: "/tokens/usdt.svg",
|
||||
icon: "/tokens/usdt.svg",
|
||||
website: "https://tether.to",
|
||||
},
|
||||
};
|
|
@ -15,9 +15,9 @@ import {
|
|||
PartiallyDecodedInstruction,
|
||||
ParsedInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { TokenRegistry } from "tokenRegistry";
|
||||
import { Cluster } from "providers/cluster";
|
||||
import { SerumMarketRegistry } from "serumMarketRegistry";
|
||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
||||
|
||||
export type ProgramName = typeof PROGRAM_NAME_BY_ID[keyof typeof PROGRAM_NAME_BY_ID];
|
||||
|
||||
|
@ -102,20 +102,25 @@ export const SYSVAR_IDS = {
|
|||
|
||||
export function addressLabel(
|
||||
address: string,
|
||||
cluster: Cluster
|
||||
cluster: Cluster,
|
||||
tokenRegistry: KnownTokenMap
|
||||
): string | undefined {
|
||||
return (
|
||||
PROGRAM_NAME_BY_ID[address] ||
|
||||
LOADER_IDS[address] ||
|
||||
SYSVAR_IDS[address] ||
|
||||
SYSVAR_ID[address] ||
|
||||
TokenRegistry.get(address, cluster)?.name ||
|
||||
tokenRegistry.get(address)?.tokenName ||
|
||||
SerumMarketRegistry.get(address, cluster)
|
||||
);
|
||||
}
|
||||
|
||||
export function displayAddress(address: string, cluster: Cluster): string {
|
||||
return addressLabel(address, cluster) || address;
|
||||
export function displayAddress(
|
||||
address: string,
|
||||
cluster: Cluster,
|
||||
tokenRegistry: KnownTokenMap
|
||||
): string {
|
||||
return addressLabel(address, cluster, tokenRegistry) || address;
|
||||
}
|
||||
|
||||
export function intoTransactionInstruction(
|
||||
|
|
Loading…
Reference in New Issue