Explorer: update to new spl-token-registry standard (#15765)
* feat: update tokenlist * feat: bump spl-token-registry version to 0.2.0
This commit is contained in:
6
explorer/package-lock.json
generated
6
explorer/package-lock.json
generated
@ -2599,9 +2599,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@solana/spl-token-registry": {
|
"@solana/spl-token-registry": {
|
||||||
"version": "0.1.9",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/spl-token-registry/-/spl-token-registry-0.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/spl-token-registry/-/spl-token-registry-0.2.0.tgz",
|
||||||
"integrity": "sha512-cXjNg3MQVC+LWoFOMYSNRZrnJLCjOXEioIJPBX2V8Ft9v4f4THV/lim6JLddKGXZC2wr5NTOaI2zLYRbtpXW0w==",
|
"integrity": "sha512-DC1gQDthODgCa0Nd5sPuoGpgEG3xdxQRoggfFFLQpwX689zILabvmVj6v6hF8bN8fGEpADZK1Fp8pwD427jkUQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cross-fetch": "^3.0.6"
|
"cross-fetch": "^3.0.6"
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"@project-serum/serum": "^0.13.25",
|
"@project-serum/serum": "^0.13.25",
|
||||||
"@react-hook/debounce": "^3.0.0",
|
"@react-hook/debounce": "^3.0.0",
|
||||||
"@sentry/react": "^6.2.0",
|
"@sentry/react": "^6.2.0",
|
||||||
"@solana/spl-token-registry": "^0.1.9",
|
"@solana/spl-token-registry": "^0.2.0",
|
||||||
"@solana/web3.js": "^0.94.2",
|
"@solana/web3.js": "^0.94.2",
|
||||||
"@testing-library/jest-dom": "^5.11.9",
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
} from "utils/tx";
|
} from "utils/tx";
|
||||||
import { Cluster, useCluster } from "providers/cluster";
|
import { Cluster, useCluster } from "providers/cluster";
|
||||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
import { TokenInfoMap } from "@solana/spl-token-registry";
|
||||||
|
|
||||||
export function SearchBar() {
|
export function SearchBar() {
|
||||||
const [search, setSearch] = React.useState("");
|
const [search, setSearch] = React.useState("");
|
||||||
@ -143,14 +143,14 @@ function buildSysvarOptions(search: string) {
|
|||||||
function buildTokenOptions(
|
function buildTokenOptions(
|
||||||
search: string,
|
search: string,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
tokenRegistry: KnownTokenMap
|
tokenRegistry: TokenInfoMap
|
||||||
) {
|
) {
|
||||||
const matchedTokens = Array.from(tokenRegistry.entries()).filter(
|
const matchedTokens = Array.from(tokenRegistry.entries()).filter(
|
||||||
([address, details]) => {
|
([address, details]) => {
|
||||||
const searchLower = search.toLowerCase();
|
const searchLower = search.toLowerCase();
|
||||||
return (
|
return (
|
||||||
details.tokenName.toLowerCase().includes(searchLower) ||
|
details.name.toLowerCase().includes(searchLower) ||
|
||||||
details.tokenSymbol.toLowerCase().includes(searchLower) ||
|
details.symbol.toLowerCase().includes(searchLower) ||
|
||||||
address.includes(search)
|
address.includes(search)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,8 +160,8 @@ function buildTokenOptions(
|
|||||||
return {
|
return {
|
||||||
label: "Tokens",
|
label: "Tokens",
|
||||||
options: matchedTokens.map(([id, details]) => ({
|
options: matchedTokens.map(([id, details]) => ({
|
||||||
label: details.tokenName,
|
label: details.name,
|
||||||
value: [details.tokenName, details.tokenSymbol, id],
|
value: [details.name, details.symbol, id],
|
||||||
pathname: "/address/" + id,
|
pathname: "/address/" + id,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@ -171,7 +171,7 @@ function buildTokenOptions(
|
|||||||
function buildOptions(
|
function buildOptions(
|
||||||
rawSearch: string,
|
rawSearch: string,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
tokenRegistry: KnownTokenMap
|
tokenRegistry: TokenInfoMap
|
||||||
) {
|
) {
|
||||||
const search = rawSearch.trim();
|
const search = rawSearch.trim();
|
||||||
if (search.length === 0) return [];
|
if (search.length === 0) return [];
|
||||||
|
@ -92,7 +92,7 @@ function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
const detailsList: React.ReactNode[] = [];
|
const detailsList: React.ReactNode[] = [];
|
||||||
const { tokenRegistry } = useTokenRegistry();
|
const { tokenRegistry } = useTokenRegistry();
|
||||||
const showLogos = tokens.some(
|
const showLogos = tokens.some(
|
||||||
(t) => tokenRegistry.get(t.info.mint.toBase58())?.icon !== undefined
|
(t) => tokenRegistry.get(t.info.mint.toBase58())?.logoURI !== undefined
|
||||||
);
|
);
|
||||||
tokens.forEach((tokenAccount) => {
|
tokens.forEach((tokenAccount) => {
|
||||||
const address = tokenAccount.pubkey.toBase58();
|
const address = tokenAccount.pubkey.toBase58();
|
||||||
@ -102,9 +102,9 @@ function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
<tr key={address}>
|
<tr key={address}>
|
||||||
{showLogos && (
|
{showLogos && (
|
||||||
<td className="w-1 p-0 text-center">
|
<td className="w-1 p-0 text-center">
|
||||||
{tokenDetails?.icon && (
|
{tokenDetails?.logoURI && (
|
||||||
<img
|
<img
|
||||||
src={tokenDetails.icon}
|
src={tokenDetails.logoURI}
|
||||||
alt="token icon"
|
alt="token icon"
|
||||||
className="token-icon rounded-circle border border-4 border-gray-dark"
|
className="token-icon rounded-circle border border-4 border-gray-dark"
|
||||||
/>
|
/>
|
||||||
@ -119,7 +119,7 @@ function HoldingsDetailTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{tokenAccount.info.tokenAmount.uiAmountString}{" "}
|
{tokenAccount.info.tokenAmount.uiAmountString}{" "}
|
||||||
{tokenDetails && tokenDetails.tokenSymbol}
|
{tokenDetails && tokenDetails.symbol}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@ -161,7 +161,7 @@ function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
|
|
||||||
const detailsList: React.ReactNode[] = [];
|
const detailsList: React.ReactNode[] = [];
|
||||||
const showLogos = tokens.some(
|
const showLogos = tokens.some(
|
||||||
(t) => tokenRegistry.get(t.info.mint.toBase58())?.icon !== undefined
|
(t) => tokenRegistry.get(t.info.mint.toBase58())?.logoURI !== undefined
|
||||||
);
|
);
|
||||||
mappedTokens.forEach((totalByMint, mintAddress) => {
|
mappedTokens.forEach((totalByMint, mintAddress) => {
|
||||||
const tokenDetails = tokenRegistry.get(mintAddress);
|
const tokenDetails = tokenRegistry.get(mintAddress);
|
||||||
@ -169,9 +169,9 @@ function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
<tr key={mintAddress}>
|
<tr key={mintAddress}>
|
||||||
{showLogos && (
|
{showLogos && (
|
||||||
<td className="w-1 p-0 text-center">
|
<td className="w-1 p-0 text-center">
|
||||||
{tokenDetails?.icon && (
|
{tokenDetails?.logoURI && (
|
||||||
<img
|
<img
|
||||||
src={tokenDetails.icon}
|
src={tokenDetails.logoURI}
|
||||||
alt="token icon"
|
alt="token icon"
|
||||||
className="token-icon rounded-circle border border-4 border-gray-dark"
|
className="token-icon rounded-circle border border-4 border-gray-dark"
|
||||||
/>
|
/>
|
||||||
@ -182,7 +182,7 @@ function HoldingsSummaryTable({ tokens }: { tokens: TokenInfoWithPubkey[] }) {
|
|||||||
<Address pubkey={new PublicKey(mintAddress)} link />
|
<Address pubkey={new PublicKey(mintAddress)} link />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{totalByMint} {tokenDetails && tokenDetails.tokenSymbol}
|
{totalByMint} {tokenDetails && tokenDetails.symbol}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@ -89,16 +89,16 @@ function MintAccountCard({
|
|||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{tokenInfo?.website && (
|
{tokenInfo?.extensions?.website && (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Website</td>
|
<td>Website</td>
|
||||||
<td className="text-lg-right">
|
<td className="text-lg-right">
|
||||||
<a
|
<a
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={tokenInfo.website}
|
href={tokenInfo.extensions.website}
|
||||||
>
|
>
|
||||||
{tokenInfo.website}
|
{tokenInfo.extensions.website}
|
||||||
<span className="fe fe-external-link ml-2"></span>
|
<span className="fe fe-external-link ml-2"></span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -160,7 +160,7 @@ function TokenAccountCard({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
balance = <>{info.tokenAmount.uiAmountString}</>;
|
balance = <>{info.tokenAmount.uiAmountString}</>;
|
||||||
unit = tokenRegistry.get(info.mint.toBase58())?.tokenSymbol || "tokens";
|
unit = tokenRegistry.get(info.mint.toBase58())?.symbol || "tokens";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -50,7 +50,7 @@ import { useCluster, Cluster } from "providers/cluster";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Location } from "history";
|
import { Location } from "history";
|
||||||
import { useQuery } from "utils/url";
|
import { useQuery } from "utils/url";
|
||||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
import { TokenInfoMap } from "@solana/spl-token-registry";
|
||||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||||
|
|
||||||
const TRUNCATE_TOKEN_LENGTH = 10;
|
const TRUNCATE_TOKEN_LENGTH = 10;
|
||||||
@ -608,7 +608,7 @@ function InstructionDetails({
|
|||||||
function formatTokenName(
|
function formatTokenName(
|
||||||
pubkey: string,
|
pubkey: string,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
tokenRegistry: KnownTokenMap
|
tokenRegistry: TokenInfoMap
|
||||||
): string {
|
): string {
|
||||||
let display = displayAddress(pubkey, cluster, tokenRegistry);
|
let display = displayAddress(pubkey, cluster, tokenRegistry);
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export function TokenLargestAccountsCard({ pubkey }: { pubkey: PublicKey }) {
|
|||||||
fetchLargestAccounts,
|
fetchLargestAccounts,
|
||||||
]);
|
]);
|
||||||
const { tokenRegistry } = useTokenRegistry();
|
const { tokenRegistry } = useTokenRegistry();
|
||||||
const unit = tokenRegistry.get(mintAddress)?.tokenSymbol;
|
const unit = tokenRegistry.get(mintAddress)?.symbol;
|
||||||
const unitLabel = unit ? `(${unit})` : "";
|
const unitLabel = unit ? `(${unit})` : "";
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -118,7 +118,7 @@ function TokenInstruction(props: InfoProps) {
|
|||||||
const tokenDetails = tokenRegistry.get(mintAddress);
|
const tokenDetails = tokenRegistry.get(mintAddress);
|
||||||
|
|
||||||
if (tokenDetails) {
|
if (tokenDetails) {
|
||||||
tokenSymbol = tokenDetails.tokenSymbol;
|
tokenSymbol = tokenDetails.symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes.push(
|
attributes.push(
|
||||||
|
@ -50,7 +50,7 @@ export function TokenBalancesCard({ signature }: SignatureProps) {
|
|||||||
|
|
||||||
const accountRows = rows.map(({ account, delta, balance, mint }) => {
|
const accountRows = rows.map(({ account, delta, balance, mint }) => {
|
||||||
const key = account.toBase58() + mint;
|
const key = account.toBase58() + mint;
|
||||||
const units = tokenRegistry.get(mint)?.tokenSymbol || "tokens";
|
const units = tokenRegistry.get(mint)?.symbol || "tokens";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={key}>
|
<tr key={key}>
|
||||||
|
@ -97,11 +97,11 @@ export function AccountHeader({ address }: { address: string }) {
|
|||||||
if (tokenDetails) {
|
if (tokenDetails) {
|
||||||
return (
|
return (
|
||||||
<div className="row align-items-end">
|
<div className="row align-items-end">
|
||||||
{tokenDetails.icon && (
|
{tokenDetails.logoURI && (
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
<div className="avatar avatar-lg header-avatar-top">
|
<div className="avatar avatar-lg header-avatar-top">
|
||||||
<img
|
<img
|
||||||
src={tokenDetails.icon}
|
src={tokenDetails.logoURI}
|
||||||
alt="token logo"
|
alt="token logo"
|
||||||
className="avatar-img rounded-circle border border-4 border-body"
|
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">
|
<div className="col mb-3 ml-n3 ml-md-n2">
|
||||||
<h6 className="header-pretitle">Token</h6>
|
<h6 className="header-pretitle">Token</h6>
|
||||||
<h2 className="header-title">{tokenDetails.tokenName}</h2>
|
<h2 className="header-title">{tokenDetails.name}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,34 +1,35 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
TokenListProvider,
|
TokenListProvider,
|
||||||
KnownToken,
|
TokenInfoMap,
|
||||||
KnownTokenMap,
|
TokenInfo,
|
||||||
|
TokenListContainer,
|
||||||
} from "@solana/spl-token-registry";
|
} from "@solana/spl-token-registry";
|
||||||
import { clusterSlug, useCluster } from "providers/cluster";
|
import { clusterSlug, useCluster } from "providers/cluster";
|
||||||
|
|
||||||
const TokenRegistryContext = React.createContext<KnownTokenMap>(new Map());
|
const TokenRegistryContext = React.createContext<TokenInfoMap>(new Map());
|
||||||
|
|
||||||
type ProviderProps = { children: React.ReactNode };
|
type ProviderProps = { children: React.ReactNode };
|
||||||
|
|
||||||
export function TokenRegistryProvider({ children }: ProviderProps) {
|
export function TokenRegistryProvider({ children }: ProviderProps) {
|
||||||
const [tokenRegistry, setTokenRegistry] = React.useState<KnownTokenMap>(
|
const [tokenRegistry, setTokenRegistry] = React.useState<TokenInfoMap>(
|
||||||
new Map()
|
new Map()
|
||||||
);
|
);
|
||||||
const { cluster } = useCluster();
|
const { cluster } = useCluster();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
new TokenListProvider()
|
new TokenListProvider().resolve().then((tokens: TokenListContainer) => {
|
||||||
.resolve(clusterSlug(cluster))
|
const tokenList = tokens
|
||||||
.then((tokens: KnownToken[]) => {
|
.filterByClusterSlug(clusterSlug(cluster))
|
||||||
setTokenRegistry(
|
.getList();
|
||||||
tokens.reduce((map: KnownTokenMap, item: KnownToken) => {
|
|
||||||
if (item.tokenName && item.tokenSymbol) {
|
setTokenRegistry(
|
||||||
map.set(item.mintAddress, item);
|
tokenList.reduce((map: TokenInfoMap, item: TokenInfo) => {
|
||||||
}
|
map.set(item.address, item);
|
||||||
return map;
|
return map;
|
||||||
}, new Map())
|
}, new Map())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [cluster]);
|
}, [cluster]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { Cluster } from "providers/cluster";
|
import { Cluster } from "providers/cluster";
|
||||||
import { SerumMarketRegistry } from "serumMarketRegistry";
|
import { SerumMarketRegistry } from "serumMarketRegistry";
|
||||||
import { KnownTokenMap } from "@solana/spl-token-registry";
|
import { TokenInfoMap } from "@solana/spl-token-registry";
|
||||||
|
|
||||||
export type ProgramName = typeof PROGRAM_NAME_BY_ID[keyof typeof PROGRAM_NAME_BY_ID];
|
export type ProgramName = typeof PROGRAM_NAME_BY_ID[keyof typeof PROGRAM_NAME_BY_ID];
|
||||||
|
|
||||||
@ -109,14 +109,14 @@ export const SYSVAR_IDS = {
|
|||||||
export function addressLabel(
|
export function addressLabel(
|
||||||
address: string,
|
address: string,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
tokenRegistry?: KnownTokenMap
|
tokenRegistry?: TokenInfoMap
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
return (
|
return (
|
||||||
PROGRAM_NAME_BY_ID[address] ||
|
PROGRAM_NAME_BY_ID[address] ||
|
||||||
LOADER_IDS[address] ||
|
LOADER_IDS[address] ||
|
||||||
SYSVAR_IDS[address] ||
|
SYSVAR_IDS[address] ||
|
||||||
SYSVAR_ID[address] ||
|
SYSVAR_ID[address] ||
|
||||||
tokenRegistry?.get(address)?.tokenName ||
|
tokenRegistry?.get(address)?.name ||
|
||||||
SerumMarketRegistry.get(address, cluster)
|
SerumMarketRegistry.get(address, cluster)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ export function addressLabel(
|
|||||||
export function displayAddress(
|
export function displayAddress(
|
||||||
address: string,
|
address: string,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
tokenRegistry: KnownTokenMap
|
tokenRegistry: TokenInfoMap
|
||||||
): string {
|
): string {
|
||||||
return addressLabel(address, cluster, tokenRegistry) || address;
|
return addressLabel(address, cluster, tokenRegistry) || address;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user