On this page
Solana Dev Kit
Copy-ready snippets for wallets, tokens, authorities, rent, LP and fees, plus a field guide to every number in the WorldScope scanner. Free, no sign-up.
Never paste a seed phrase or private key into a website or front-end code. Test on devnet first. Educational only, not financial advice.
npm i @solana/web3.js @solana/spl-tokenNo matches. Try another word or pick All.
Wallet
Connect a wallet, read balances and ask the user to sign.
Wallet connect button (React)
Wraps your app in the Solana Wallet Adapter providers and renders a ready-made Connect button.
When to use: Any React or Next.js dApp that needs users to connect Phantom, Solflare, Backpack or another wallet.
npm i @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-react-uiimport { useMemo } from 'react';
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import { clusterApiUrl } from '@solana/web3.js';
import '@solana/wallet-adapter-react-ui/styles.css';
export default function App() {
const endpoint = useMemo(() => clusterApiUrl('devnet'), []);
// Wallets that support the Wallet Standard (Phantom, Solflare, Backpack...)
// are detected automatically, so the list can stay empty.
const wallets = useMemo(() => [], []);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<WalletMultiButton />
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}Send SOL from the connected wallet
Builds a SOL transfer, asks the connected wallet to sign and send it, then waits for confirmation.
When to use: Tips, payments or any action where the user pays from their own wallet. Must sit inside the providers above.
npm i @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-react-uiimport { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
export function SendSol() {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
async function send() {
if (!publicKey) throw new Error('Connect a wallet first');
const tx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: new PublicKey('RECIPIENT_ADDRESS'),
lamports: Math.round(0.01 * LAMPORTS_PER_SOL),
})
);
const {
context: { slot: minContextSlot },
value: { blockhash, lastValidBlockHeight },
} = await connection.getLatestBlockhashAndContext();
const signature = await sendTransaction(tx, connection, { minContextSlot });
await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature }, 'confirmed');
console.log('Sent:', signature);
}
return <button onClick={send} disabled={!publicKey}>Send 0.01 SOL</button>;
}Connect without React (injected provider)
Connects to the wallet the browser extension injects, with no framework and no build step.
When to use: Plain HTML pages, quick prototypes and Blogger or WordPress widgets.
const provider = window.phantom?.solana || window.solflare || window.backpack;
if (!provider) throw new Error('No Solana wallet found. Install Phantom, Solflare or Backpack.');
const { publicKey } = await provider.connect();
console.log('Connected:', publicKey.toString());
// Later, to disconnect:
// await provider.disconnect();Read a SOL balance
Fetches any address's SOL balance and converts lamports to SOL.
When to use: Showing a balance, or checking a payer has enough SOL before you build a transaction.
npm i @solana/web3.jsimport { Connection, PublicKey, LAMPORTS_PER_SOL, clusterApiUrl } from '@solana/web3.js';
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const owner = new PublicKey('YOUR_WALLET_ADDRESS');
const lamports = await connection.getBalance(owner);
console.log(lamports / LAMPORTS_PER_SOL, 'SOL'); // 1 SOL = 1,000,000,000 lamportsCreate Token
Create an SPL token, mint the supply and move tokens.
Create a token and mint the supply
Creates an SPL token mint with 6 decimals, opens your token account and mints 1,000,000,000 tokens into it.
When to use: Scripts and back-ends on devnet. On mainnet, load your own keypair from a secure file instead of generating one.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, Keypair, LAMPORTS_PER_SOL, clusterApiUrl } from '@solana/web3.js';
import { createMint, getOrCreateAssociatedTokenAccount, mintTo } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const payer = Keypair.generate(); // devnet only
// Fund the payer on devnet (the faucet is rate limited)
const airdrop = await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
const latest = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: airdrop, ...latest }, 'confirmed');
const decimals = 6;
const mint = await createMint(
connection,
payer, // pays the fees and rent
payer.publicKey, // mint authority
payer.publicKey, // freeze authority (pass null for none)
decimals
);
const ata = await getOrCreateAssociatedTokenAccount(connection, payer, mint, payer.publicKey);
await mintTo(connection, payer, mint, ata.address, payer, 1_000_000_000n * 10n ** BigInt(decimals));
console.log('Mint:', mint.toBase58());Send tokens to another wallet
Transfers SPL tokens, creating the recipient's token account first if it does not exist yet.
When to use: Airdrops, payouts and moving tokens between your own wallets.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { getMint, getOrCreateAssociatedTokenAccount, transferChecked } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const mint = new PublicKey('YOUR_MINT_ADDRESS');
const recipient = new PublicKey('RECIPIENT_WALLET');
// payer: your Keypair (owner of the tokens)
const { decimals } = await getMint(connection, mint);
const from = await getOrCreateAssociatedTokenAccount(connection, payer, mint, payer.publicKey);
const to = await getOrCreateAssociatedTokenAccount(connection, payer, mint, recipient);
const amount = 250n * 10n ** BigInt(decimals); // 250 tokens
const sig = await transferChecked(connection, payer, from.address, mint, to.address, payer, amount, decimals);
console.log('Transfer:', sig);Authorities
Mint and freeze authority: check them, revoke them, understand them.
Revoke mint and freeze authority
Sets the mint authority and the freeze authority to none, so no one can ever print more tokens or freeze holders.
When to use: Right after minting the final supply. This is permanent and cannot be undone.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { setAuthority, AuthorityType } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const mint = new PublicKey('YOUR_MINT_ADDRESS');
// payer: the Keypair that is the current mint and freeze authority
await setAuthority(connection, payer, mint, payer, AuthorityType.MintTokens, null);
await setAuthority(connection, payer, mint, payer, AuthorityType.FreezeAccount, null);
console.log('Mint and freeze authority revoked');Revoke both authorities with the user's wallet
Puts both revoke instructions in one transaction and lets the connected wallet sign it.
When to use: When the authority lives in a browser wallet (Phantom, Solflare) instead of a server keypair.
npm i @solana/web3.js @solana/spl-token @solana/wallet-adapter-reactimport { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { PublicKey, Transaction } from '@solana/web3.js';
import { AuthorityType, createSetAuthorityInstruction } from '@solana/spl-token';
export function useRevokeAuthorities() {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
return async (mintAddress: string) => {
if (!publicKey) throw new Error('Connect a wallet first');
const mint = new PublicKey(mintAddress);
const tx = new Transaction().add(
createSetAuthorityInstruction(mint, publicKey, AuthorityType.MintTokens, null),
createSetAuthorityInstruction(mint, publicKey, AuthorityType.FreezeAccount, null)
);
const latest = await connection.getLatestBlockhash();
tx.recentBlockhash = latest.blockhash;
tx.feePayer = publicKey;
const signature = await sendTransaction(tx, connection);
await connection.confirmTransaction({ signature, ...latest }, 'confirmed');
return signature;
};
}Check a token's authorities and supply
Reads a mint and tells you whether mint and freeze authority are still active, plus the real supply.
When to use: Before you buy or list any token. This is the same first check the scanner runs.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { getMint } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('mainnet-beta'), 'confirmed');
const mint = await getMint(connection, new PublicKey('TOKEN_MINT_ADDRESS'));
console.log('Decimals:', mint.decimals);
console.log('Supply:', Number(mint.supply) / 10 ** mint.decimals);
console.log('Mint authority:', mint.mintAuthority ? mint.mintAuthority.toBase58() + ' (can print more!)' : 'revoked');
console.log('Freeze authority:', mint.freezeAuthority ? mint.freezeAuthority.toBase58() + ' (can freeze wallets!)' : 'revoked');
// Token-2022 mints: pass TOKEN_2022_PROGRAM_ID as the 4th argument of getMintFreeze or thaw (unlock) a token account
Locks a holder's token account so it cannot send tokens, or unlocks it again. Only works while the freeze authority is active.
When to use: Regulated or test tokens. For a meme coin, an active freeze authority is a red flag, so revoke it instead.
npm i @solana/web3.js @solana/spl-tokenimport { PublicKey } from '@solana/web3.js';
import { freezeAccount, thawAccount } from '@solana/spl-token';
// connection, payer (Keypair = freeze authority) as in the snippets above
const mint = new PublicKey('YOUR_MINT_ADDRESS');
const tokenAccount = new PublicKey('HOLDER_TOKEN_ACCOUNT');
await freezeAccount(connection, payer, tokenAccount, mint, payer); // lock
await thawAccount(connection, payer, tokenAccount, mint, payer); // unlockAccounts & Rent
Token accounts hold rent. List them, burn dust, close them, get SOL back.
List every token account in a wallet
Lists all classic SPL and Token-2022 accounts a wallet owns, with balances, and flags the empty ones.
When to use: Before cleaning up a wallet, or to build a portfolio view.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('mainnet-beta'), 'confirmed');
const owner = new PublicKey('YOUR_WALLET_ADDRESS');
for (const programId of [TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID]) {
const { value } = await connection.getParsedTokenAccountsByOwner(owner, { programId });
for (const { pubkey, account } of value) {
const info = account.data.parsed.info;
const empty = info.tokenAmount.amount === '0';
console.log(pubkey.toBase58(), info.mint, info.tokenAmount.uiAmountString, empty ? 'EMPTY' : '');
}
}Close empty token accounts and get the rent back
Closes up to 20 empty classic SPL token accounts in one transaction and sends their rent to your wallet.
When to use: Wallet cleanup. Each empty account holds about 0.002 SOL of rent that comes back when it is closed.
npm i @solana/web3.js @solana/spl-token @solana/wallet-adapter-reactimport { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { Transaction } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID, createCloseAccountInstruction } from '@solana/spl-token';
export function useCloseEmptyAccounts() {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
return async () => {
if (!publicKey) throw new Error('Connect a wallet first');
const { value } = await connection.getParsedTokenAccountsByOwner(publicKey, { programId: TOKEN_PROGRAM_ID });
const empty = value.filter((a) => a.account.data.parsed.info.tokenAmount.amount === '0').slice(0, 20);
if (!empty.length) return null;
const tx = new Transaction();
for (const { pubkey } of empty) {
// account to close, where the rent goes, owner who signs
tx.add(createCloseAccountInstruction(pubkey, publicKey, publicKey));
}
const latest = await connection.getLatestBlockhash();
tx.recentBlockhash = latest.blockhash;
tx.feePayer = publicKey;
const signature = await sendTransaction(tx, connection);
await connection.confirmTransaction({ signature, ...latest }, 'confirmed');
return signature;
};
}Burn leftover dust, then close the account
Burns whatever balance is left in a token account and closes it in the same transaction.
When to use: Worthless dust you never want back. A token account can only be closed once its balance is zero.
npm i @solana/web3.js @solana/spl-tokenimport { PublicKey, Transaction, sendAndConfirmTransaction } from '@solana/web3.js';
import { getAccount, getMint, createBurnCheckedInstruction, createCloseAccountInstruction } from '@solana/spl-token';
// connection and owner (Keypair) as in the snippets above
const tokenAccount = new PublicKey('TOKEN_ACCOUNT_TO_CLEAN');
const acc = await getAccount(connection, tokenAccount);
const { decimals } = await getMint(connection, acc.mint);
const tx = new Transaction();
if (acc.amount > 0n) {
tx.add(createBurnCheckedInstruction(tokenAccount, acc.mint, owner.publicKey, acc.amount, decimals));
}
tx.add(createCloseAccountInstruction(tokenAccount, owner.publicKey, owner.publicKey));
const sig = await sendAndConfirmTransaction(connection, tx, [owner]);
console.log('Burned and closed:', sig);How much rent does an account lock?
Asks the network for the rent-exempt minimum of a 165-byte token account and of a 82-byte mint.
When to use: Estimating costs before creating accounts, or what closing accounts will return.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, LAMPORTS_PER_SOL, clusterApiUrl } from '@solana/web3.js';
import { ACCOUNT_SIZE, MINT_SIZE } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const tokenAccountRent = await connection.getMinimumBalanceForRentExemption(ACCOUNT_SIZE); // 165 bytes
const mintRent = await connection.getMinimumBalanceForRentExemption(MINT_SIZE); // 82 bytes
console.log('Token account rent:', tokenAccountRent / LAMPORTS_PER_SOL, 'SOL'); // about 0.00204
console.log('Mint rent:', mintRent / LAMPORTS_PER_SOL, 'SOL');Liquidity / LP
Liquidity pool tokens decide whether a pool can be pulled.
Burn all your LP tokens
Burns your whole balance of a pool's LP token, so that share of liquidity can never be withdrawn.
When to use: After adding liquidity, to prove the pool cannot be pulled. Permanent: burned LP is gone for good.
npm i @solana/web3.js @solana/spl-token @solana/wallet-adapter-reactimport { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { PublicKey, Transaction } from '@solana/web3.js';
import { getAssociatedTokenAddressSync, getAccount, getMint, createBurnCheckedInstruction } from '@solana/spl-token';
export function useBurnLp() {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
return async (lpMintAddress: string) => {
if (!publicKey) throw new Error('Connect a wallet first');
const lpMint = new PublicKey(lpMintAddress);
const ata = getAssociatedTokenAddressSync(lpMint, publicKey);
const { amount } = await getAccount(connection, ata);
const { decimals } = await getMint(connection, lpMint);
if (amount === 0n) throw new Error('No LP tokens in this wallet');
const tx = new Transaction().add(createBurnCheckedInstruction(ata, lpMint, publicKey, amount, decimals));
const latest = await connection.getLatestBlockhash();
tx.recentBlockhash = latest.blockhash;
tx.feePayer = publicKey;
const signature = await sendTransaction(tx, connection);
await connection.confirmTransaction({ signature, ...latest }, 'confirmed');
return signature;
};
}Check who holds a pool's LP tokens
Reads the LP mint supply and its largest holders, so you can see how much is held by one wallet versus burned or locked.
When to use: Before buying a token: if one normal wallet holds most of the LP, the pool can be pulled.
npm i @solana/web3.js @solana/spl-tokenimport { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { getMint } from '@solana/spl-token';
const connection = new Connection(clusterApiUrl('mainnet-beta'), 'confirmed');
const lpMint = new PublicKey('LP_MINT_ADDRESS');
const mint = await getMint(connection, lpMint);
const supply = Number(mint.supply);
console.log('LP supply left:', supply / 10 ** mint.decimals, '(burned LP no longer counts)');
const { value } = await connection.getTokenLargestAccounts(lpMint);
for (const a of value) {
const pct = supply ? (Number(a.amount) / supply) * 100 : 0;
console.log(a.address.toBase58(), pct.toFixed(2) + '%');
}
// Then look up who owns the top accounts: a locker program is fine,
// a normal wallet holding most of the LP is a rug risk.Transactions & Fees
Priority fees, simulation and reliable confirmation.
Add a priority fee
Sets a compute-unit limit and a price per compute unit, so your transaction lands faster when the network is busy.
When to use: Busy launches and mints, or any transaction that keeps expiring.
npm i @solana/web3.jsimport { ComputeBudgetProgram, Transaction } from '@solana/web3.js';
// connection as above; instructions = your own instructions
const recent = await connection.getRecentPrioritizationFees();
const fees = recent.map((f) => f.prioritizationFee).sort((a, b) => a - b);
const microLamports = fees.length ? fees[Math.floor(fees.length * 0.75)] : 1000; // 75th percentile
const tx = new Transaction().add(
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports }),
...instructions
);
// Priority fee paid = units x microLamports / 1,000,000 lamportsSimulate before you send
Runs the transaction on an RPC node without sending it and prints errors, logs and compute units used.
When to use: Before every mainnet send, and to size the compute-unit limit for the priority fee.
npm i @solana/web3.jsimport { TransactionMessage, VersionedTransaction } from '@solana/web3.js';
// connection, payer (PublicKey), instructions as above
const { blockhash } = await connection.getLatestBlockhash();
const message = new TransactionMessage({
payerKey: payer,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();
const sim = await connection.simulateTransaction(new VersionedTransaction(message), { sigVerify: false });
console.log('Error:', sim.value.err); // null = it would succeed
console.log('Units used:', sim.value.unitsConsumed);
console.log(sim.value.logs?.join('\n'));Send, retry and confirm reliably
Signs a transaction, sends it with retries and confirms it against its blockhash expiry, so you never wait forever.
When to use: Back-end scripts and bots where a dropped transaction must be detected and resent.
npm i @solana/web3.js// connection, tx (Transaction), signer (Keypair) as above
const latest = await connection.getLatestBlockhash('confirmed');
tx.recentBlockhash = latest.blockhash;
tx.feePayer = signer.publicKey;
tx.sign(signer);
const signature = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false, // keep the free simulation check
maxRetries: 5,
});
const { value } = await connection.confirmTransaction({ signature, ...latest }, 'confirmed');
if (value.err) throw new Error('Failed: ' + JSON.stringify(value.err));
console.log('Confirmed:', signature);
// If this throws TransactionExpiredBlockheightExceededError, rebuild with a fresh blockhash and resend.Errors & Fixes
The errors developers hit most, what they mean and the fix.
| Error | What it means | Fix |
|---|---|---|
Blockhash not found / TransactionExpiredBlockheightExceededError | The transaction waited too long and its blockhash expired (about 60-90 seconds). | Get a fresh blockhash, sign again and resend. Add a priority fee when the network is busy. See snippet |
Attempt to debit an account but found no record of a prior credit | The fee payer has 0 SOL on this cluster. Devnet and mainnet balances are separate. | Fund the payer on the cluster you are using (devnet faucet: faucet.solana.com). |
Transaction results in an account with insufficient funds for rent | An account would drop below its rent-exempt minimum. | Leave the rent-exempt amount in the account, or send a little more SOL. See snippet |
custom program error: 0x1 | Token program InsufficientFunds: not enough tokens in the source account. | Check the balance and the decimals. 1 token with 6 decimals is 1,000,000 base units. |
custom program error: 0x3 | MintMismatch: the token account belongs to a different mint. | Derive the account for this exact mint with getAssociatedTokenAddressSync. See snippet |
custom program error: 0x4 | OwnerMismatch: the signer does not own that token account. | Sign with the account's owner, or use the owner's own associated token account. |
custom program error: 0x5 | FixedSupply: the mint authority was revoked, so no more tokens can be minted. | Expected after a revoke. Mint the full supply before revoking. See snippet |
custom program error: 0xb | NonNativeHasBalance: you tried to close a token account that still holds tokens. | Transfer or burn the balance first, then close. See snippet |
custom program error: 0x11 | AccountFrozen: the freeze authority froze this token account. | Only the freeze authority can thaw it. On a meme coin, treat this as a red flag. See snippet |
custom program error: 0x12 | MintDecimalsMismatch: the decimals passed to a *Checked instruction are wrong. | Read decimals with getMint() instead of hard-coding them. See snippet |
TokenAccountNotFoundError | The wallet has no token account for this mint yet. | Add createAssociatedTokenAccountIdempotentInstruction before the transfer. See snippet |
TokenInvalidAccountOwnerError | The mint or account is owned by Token-2022, but you used the classic Token program (or the reverse). | Detect the owner program and pass that programId to every spl-token call. See snippet |
Transaction too large | A Solana transaction is limited to 1,232 bytes. | Split into smaller batches (for example close 15-20 accounts per transaction) or use address lookup tables. See snippet |
Computational budget exceeded | The transaction used more compute units than its limit. | Simulate to read unitsConsumed, then raise setComputeUnitLimit a little above it. See snippet |
429 Too Many Requests | The public RPC endpoint rate-limited you. | Back off and retry, and use your own RPC provider for anything real. |
WalletSignTransactionError: User rejected the request | The user closed or rejected the wallet pop-up. | Not a bug. Show a clear message and a retry button. |
Fix TokenAccountNotFoundError
Creates the associated token account in the same transaction if it is missing (idempotent, safe to repeat).
When to use: When sending tokens to a wallet that has never held that token.
npm i @solana/web3.js @solana/spl-tokenimport { Transaction } from '@solana/web3.js';
import {
getAssociatedTokenAddressSync,
createAssociatedTokenAccountIdempotentInstruction,
} from '@solana/spl-token';
// payer, owner (PublicKey), mint (PublicKey)
const ata = getAssociatedTokenAddressSync(mint, owner);
const tx = new Transaction().add(
// does nothing if the account already exists
createAssociatedTokenAccountIdempotentInstruction(payer, ata, owner, mint)
// , ...your transfer instruction
);Fix TokenInvalidAccountOwnerError (Token-2022)
Detects whether a mint uses the classic Token program or Token-2022 and passes the right program id.
When to use: When spl-token calls fail on newer tokens (for example pump.fun or Token-2022 mints).
npm i @solana/web3.js @solana/spl-tokenimport { PublicKey } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, getMint } from '@solana/spl-token';
const mintKey = new PublicKey('TOKEN_MINT_ADDRESS');
const info = await connection.getAccountInfo(mintKey);
if (!info) throw new Error('Mint not found on this cluster');
const programId = info.owner.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
const mint = await getMint(connection, mintKey, 'confirmed', programId);
// Pass the same programId to getAssociatedTokenAddressSync, transferChecked, etc.Read the Scanner
Every number the WorldScope Meme Scanner shows, with good, caution and danger ranges.
| Metric | What it means | Good | Caution | Danger |
|---|---|---|---|---|
| Liquidity | Money in the pool that you sell into. Small pools move a lot on small sells and can be drained. | over $50k | $10k to $50k | under $10k |
| FDV vs liquidity | Fully diluted value divided by liquidity. A high ratio means the price is mostly air. | under 50× | 50× to 200× | over 200× |
| Volume / liquidity (24h) | Daily volume compared with the pool. Very high values can mean bots trading with themselves. | under 30× | over 30× with many different buyers | dozens of × with few holders |
| Buy pressure | Share of buys among all transactions. | 40% to 65% | outside 40% to 65% | near 100% buys, almost no sells (honeypot pattern) |
| Top 10 holders | Share of supply held by the ten largest wallets (pool, burn and locker accounts excluded where the data allows). | 30% or less | 30% to 50% | over 50% (over 80% is extreme) |
| Dev / creator balance | Share of supply the creator wallet still holds. Also watch for insiders and serial launchers. | under 5% | 5% to 20% | over 20%, or a dev with 5+ earlier tokens |
| Mint authority | Whether someone can still print new tokens. | revoked | – | active |
| Freeze authority | Whether someone can still freeze your token account so you cannot sell. | revoked | – | active |
| LP burned or locked | Share of liquidity-pool tokens that are burned or locked, so the pool cannot be pulled. | 95% or more | 90% to 95% | under 90%, or unlocked |
| Pair age | How long the trading pair has existed. | over 24 hours | 1 to 24 hours | under 1 hour |
| 24h price change | Size of the price move in the last day. | normal swings | large swings | over 300% in a day |
| Sell tax (EVM chains) | Tax taken when you sell. Also check whether the owner can change it. | under 8% | 8% to 20% | over 20%, or modifiable tax |
| Socials | Website, X or Telegram links on the token. | verified links | new or thin accounts | no socials at all |
The 0-100 risk score
Every warning sign adds points and positives never hide a danger. The total is capped at 100. A confirmed honeypot or rugged token always scores 90 or more.
- 0-19Low
- 20-39Moderate
- 40-59Elevated
- 60-79High
- 80-100Extreme
A low score means the usual warning signs were not found. It does not mean a token is safe or will go up. Data comes from free public APIs and can lag by minutes.
Danger Signs
A checklist of red flags to run before you buy or ship.
One red flag is a reason to slow down. Two or more is a reason to walk away.
FAQ
Is this code safe to paste into production?
Treat every snippet as a starting point. Run it on devnet first, read what each instruction does, simulate before you send on mainnet, and never put a private key or seed phrase in front-end code.
Which libraries do these snippets use?
@solana/web3.js (1.x), @solana/spl-token and the Solana Wallet Adapter. They are the libraries most tutorials, wallets and tools still build on. The newer @solana/kit is the successor of web3.js; the concepts here (mints, token accounts, authorities, rent) are the same in both.
What is the difference between the Token program and Token-2022?
Token-2022 is a newer token program with optional extensions such as transfer fees and on-chain metadata. Accounts and mints of each program can only be used with that program, so pass the right programId (see the TokenInvalidAccountOwnerError fix).
Does revoking mint and freeze authority make a token safe?
No. It removes two common rug tools, but a token can still fail through unlocked liquidity, a dev holding most of the supply, or plain hype. It is a minimum, not a guarantee.
Can I undo a revoke or an LP burn?
No. Setting an authority to none and burning LP tokens are both permanent on-chain actions. That is exactly why buyers trust them.
How much SOL do I get back when I close a token account?
The rent it holds: about 0.002 SOL for a standard 165-byte token account (ask the network with getMinimumBalanceForRentExemption for the exact value). Only empty accounts can be closed, so burn or move any dust first.
Why devnet in the examples?
Devnet SOL is free from the faucet and nothing is at risk. Change clusterApiUrl('devnet') to your mainnet RPC only when the code works end to end.
Will WorldScope ever ask for my seed phrase?
Never. Our tools only ask your wallet to sign a transaction you can read first. Anyone asking for a seed phrase or private key is a scammer.
Is any of this financial advice?
No. This page is educational only. Meme coins are extremely risky and most go to zero.
Free WorldScope tools
Educational only, not financial advice. Code is provided as-is under no warranty; review and test before use. Library docs: solana.com/docs, SPL Token, Wallet Adapter.
Comments