diff --git a/README.md b/README.md index ac80229b26e0..520d344e5e27 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # @solana/spl-token-registry -[![npm](https://img.shields.io/npm/v/@solana/spl-token-registry)](https://unpkg.com/@solana/spl-token-registry@latest/) [![GitHub license](https://img.shields.io/badge/license-APACHE-blue.svg)](https://github.com/solana-labs/token-list/blob/b3fa86b3fdd9c817139e38641d46c5a892542a52/LICENSE) +[![npm](https://img.shields.io/npm/v/@solana/spl-token-registry)](https://unpkg.com/@solana/spl-token-registry@latest/) [![GitHub license](https://img.shields.io/badge/license-APACHE-blue.svg)](https://github.com/solana-labs/token-list/blob/b3fa86b3fdd9c817139e38641d46c5a892542a52/LICENSE) Solana Token Registry is a package that allows application to query for list of tokens. -JSON schema for the tokens includes: name, symbol, logo and mint account address +JSON schema for the tokens includes: chainId, address, name, decimals, symbol, logoURI (optional), tags (optional), and custom extensions metadata. ## Installation @@ -19,12 +19,11 @@ yarn install @solana/spl-token-registry ### Query available tokens -```typescript - -new TokenListProvider().resolve("mainnet-beta").then(tokens => { - console.log(tokens); +```typescript +new TokenListProvider().resolve().then((tokens) => { + const tokenList = tokens.filterByClusterSlug('mainnet-beta').getList(); + console.log(tokenList); }); - ``` ### Render icon for token in React @@ -38,21 +37,23 @@ export const Icon = (props: { mint: string }) => { const [tokenMap, setTokenMap] = useState>(new Map()); useEffect(() => { - new TokenListProvider().resolve("mainnet-beta").then(tokens => { - setTokenMap(tokens.reduce((map, item) => { - map.set(item.mintAddress, item); + new TokenListProvider().resolve().then(tokens => { + const tokenList = tokens.filterByChain(ENV.MainnetBeta).getList(); + + setTokenMap(tokenList.reduce((map, item) => { + map.set(item.address, item); return map; },new Map())); }); }, [setTokenMap]); const token = tokenMap.get(props.mint); - if (!token) return null; + if (!token || !token.logoURI) return null; - return (); + return (); ``` ## Adding new token -Submit PR with changes to JSON file `src/tokens/.json` +Submit PR with changes to JSON file `src/tokens/solana.tokenlist.json` diff --git a/package-lock.json b/package-lock.json index f6f6116bdcc7..5a82c1206c00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@solana/spl-token-registry", - "version": "0.1.6", + "version": "0.1.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/index.ts b/src/index.ts index 96a2caf5220c..64c57b3cec5b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from './lib/tokens'; +export * from './lib/tokenlist'; diff --git a/src/lib/tokenlist.spec.ts b/src/lib/tokenlist.spec.ts index 69011ef13bfd..301c928d8832 100644 --- a/src/lib/tokenlist.spec.ts +++ b/src/lib/tokenlist.spec.ts @@ -3,7 +3,7 @@ import { ENV, Strategy, TokenListProvider } from './tokenlist'; test('Token list is filterable by a tag', async (t) => { const list = (await new TokenListProvider().resolve(Strategy.Static)) - .filterByChain(ENV.MainnetBeta) + .filterByChainId(ENV.MainnetBeta) .filterByTag('nft') .getList(); @@ -12,7 +12,7 @@ test('Token list is filterable by a tag', async (t) => { test('Token list can exclude by a tag', async (t) => { const list = (await new TokenListProvider().resolve(Strategy.Static)) - .filterByChain(ENV.MainnetBeta) + .filterByChainId(ENV.MainnetBeta) .excludeByTag('nft') .getList(); @@ -21,7 +21,7 @@ test('Token list can exclude by a tag', async (t) => { test('Token list can exclude by a chain', async (t) => { const list = (await new TokenListProvider().resolve(Strategy.Static)) - .excludeByChain(ENV.MainnetBeta) + .excludeByChainId(ENV.MainnetBeta) .getList(); t.false(list.some((item) => item.chainId === ENV.MainnetBeta)); diff --git a/src/lib/tokenlist.ts b/src/lib/tokenlist.ts index b191f590a7d9..89c6f8ebbd60 100644 --- a/src/lib/tokenlist.ts +++ b/src/lib/tokenlist.ts @@ -21,6 +21,10 @@ export interface TagDetails { readonly description: string; } +export interface TokenExtensions { + readonly website?: string; +} + export interface TokenInfo { readonly chainId: number; readonly address: string; @@ -29,10 +33,17 @@ export interface TokenInfo { readonly symbol: string; readonly logoURI?: string; readonly tags?: string[]; + readonly extensions?: TokenExtensions; } export type TokenInfoMap = Map; +export const CLUSTER_SLUGS: { [id: string]: ENV } = { + 'mainnet-beta': ENV.MainnetBeta, + testnet: ENV.Testnet, + devnet: ENV.Devnet, +}; + export class GitHubTokenListResolutionStrategy { repositories = [ 'https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json', @@ -101,7 +112,9 @@ export class TokenListProvider { [Strategy.CDN]: new CDNTokenListResolutionStrategy(), }; - resolve = async (strategy: Strategy = Strategy.CDN) => { + resolve = async ( + strategy: Strategy = Strategy.CDN + ): Promise => { return new TokenListContainer( await TokenListProvider.strategies[strategy].resolve() ); @@ -118,12 +131,12 @@ export class TokenListContainer { return this; }; - filterByChain = (chainId: number | ENV) => { + filterByChainId = (chainId: number | ENV) => { this.tokenList = this.tokenList.filter((item) => item.chainId === chainId); return this; }; - excludeByChain = (chainId: number | ENV) => { + excludeByChainId = (chainId: number | ENV) => { this.tokenList = this.tokenList.filter((item) => item.chainId !== chainId); return this; }; @@ -135,6 +148,13 @@ export class TokenListContainer { return this; }; + filterByClusterSlug = (slug: string) => { + if (slug in CLUSTER_SLUGS) { + this.filterByChainId(CLUSTER_SLUGS[slug]); + } + return this; + }; + getList() { return this.tokenList; } diff --git a/src/tokens/solana.tokenlist.json b/src/tokens/solana.tokenlist.json index 5084537051ca..5e5de4375bb2 100644 --- a/src/tokens/solana.tokenlist.json +++ b/src/tokens/solana.tokenlist.json @@ -48,7 +48,10 @@ "name": "USDC", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/f3ffd0b9ae2165336279ce2f8db1981a55ce30f8/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - "tags": ["stablecoin"] + "tags": ["stablecoin"], + "extensions": { + "website": "https://www.centre.io/" + } }, { "chainId": 101, @@ -188,7 +191,10 @@ "name": "Serum", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x476c5E26a75bd202a9683ffD34359C0CC15be0fF/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://projectserum.com/" + } }, { "chainId": 101, @@ -206,7 +212,10 @@ "name": "MegaSerum", "decimals": 0, "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x476c5E26a75bd202a9683ffD34359C0CC15be0fF/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://projectserum.com/" + } }, { "chainId": 101, @@ -275,7 +284,10 @@ "name": "Bonfida", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/dr497/awesome-serum-markets/02ce7c74fd2e9bd4cb55a15f735fc3ad0e7335f6/icons/fida.svg", - "tags": [] + "tags": [], + "extensions": { + "website": "https://bonfida.com/" + } }, { "chainId": 101, @@ -293,7 +305,10 @@ "name": "MAPS", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/explorer/master/public/tokens/maps.svg", - "tags": [] + "tags": [], + "extensions": { + "website": "https://maps.me/" + } }, { "chainId": 101, @@ -302,7 +317,10 @@ "name": "BRZ", "decimals": 4, "logoURI": "https://raw.githubusercontent.com/solana-labs/explorer/master/public/tokens/brz.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://brztoken.io/" + } }, { "chainId": 101, @@ -311,7 +329,10 @@ "name": "USDT", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/explorer/master/public/tokens/usdt.svg", - "tags": ["stablecoin"] + "tags": ["stablecoin"], + "extensions": { + "website": "https://tether.to/" + } }, { "chainId": 101, @@ -320,7 +341,10 @@ "name": "Standard", "decimals": 9, "logoURI": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/2oDxYGgTBmST4rc3yn1YtcSEck7ReDZ8wHWLqZAuNWXH/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://benchmarkprotocol.finance/" + } }, { "chainId": 101, @@ -329,7 +353,10 @@ "name": "Raydium", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://raydium.io/" + } }, { "chainId": 101, @@ -338,7 +365,10 @@ "name": "Raydium LP Token (RAY-USDT)", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://raydium.io/" + } }, { "chainId": 101, @@ -347,7 +377,10 @@ "name": "Raydium LP Token (RAY-USDC)", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://raydium.io/" + } }, { "chainId": 101, @@ -356,7 +389,10 @@ "name": "Raydium LP Token (RAY-SRM)", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://raydium.io/" + } }, { "chainId": 101, @@ -364,7 +400,10 @@ "symbol": "LSD", "name": "LSD", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -372,7 +411,10 @@ "symbol": "Unlimited Energy", "name": "Unlimited Energy", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -380,7 +422,10 @@ "symbol": "Need for Speed", "name": "Need for Speed", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -388,7 +433,10 @@ "symbol": "ADOR OPENS", "name": "ADOR OPENS", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -396,7 +444,10 @@ "symbol": "CMS - Rare", "name": "CMS - Rare", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -404,7 +455,10 @@ "symbol": "Tesla", "name": "Tesla", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -412,7 +466,10 @@ "symbol": "DeceFi", "name": "DeceFi", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -420,7 +477,10 @@ "symbol": "Power User", "name": "Power User", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -428,7 +488,10 @@ "symbol": "VIP Member", "name": "VIP Member", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -436,7 +499,10 @@ "symbol": "Uni Christmas", "name": "Uni Christmas", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -444,7 +510,10 @@ "symbol": "BNB", "name": "BNB", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -452,7 +521,10 @@ "symbol": "Seldom", "name": "Seldom", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -460,7 +532,10 @@ "symbol": "Satoshi Closeup", "name": "Satoshi Closeup", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -468,7 +543,10 @@ "symbol": "Satoshi GB", "name": "Satoshi GB", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -476,7 +554,10 @@ "symbol": "Satoshi OG", "name": "Satoshi OG", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -484,7 +565,10 @@ "symbol": "Satoshi BTC", "name": "Satoshi BTC", "decimals": 10, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -492,7 +576,10 @@ "symbol": "Satoshi GB", "name": "Satoshi GB", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -500,7 +587,10 @@ "symbol": "Satoshi", "name": "Satoshi", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -508,7 +598,10 @@ "symbol": "Satoshi Closeup", "name": "Satoshi Closeup", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -516,7 +609,10 @@ "symbol": "Satoshi BTC", "name": "Satoshi BTC", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -524,7 +620,10 @@ "symbol": "Satoshi Nakamoto", "name": "Satoshi Nakamoto", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -532,7 +631,10 @@ "symbol": "Charles Hoskinson", "name": "Charles Hoskinson", "decimals": 9, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -540,7 +642,10 @@ "symbol": "SBF", "name": "SBF", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -548,7 +653,10 @@ "symbol": "Bitcoin Tram", "name": "Bitcoin Tram", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -556,7 +664,10 @@ "symbol": "SRM tee-shirt", "name": "SRM tee-shirt", "decimals": 0, - "tags": ["nft"] + "tags": ["nft"], + "extensions": { + "website": "https://solible.com/" + } }, { "chainId": 101, @@ -565,7 +676,10 @@ "name": "PERK", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/perkexchange/assets/master/logos/SPL-token/logo.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://perk.exchange/" + } }, { "chainId": 101, @@ -574,7 +688,10 @@ "name": "BitSong", "decimals": 6, "logoURI": "https://raw.githubusercontent.com/bitsongofficial/assets/master/logo_128x128.png", - "tags": [] + "tags": [], + "extensions": { + "website": "https://bitsong.io/" + } }, { "chainId": 102,