
Solana
- 3 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-master
Reference Solana development - accounts, transactions, programs, PDAs, CPI, fees, JS/Rust clients, SPL tokens, RPC, and payments.
About
A concise reference for building on Solana covering accounts, programs, PDAs, CPI, clients, SPL tokens, RPC, and payments. A developer uses it when building Solana programs, clients, or payment tooling.
- Accounts, programs, PDAs, and CPI concepts
- JS/Rust clients, SPL tokens, RPC, and payments
Solana by the numbers
- 3 all-time installs (skills.sh)
- Ranked #390 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-master --skill solanaAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-master ↗ |
What it does
Reference Solana development - accounts, transactions, programs, PDAs, CPI, fees, JS/Rust clients, SPL tokens, RPC, and payments.
Files
Skill is based on Solana documentation (solana-com), generated 2026-02-09.
Concise reference for building on Solana: accounts, transactions, programs, PDAs, CPI, fees, JavaScript/Rust clients, frontend, SPL tokens, RPC, payments, and terminology.
Core References
| Topic | Description | Reference |
|---|---|---|
| Accounts | Account model, address, keypair, PDA | core-accounts |
| Transactions & Instructions | Tx format, signatures, message, build & send | core-transactions-instructions |
| Versioned Transactions | v0 message, lookup tables, maxSupportedTransactionVersion | core-versioned-transactions |
| Programs & PDA | Programs, PDA derivation, canonical bump | core-programs-pda |
| CPI & Fees | Cross-program invocation, base/priority fees, CU | core-cpi-fees |
| Rent | Rent exemption, getMinimumBalanceForRentExemption, reclaim on close | core-rent |
Clients & Frontend
| Topic | Description | Reference |
|---|---|---|
| JavaScript/TypeScript | @solana/kit, web3.js, @solana/client, SPL | clients-javascript |
| Rust | solana-sdk, solana-client, keypair, RPC | clients-rust |
| React & Next.js | @solana/react-hooks, provider, wallet | frontend-react-nextjs |
Features
| Topic | Description | Reference |
|---|---|---|
| Staking | Stake accounts, delegate/withdraw, warmup/cooldown, merge/split | features-staking |
| Confirmation & Expiration | Blockhash validity, commitment levels, confirmation flow | features-confirmation |
| Actions & Blinks | Solana Actions API, blinks, actions.json | features-actions-blinks |
| Retry & Rebroadcast | maxRetries, lastValidBlockHeight, when to re-sign | features-retry |
| Fee Sponsorship | Fee payer, gas abstraction, fee relayer | features-fee-sponsorship |
| Offline Signing | Serialize, sign off-network, recover, durable nonce | features-offline-signing |
Tokens
| Topic | Description | Reference |
|---|---|---|
| SPL Token Basics | Mint, token account, transfer, ATA, approve, burn | tokens-basics |
| Token-2022 Extensions | Metadata, transfer fees, confidential, hooks | tokens-extensions |
RPC & Payments
| Topic | Description | Reference |
|---|---|---|
| RPC HTTP & WebSocket | getAccountInfo, getBalance, subscriptions | rpc-http-websocket |
| Payments & Solana Pay | Payment URLs, verification, send/accept | payments-solana-pay |
Cookbook & Reference
| Topic | Description | Reference |
|---|---|---|
| Cookbook Recipes | Send SOL, keypair, balance, memo, priority fees | cookbook-recipes |
| Clusters & Terminology | Mainnet, devnet, terms, staking | references-clusters-terminology |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Compute Optimization | CU limits, measurement, logging, data types, PDAs | best-practices-compute |
Generation Info
- Source:
sources/solana(solana-com) - Git SHA:
c5940c1648a0551819fc2942890c701af219a84a - Generated: 2026-02-25
Durable Nonces
Durable nonces replace the recent blockhash in a transaction so the tx does not expire in ~60–90 seconds. Use them for offline signing, scheduled execution, multisig (co-signing over time), or burst submissions without duplicate-blockhash issues.
When to use
- Offline signing: Sign on an air-gapped device; submit later.
- Multisig / DAO: One party signs; others co-sign later (e.g. >90s).
- Scheduled txs: Pre-sign and submit at a future time.
- Burst of txs: Avoid "already processed" from shared recent blockhash.
Concepts
- Nonce account: On-chain account (SystemProgram-owned, rent-exempt) storing the current nonce (32-byte value, often base58).
- Nonce authority: Keypair that can advance the nonce or withdraw SOL from the nonce account.
- Advance nonce: First instruction in a durable tx must be
SystemProgram.nonceAdvance. It consumes the stored nonce and replaces it with a new one, so each durable tx is unique (no double-spend).
CLI (create, advance, use)
# Create authority and nonce account keypairs
solana-keygen new -o nonce-authority.json
solana-keygen new -o nonce-account.json
# Create nonce account (authority pays rent ~0.0015 SOL)
solana create-nonce-account nonce-account.json 0.0015
# Get current nonce (use this as blockhash when building tx)
solana nonce nonce-account.json
# Advance nonce (do after each durable tx, or to get a fresh nonce)
solana new-nonce nonce-account.json
# Sign-only transfer using nonce as blockhash (offline)
solana transfer <RECIPIENT> <AMOUNT> --sign-only --blockhash <NONCE_VALUE> \
--fee-payer co-sender.json --from <SENDER_PUBKEY> --keypair co-sender.json
# Submit later with nonce account (advance is prepended automatically)
solana transfer <RECIPIENT> <AMOUNT> --nonce nonce-account.json \
--nonce-authority nonce-authority.json --blockhash <NONCE_VALUE> \
--from sender.json --keypair sender.json --signer <CO_SIGNER_PUBKEY=SIGNATURE>Web3.js (create nonce account)
const nonceKeypair = Keypair.generate();
const tx = new Transaction();
tx.feePayer = nonceAuthKP.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.add(
SystemProgram.createAccount({
fromPubkey: nonceAuthKP.publicKey,
newAccountPubkey: nonceKeypair.publicKey,
lamports: 0.0015 * LAMPORTS_PER_SOL,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceKeypair.publicKey,
authorizedPubkey: nonceAuthKP.publicKey,
}),
);
tx.sign(nonceKeypair, nonceAuthKP);
await sendAndConfirmRawTransaction(connection, tx.serialize({ requireAllSignatures: false }));Web3.js (build durable transaction)
const accountInfo = await connection.getAccountInfo(nonceKeypair.publicKey);
const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
const advanceIX = SystemProgram.nonceAdvance({
authorizedPubkey: nonceAuthKP.publicKey,
noncePubkey: nonceKeypair.publicKey,
});
const tx = new Transaction();
tx.add(advanceIX);
tx.add(/* your instruction(s) */);
tx.recentBlockhash = nonceAccount.nonce; // use nonce as blockhash
tx.feePayer = payer.publicKey;
tx.sign(nonceAuthKP);
// Optional: serialize and submit later
const serialized = bs58.encode(tx.serialize({ requireAllSignatures: false }));Key points
- First instruction must be
nonceAdvance; runtime uses stored nonce as blockhash and advances it so the same signed tx cannot be replayed. - Nonce account needs ~0.0015 SOL for rent; authority can withdraw when done.
- Fetch current nonce before building each durable tx (or advance once per tx).
<!-- Source references:
- https://github.com/solana-foundation/solana-com (apps/docs/content/guides/advanced/introduction-to-durable-nonces.mdx)
- https://docs.anza.xyz/implemented-proposals/durable-tx-nonces
-->
Address Lookup Tables (ALTs)
Address Lookup Tables store a list of addresses on-chain. A transaction references them by 1-byte index instead of 32-byte address, so you can use up to 64 addresses per transaction (vs 32 without ALTs). Use ALTs when a tx would exceed the legacy account limit (e.g. large swaps, many token accounts).
Requirements
- Versioned transactions (v0) only. Legacy transactions cannot use lookup table addresses. See advanced-versioned-transactions.
- Create and extend the table in separate txs; then use the table in v0 txs.
Create lookup table (web3.js)
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const slot = await connection.getSlot();
const [lookupTableInst, lookupTableAddress] =
web3.AddressLookupTableProgram.createLookupTable({
authority: payer.publicKey,
payer: payer.publicKey,
recentSlot: slot,
});
// Send lookupTableInst in a transaction to create the table on-chainExtend (add addresses)
const extendInstruction = web3.AddressLookupTableProgram.extendLookupTable({
payer: payer.publicKey,
authority: payer.publicKey,
lookupTable: lookupTableAddress,
addresses: [
payer.publicKey,
web3.SystemProgram.programId,
// add more; ~20 per tx due to legacy tx size if using legacy for extend
],
});
// Send extendInstruction in a transactionExtending is limited by tx size; use multiple extend txs to add many addresses.
Fetch table
const lookupTableAccount = (
await connection.getAddressLookupTable(lookupTableAddress)
).value;
// lookupTableAccount.state.addresses — array of PublicKeyUse in a v0 transaction
const messageV0 = new web3.TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash: blockhash,
instructions: arrayOfInstructions,
}).compileToV0Message([lookupTableAccount]);
const transactionV0 = new web3.VersionedTransaction(messageV0);
transactionV0.sign([payer]);
// Must sign before send; do not pass signers to sendAndConfirmTransaction
const txid = await connection.sendRawTransaction(transactionV0.serialize(), { ... });Instructions are built the same way; the message compiles to v0 and includes the lookup table so addresses resolve from the table.
Key points
- Create with
createLookupTable(slot + authority + payer); extend withextendLookupTable. - Use
getAddressLookupTableto load table, thencompileToV0Message([lookupTableAccount])so the v0 message can reference table indices. - VersionedTransaction must be signed before calling
sendRawTransaction(no signer array in send).
<!-- Source references:
- https://github.com/solana-foundation/solana-com (apps/docs/content/guides/advanced/lookup-tables.mdx)
- https://docs.anza.xyz/proposals/versioned-transactions
-->
Solana Best Practices — Compute Optimization
Minimizing compute usage improves inclusion likelihood, lowers priority fees, and keeps programs composable. Agents that build or simulate transactions should be aware of CU limits and optimization patterns.
Compute limits
- Max per transaction: 1.4 million CU
- Max per account per block: 12 million CU
- Max per block: 60 million CU
Hitting per-account-per-block limit can throttle high-throughput use of a single program.
Measuring compute (Rust programs)
Use the compute_fn! macro to measure snippets:
compute_fn!("My message" => {
// code to measure
});Output shows CU before/after. Store and reuse PDA bumps instead of calling find_program_address repeatedly to save CU.
Optimization patterns
- Logging: Avoid non-essential logs; base58 and string concatenation are expensive. Prefer
.key().log()for pubkeys. - Data types: Prefer smaller types (e.g.
u8) when sufficient; larger types cost more CU. - Serialization: Prefer zero-copy / direct account data access where possible; can cut serialization CU significantly.
- PDAs: Store the bump in an account and use
create_program_addresswith that bump instead offind_program_addressin hot paths.
Client-side (agents)
- Simulate transactions to choose a reasonable
SetComputeUnitLimit; addSetComputeUnitPricefor priority when needed. - Prefer versioned transactions and lookup tables to reduce transaction size and thus cost when many accounts are involved.
Key points
- Stay under 1.4M CU per transaction; watch per-account-per-block usage at scale.
- Measure with compute_fn!; reduce logging, use smaller types and zero-copy; cache PDA bumps.
<!-- Source: https://solana.com/developers/guides/advanced/how-to-optimize-compute, https://github.com/solana-foundation/solana-com -->
Solana — JavaScript/TypeScript Clients
Recommended: @solana/kit
- @solana/kit: Main SDK (addresses, transactions, RPC, subscriptions). Use for new code.
- @solana-program/system, @solana-program/token, @solana-program/token-2022, @solana-program/memo, @solana-program/compute-budget: Program helpers.
- RPC:
createSolanaRpc(url),createSolanaRpcSubscriptions(wsUrl). Userpc.getLatestBlockhash().send(),rpc.getAccountInfo(...).send(), etc. - Transactions:
createTransactionMessage,appendTransactionMessageInstructions,signTransactionMessageWithSigners,sendAndConfirmTransactionFactory.
Legacy: @solana/web3.js
- @solana/web3.js: Connection, Keypair, Transaction, sendAndConfirmTransaction.
- @solana/spl-token: Token, Token-2022, Associated Token Account.
- @solana/spl-memo: Memo program.
- Use for compatibility with existing codebases.
Headless: @solana/client
- @solana/client: Headless runtime (RPC, wallets, transactions, subscriptions). For non-React apps that need a single store.
Sending SOL (Kit)
import { getTransferSolInstruction } from "@solana-program/system";
const ix = getTransferSolInstruction({
source: sender,
destination: recipientAddress,
amount: lamports(amountLamports)
});
// Append ix to transaction message, sign, send.Key points
- Prefer Kit + program packages for new projects; use web3.js + @solana/spl-token where legacy is required.
- Always use a commitment (e.g.
confirmed) for sendAndConfirm and RPC calls where consistency matters.
<!-- Source references:
- https://solana.com/docs/clients/official/javascript
- https://solana.com/docs/frontend/client
- https://github.com/solana-foundation/solana-com
-->
Solana — Rust Client
Crates
- solana_sdk: Keypair, Pubkey, Transaction, Instruction, signature::Signer.
- solana_client: RpcClient (sync), nonblocking::rpc_client::RpcClient (async). Connection to RPC.
- solana_system_interface, solana_program: Instruction builders and program IDs for system/SPL.
Keypair and PDA
use solana_sdk::signer::{keypair::Keypair, Signer};
let keypair = Keypair::new();
let pubkey = keypair.pubkey();
use solana_sdk::pubkey::Pubkey;
let (pda, bump) = Pubkey::find_program_address(&[b"seed"], &program_id);Building and sending a transaction
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::transaction::Transaction;
let rpc = RpcClient::new(url);
let blockhash = rpc.get_latest_blockhash().await?;
let mut tx = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
tx.sign(&[&payer], blockhash);
let sig = rpc.send_and_confirm_transaction(&tx).await?;RPC
get_latest_blockhash,get_account_info,get_balance,get_multiple_accounts,send_transaction,send_and_confirm_transaction.- For subscriptions use solana_client with WebSocket (e.g.
RpcClientwith ws support or a dedicated subscription API).
Key points
- Implement
Signerfor custom signers (e.g. hardware). Keypair implements it. - Use
send_and_confirm_transactionwith retries/commitment for production; handle blockhash expiry.
<!-- Source references:
- https://solana.com/docs/clients/official/rust
- https://github.com/solana-foundation/solana-com
-->
Solana — Cookbook Recipes
Send SOL (Kit)
import { getTransferSolInstruction } from "@solana-program/system";
const ix = getTransferSolInstruction({
source: sender,
destination: recipientAddress,
amount: lamports(amountLamports)
});
// Add to transaction, set fee payer and blockhash, sign, sendAndConfirm.Keypair: create, load, restore
- Create:
generateKeyPairSigner()(Kit) orKeypair.generate()(web3.js). - Load from file: Use keypair bytes or JSON; Kit/web3.js have helpers (e.g. from secret key).
- Restore from mnemonic: Use a BIP39/HD wallet library (e.g. @solana/web3.js with bip39) to derive keypair; not in core SDK.
Get balance
- SOL:
rpc.getBalance(address).send()(Kit) orconnection.getBalance(publicKey)(web3.js). - Token: Get token account(s) for owner (e.g. ATA), parse token account data for amount (Kit: token program helpers; web3.js: getAccount with @solana/spl-token parsing).
Add memo to transaction
- Append Memo program instruction with memo string. @solana-program/memo (Kit) or @solana/spl-memo (legacy): build instruction with memo bytes, add to transaction.
Priority fees (Compute Budget)
- Add SetComputeUnitLimit and SetComputeUnitPrice instructions (Compute Budget program). Kit: @solana-program/compute-budget; web3.js: build from compute budget IDs. Simulate first to choose CU limit; set price per CU for priority.
Connect to environment
- Mainnet:
https://api.mainnet-beta.solana.com(or preferred RPC). Devnet:https://api.devnet.solana.com. Test validator:http://localhost:8899(RPC),ws://localhost:8900(WebSocket).
Key points
- Always set transaction lifetime (blockhash) and fee payer before signing.
- For production: use commitment (e.g. confirmed), retries, and (if needed) priority fees.
<!-- Source references:
- https://solana.com/developers/cookbook
- https://github.com/solana-foundation/solana-com
-->
Solana Core — Accounts
All data on Solana is stored in accounts. The ledger is a key-value store: key = 32-byte address, value = account (data + metadata).
Account address
- Public key: Ed25519 public key; private key signs transactions. Shown as base58.
- Program-derived address (PDA): Deterministically derived from program ID + seeds; no private key; programs can sign for PDAs.
Keypair (TypeScript)
// @solana/kit (recommended)
import { generateKeyPairSigner } from "@solana/kit";
const signer = await generateKeyPairSigner();
// Legacy @solana/web3.js
import { Keypair } from "@solana/web3.js";
const keypair = Keypair.generate();
// keypair.publicKey, keypair.secretKeyPDA derivation (TypeScript)
- Kit:
getProgramDerivedAddress(programId, seeds)from@solana/kit. - Legacy:
findProgramAddressSync(seeds, programId)from@solana/web3.js. - Returns
[address, bump]. Bump is the canonical byte (255 down to 0) that makes the address off-curve.
Account structure
- lamports: Balance (1 SOL = 10^9 lamports).
- owner: Program that owns the account (only owner can modify).
- data: Opaque bytes (program-defined).
- executable: Whether the account is a program (executable).
Key points
- Only the owning program can change account data.
- Rent: accounts can be rent-exempt if they hold enough lamports (or are system-owned).
- Use PDAs for deterministic, program-controlled addresses (e.g. per-user state).
<!-- Source references:
- https://solana.com/docs/core/accounts
- https://github.com/solana-foundation/solana-com
-->
Solana Core — CPI and Fees
Cross-program invocation (CPI)
- CPI: One program calls an instruction of another program. Enables composability.
- Account permissions (signer, writable) flow from caller to callee; max call depth is 4 (stack height 5).
- For PDA signers: use
invoke_signed(instruction, account_infos, signers_seeds)(Rust). Pass the seeds and bump used to derive the PDA so the runtime can verify and add the PDA as signer.
Transaction fees
- Base fee: 5000 lamports per signature. Paid by first signer (must be System Program–owned). 50% burned, 50% to validator.
- Prioritization fee: Optional.
priority_fee = CU_limit * CU_price; 100% to validator. Use Compute Budget instructions to setSetComputeUnitLimitandSetComputeUnitPrice.
Compute units
- Default: 200,000 CU per instruction, 1.4M per transaction. Override with Compute Budget program instructions.
- Set CU limit to estimated usage + ~10% margin to avoid overpaying for unused CUs.
- Simulate the transaction to estimate CUs before sending.
Key points
- CPI with PDA: always pass the same seeds (including bump) in
signers_seedsthat were used to derive the PDA. - Priority fee is charged on the requested CU limit, not actual usage—tune limit to avoid waste.
<!-- Source references:
- https://solana.com/docs/core/cpi
- https://solana.com/docs/core/fees
- https://github.com/solana-foundation/solana-com
-->
Solana Core — Programs and PDAs
Programs
- On Solana, programs are the executable (smart contracts). Stored in executable accounts.
- Each program exposes instructions; clients send transactions containing those instructions.
- System Program, Token Program, Token-2022, Compute Budget, etc. are built-in programs.
Program-derived address (PDA)
- PDA = address derived from
(program_id, seeds)so it lies off the Ed25519 curve (no private key). - Canonical bump: Single byte 255→0 used in derivation; first valid off-curve bump is stored and reused.
- Programs can sign for PDAs derived from their program ID (via CPI with
invoke_signedand signer seeds).
Derivation (Rust)
use solana_sdk::pubkey::Pubkey;
pubkey::find_program_address(&[b"seed", owner.as_ref()], &program_id)
// returns (PDA, bump)Creating PDA accounts
- Deriving the address does not create the account. The program must allocate and assign owner in an instruction (e.g.
create_accountor program-specific init). - Pass the PDA and bump (and seeds) when the program needs to sign for the PDA in CPIs.
Key points
- Use PDAs for deterministic addresses (e.g. user state, vaults) and for program signing without keypairs.
- Always store and use the canonical bump when invoking instructions that require the PDA as signer.
<!-- Source references:
- https://solana.com/docs/core/programs
- https://solana.com/docs/core/pda
- https://github.com/solana-foundation/solana-com
-->
Solana Core — Rent
Accounts pay rent for storage. Accounts that hold at least the rent-exempt minimum are not charged rent and can exist indefinitely. When an account is closed, lamports (including rent-exempt balance) are reclaimed by the close authority or the account owner.
Minimum balance for rent exemption
- RPC:
getMinimumBalanceForRentExemption(space)— returns lamports required for the given account data size (in bytes) to be rent-exempt. - Kit:
rpc.getMinimumBalanceForRentExemption(space).send()(space as bigint). - web3.js:
connection.getMinimumBalanceForRentExemption(space). - Rust:
rpc_client.get_minimum_balance_for_rent_exemption(data_len).await. - Python:
rpc.get_minimum_balance_for_rent_exemption(space).
When creating an account, fund it with at least this amount (plus transaction fees) so it is rent-exempt. Rent can be reclaimed in full when the account is closed.
Key points
- Pass the account data size (bytes) to get the rent-exempt minimum in lamports.
- Use when creating accounts (mints, token accounts, PDAs, etc.) so they stay rent-exempt.
<!-- Source: https://solana.com/developers/cookbook/accounts/calculate-rent, https://github.com/solana-foundation/solana-com -->
Solana Core — Transactions and Instructions
A transaction is a list of instructions executed in order. If any instruction fails, the whole transaction is rolled back (atomic).
Transaction layout
- Size limit: 1232 bytes (signatures + message).
- signatures: Array of 64-byte signatures; first signer pays the base fee.
- message: Header, account keys, recent blockhash, instructions.
Instruction
- program_id: Program to run.
- accounts: Account metas (pubkey, is_signer, is_writable).
- data: Opaque instruction bytes (program-specific).
Building and sending (TypeScript — Kit)
import {
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
signTransactionMessageWithSigners,
sendAndConfirmTransactionFactory
} from "@solana/kit";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const message = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(feePayer, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([instruction], tx)
);
const signed = await signTransactionMessageWithSigners(message);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, { commitment: "confirmed" });Building and sending (Legacy web3.js)
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
const tx = new Transaction().add(instruction);
const sig = await sendAndConfirmTransaction(connection, tx, [signer]);Key points
- Get a fresh recent blockhash for each transaction; it expires (~60–90 s).
- Fee payer must be the first signer and own a System Program account (can pay fees).
- Transaction signature = first signature; use it to look up the tx on-chain.
<!-- Source references:
- https://solana.com/docs/core/transactions
- https://solana.com/docs/core/instructions
- https://github.com/solana-foundation/solana-com
-->
Solana Core — Versioned Transactions
Versioned transactions enable extra runtime features (e.g. Address Lookup Tables). Onchain programs do not need changes; client code must set maxSupportedTransactionVersion on RPC calls that return transactions to avoid errors.
Transaction versions
- legacy — Older format; no lookup tables.
- 0 — Adds support for Address Lookup Tables.
maxSupportedTransactionVersion (required)
RPC methods that return transactions (e.g. getBlock, getTransaction) should include the highest version your app supports. If omitted, only legacy is allowed and the RPC will fail when a version 0 transaction is returned.
web3.js
const block = await connection.getBlock(slot, { maxSupportedTransactionVersion: 0 });
const tx = await connection.getTransaction(signature, { maxSupportedTransactionVersion: 0 });JSON-RPC
Include in the options object: "maxSupportedTransactionVersion": 0.
Creating a v0 transaction (web3.js)
Build a TransactionMessage with payerKey, recentBlockhash, and instructions, then compile to v0 and wrap in VersionedTransaction:
const messageV0 = new web3.TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();
const transaction = new web3.VersionedTransaction(messageV0);
transaction.sign([payer]);Sign before calling sendTransaction; sendTransaction for versioned transactions does not accept signers as a second argument.
Key points
- Use version
0when using Address Lookup Tables or when parsing blocks/transactions that may contain v0. - Always set
maxSupportedTransactionVersion: 0(or higher when new versions exist) on getBlock/getTransaction and similar RPC calls. - VersionedTransaction must be signed before sendTransaction.
<!-- Source: https://solana.com/developers/guides/advanced/versions, https://github.com/solana-foundation/solana-com -->
Solana Features — Actions and Blinks
Solana Actions are specification-compliant APIs that return transactions (or messages) for users to preview and sign. Blinks (blockchain links) turn an Action into a shareable URL that Action-aware clients (wallets, bots) can unfurl and execute.
When to use
- Expose a single action (e.g. "Donate", "Stake 1 SOL") or multiple choices (e.g. "Vote Yes" / "Vote No") behind a URL.
- Let users sign and submit transactions without leaving a chat, social feed, or widget.
- Register domain routes to Action APIs via
actions.jsonso clients can discover and call them.
Actions API
- GET to Action URL: returns metadata (title, icon, description, label, optional
links.actionsfor multiple buttons). Optionalparametersdescribe user inputs (amount, choice, etc.). - POST to Action URL: body
{ "account": "<base58-pubkey>" }. Response:{ "transaction": "<base64-serialized-tx>", "message?" }. Client replaces fee payer and recent blockhash if not partially signed, then prompts user to sign and submit. - OPTIONS: Must return CORS headers (
Access-Control-Allow-Origin: *, etc.) so browsers and wallets can call the API. Same foractions.json.
Blink URL
- Format:
https://example.com/?action=<url_encoded_action_url>. - Action URL scheme:
solana-action:<absolute_https_url>(e.g.solana-action:https://actions.example.com/donate). - Client decodes
action, introspects the Action API (GET then POST), and renders UI for the user to complete signing.
actions.json
- Served at domain root:
https://yoursite.com/actions.json. - Maps site paths to Action API paths. Example:
{
"rules": [
{ "pathPattern": "/donate", "apiPath": "/api/donate" },
{ "pathPattern": "/actions/*", "apiPath": "/api/actions/*" }
]
}pathPatternsupports exact path and wildcards (*single segment,**multiple). Query params are preserved.- Response must include CORS header
Access-Control-Allow-Origin: *.
SDK and tooling
@solana/actions— build Action endpoints (GET/POST) and conform to the spec.- Publish
actions.jsonat domain root and ensure GET/OPTIONS (and POST) return required CORS headers. - Test with Blinks Inspector. Some clients (e.g. social unfurl) may require allowlisting (e.g. Dialect Actions Registry).
Key points
- Actions = GET (metadata + optional params) + POST (account pubkey) → signable transaction.
- Blinks = URL containing
action=<solana-action:...>; clients introspect and run the Action lifecycle. actions.jsonmaps website URLs to Action API URLs; CORS and OPTIONS are required.
<!-- Source references:
- https://solana.com/developers/guides/advanced/actions
- https://github.com/solana-foundation/solana-com
-->
Solana Features — Transaction Confirmation & Expiration
Transactions expire when their recent blockhash is too old. Confirmation means observing the transaction in a block at a given commitment level. Agents must handle blockhash freshness and choose commitment appropriately.
Transaction and blockhash
- A transaction includes a recent blockhash used as a PoH timestamp.
- Validators keep a queue of recent blockhashes (e.g. ~300); a transaction is only processed if its blockhash is within the allowed window (e.g. ~151 most recent). Slots are ~400–600 ms, so a blockhash is typically valid for about 60–90 seconds.
- If a transaction is submitted after its blockhash has left the window, it will be rejected (expired). Fetching a new blockhash and rebuilding the transaction is required.
Confirmation flow
1. Build message and instructions. 2. Fetch a recent blockhash and attach it to the message. 3. Simulate (optional). 4. User/signer signs. 5. Send to RPC; leader includes it in a block. 6. Confirm by polling or subscription until the transaction appears at the desired commitment, or until expiry.
Commitment levels
- processed: Not finalized; can be rolled back.
- confirmed: Vote threshold; still can be rolled back in theory.
- finalized: Fully confirmed by supermajority; safe for critical decisions.
Use finalized when you must assume the transaction is permanent; use confirmed for faster UX when rollback risk is acceptable.
Best practices for agents
- Refresh blockhash shortly before signing/sending; avoid reusing old blockhashes.
- Set `maxSupportedTransactionVersion` (e.g.
0) on RPC calls that return transactions (getBlock,getTransaction) when using versioned transactions to avoid RPC errors. - Poll or subscribe (e.g.
getSignatureStatusesor subscription) with the desired commitment; stop when status is confirmed/finalized or when the blockhash would be expired (then treat as failed and retry with a new blockhash if appropriate). - Timeout: If confirmation does not occur within the blockhash validity window, consider the transaction expired and do not rely on it; retry with a new transaction and new blockhash if needed.
Key points
- Blockhash age determines transaction validity; ~60–90 seconds typical.
- Confirm using commitment (processed / confirmed / finalized); use finalized for irreversibility.
- Always request a fresh blockhash when building/sending and specify max supported transaction version for versioned tx RPC responses.
<!-- Source references:
- https://solana.com/developers/guides/advanced/confirmation
- https://github.com/solana-foundation/solana-com
-->
Solana Features — Fee Sponsorship
Set fee payer to sponsor pubkey; sponsor signs. Fee relayers (e.g. Kora) can add fee payer signature and send; users need not hold SOL.
Solana Features — Offline Signing
Create message, sign serialized message with Ed25519, recover transaction with signatures, sendRawTransaction. Use durable nonce for long delay. Partial sign with requireAllSignatures false for multi-signer.
Solana Features — Retrying Transactions
Use sendTransaction maxRetries for custom rebroadcast. Track lastValidBlockHeight; only re-sign after blockhash expired. Keep skipPreflight false.
Solana Features — Staking
Staking SOL delegates tokens to validators to earn rewards and secure the network. Stake accounts are distinct from system (wallet) accounts and have dedicated authorities and lifecycle.
Stake account vs system account
- System account: Send/receive SOL only; keypair controls the address.
- Stake account: Holds delegated stake; controlled by stake authority and withdraw authority, not necessarily by the address keypair. The address may have no keypair (e.g. when created via CLI with a one-off keypair only for uniqueness).
Authorities
- Stake authority signs: delegate, deactivate, split, merge, set new stake authority.
- Withdraw authority signs: withdraw to wallet, set new withdraw authority, set new stake authority.
Set at creation; can be changed later. Withdraw authority has more power (can liquidate and reset stake authority). Use the same or different addresses for each.
One validator per stake account
Each stake account delegates to a single validator. To delegate to multiple validators or split amounts, create multiple stake accounts (or split one into several).
Delegation lifecycle
- Warmup / cooldown: Delegation and deactivation take multiple epochs to fully activate or deactivate; a fraction changes at each epoch. Total stake changing per epoch is limited. Duration is network-dependent.
- Merge: Two stake accounts with the same authorities and lockup can be merged when both are deactivated, or when merging inactive into activating during its activation epoch; or when both activated/activating with matching voter and vote credits.
Lockup
Lockup can be set at creation; only withdraw and updating withdraw authority are blocked until date/epoch. Lockup can be modified by lockup authority or custodian. Delegation, deactivation, split, and changing stake authority still work during lockup.
Destroying a stake account
Withdraw all SOL and leave the account undelegated; the account is no longer tracked (balance 0). Re-create manually if the same address is needed again.
Usage (conceptual)
Agents that need to reason about staking should differentiate stake accounts (authorities, delegation state) from plain wallets; use RPC (e.g. getAccountInfo for stake program accounts) or CLI (solana stake-account, solana validators) to inspect state; create/delegate/withdraw/merge/split via Stake Program instructions, typically using official CLI or SDKs that wrap them.
Key points
- Stake and withdraw authorities define who can perform which operations; secure the withdraw authority.
- One delegation per stake account; use multiple accounts or split for multiple validators or amounts.
- Warmup/cooldown and merge rules are epoch-based; check current network docs for exact limits.
<!-- Source references:
- https://solana.com/docs/references/staking
- https://solana.com/docs/references/staking/stake-accounts
- https://github.com/solana-foundation/solana-com
-->
Solana — Frontend (React / Next.js)
@solana/react-hooks
- Built on @solana/client: React provider and hooks (same runtime and cache).
- Use for wallet state, RPC, and transaction sending from React components.
- Package:
@solana/react-hooks. Wrap the app with the provider; use hooks for accounts, balance, and sending txs.
@solana/client (headless)
- @solana/client: Single store for RPC, wallets, transactions, subscriptions. Use when you do not need React or want a shared store outside React.
Next.js
- Official docs: Next.js + Solana. Use App Router or Pages with the Solana provider and wallet adapters as needed.
- Wallet connection: Use wallet-adapter (e.g. @solana/wallet-adapter-react) with Phantom, Solflare, etc.; connect to the same RPC/context as @solana/client or @solana/react-hooks.
Web3 compatibility
- web3-compat layer exists for migrating from Ethereum-style APIs; prefer native Solana client/hooks for new code.
Key points
- Provider must wrap the tree that needs RPC/wallet; use hooks to read state and submit transactions.
- For production, configure RPC endpoint (and optional commitment) in the provider.
<!-- Source references:
- https://solana.com/docs/frontend/react-hooks
- https://solana.com/docs/frontend/nextjs-solana
- https://solana.com/docs/frontend/web3-compat
- https://github.com/solana-foundation/solana-com
-->
Solana — Payments and Solana Pay
Solana Pay
- Payment request URL:
solana:<recipient>?amount=<lamports>&reference=<base58>&label=.... Used in QR / deep links. - Transfer: Sender creates a transfer of SOL or SPL token to recipient; optional memo and reference (e.g. for idempotency).
- Verification: Merchant verifies on-chain that a transfer with the expected amount, recipient, and reference (and optional memo) was confirmed.
Send payments
- Basic: Build transfer instruction (SystemProgram.transfer or SPL transfer), send transaction; optionally include memo/reference in memo instruction or metadata.
- Batch: Multiple transfers in one or more transactions; respect size and compute limits.
- Fee abstraction: Use Compute Budget and/or fee payer services so user pays with token or third party pays fees.
Accept payments
- Indexing: Poll or subscribe to account/transaction history to detect incoming transfers matching recipient + reference.
- Verification tools: Compare expected amount, mint, reference, and confirmation status against on-chain data.
Key points
- Use reference (and memo if needed) to tie a payment to an order or session; verify on-chain before fulfilling.
- For production: use confirmed/finalized commitment and handle reorgs and duplicate references.
<!-- Source references:
- https://solana.com/docs/payments
- https://solana.com/docs/payments/send-payments
- https://solana.com/docs/payments/accept-payments
- https://github.com/solana-foundation/solana-com
-->
Solana — Clusters and Terminology
Clusters
- Mainnet-beta: Production; real SOL and tokens.
- Devnet: Development; airdrop SOL; similar to mainnet.
- Testnet: Staging/validation; may be deprecated or repurposed.
- Local: solana-test-validator; RPC http://localhost:8899, WS ws://localhost:8900.
Terminology
- Account: Key-value record; holds lamports, owner program, data, executable flag.
- Lamport: Smallest unit of SOL (10^9 lamports = 1 SOL).
- Program: Executable on-chain (smart contract).
- Instruction: Single operation for one program; transaction = list of instructions.
- PDA: Program-derived address; no private key; program can sign.
- Blockhash: Identifies a block; used in transaction message for expiry (~60–90 s).
- Commitment: processed / confirmed / finalized; how much confirmation the RPC reports.
- Slot: Leader schedule unit; one block per slot (typically).
- Signer: Account that must sign the transaction; first signer pays base fee.
- Compute unit (CU): Measure of execution; fees and limits expressed in CUs.
Staking
- Stake account: Holds delegated SOL; owner delegates to a validator.
- Stake program: Create stake account, delegate, withdraw, deactivate.
- Rewards: Validator rewards distributed to delegators; check stake program docs and RPC (getStakeActivation, etc.).
Key points
- Use devnet or local for testing; mainnet for production. Same RPC API; different endpoints and chain state.
- Account owner is the program that can modify the account; authority is an address with a permission (e.g. mint authority).
<!-- Source references:
- https://solana.com/docs/references/clusters
- https://solana.com/docs/references/terminology
- https://solana.com/docs/references/staking
- https://github.com/solana-foundation/solana-com
-->
Solana — RPC (HTTP and WebSocket)
HTTP (common methods)
- getLatestBlockhash: For transaction lifetime; use in message.
- getAccountInfo: Account data, lamports, owner, executable.
- getBalance: Lamport balance for address.
- getBlock, getBlockHeight, getBlocks, getBlockProduction: Block data.
- getTransaction, getSignatureStatuses: Transaction status and confirmation.
- sendTransaction: Submit serialized transaction.
- getMultipleAccountsInfo (or getMultipleAccounts): Batch account fetch.
- simulateTransaction: Simulate without sending; use for CU estimate and debugging.
WebSocket subscriptions
- accountSubscribe / accountUnsubscribe: Account data changes.
- programSubscribe / programUnsubscribe: Accounts owned by a program.
- signatureSubscribe / signatureUnsubscribe: Transaction confirmation.
- slotSubscribe, slotsUpdatesSubscribe, rootSubscribe, voteSubscribe: Slot and vote updates.
- logsSubscribe / logsUnsubscribe: Transaction logs (program logs).
Commitment
- processed, confirmed, finalized. Use
confirmedfor fast feedback;finalizedfor irreversible. - Pass commitment in RPC options and in sendAndConfirm/similar helpers.
Key points
- Prefer getMultipleAccounts for many accounts; use subscriptions for real-time account or signature updates.
- Deprecated methods (e.g. getConfirmedBlock) are superseded by non–“confirmed” names; use current API.
<!-- Source references:
- https://solana.com/docs/rpc
- https://solana.com/docs/rpc/http
- https://solana.com/docs/rpc/websocket
- https://github.com/solana-foundation/solana-com
-->
Solana — SPL Token Basics
- Token Program and Token-2022 (extensions) share the same base instructions. Examples apply to both unless noted.
- Mint: Issuer of tokens; holds mint authority and optional freeze authority.
- Token account: Holds a balance of one mint; owned by Token program; associated with owner (wallet/program).
Common instructions
- Create mint: InitializeMint (decimals, mint_authority, freeze_authority).
- Create token account: CreateAccount or CreateAssociatedTokenAccount (ATA).
- Mint tokens: MintTo (mint, destination token account, amount; requires mint_authority).
- Transfer: Transfer or TransferChecked (source, destination, amount; optional decimals for Checked).
- Approve / revoke delegate: Approve, Revoke (delegate can transfer up to amount).
- Burn: Burn, BurnChecked.
- Freeze / thaw: FreezeAccount, ThawAccount (requires freeze_authority).
Associated Token Account (ATA)
- One token account per (owner, mint) with deterministic address: PDA(owner, token_program_id, mint).
- Use getOrCreateAssociatedTokenAccount (or program equivalent) so the destination has a token account for the mint before transfer.
TypeScript (Kit)
- Use @solana-program/token or @solana-program/token-2022 for instruction builders (create mint, create ATA, mintTo, transfer, etc.).
- Legacy: @solana/spl-token (getAssociatedTokenAddressSync, createAssociatedTokenAccountInstruction, createTransferInstruction, etc.).
Key points
- Always ensure the recipient has an ATA for the mint when transferring; create it if missing.
- For Token-2022 use the Token-2022 program ID and extension-specific instructions (e.g. metadata, transfer fee) where needed.
<!-- Source references:
- https://solana.com/docs/tokens/basics
- https://github.com/solana-foundation/solana-com
-->
Solana — Token Extensions (Token-2022)
Token-2022 supports extensions that are enabled at mint creation. Same base instructions as Token program; extra data and instructions for each extension.
Common extensions
- Metadata: On-chain token name/symbol/URI (Token Metadata extension).
- Transfer fees: Fee on transfer (percentage or fixed); fee authority can withdraw.
- Confidential transfer: Encrypted balances; separate deposit/withdraw/transfer flow.
- Memo: Memo text stored with transfer (Memo program or extension).
- Interest-bearing: Mint has interest rate; balances accrue over time.
- Permanent delegate: Designated account can transfer any token account (e.g. compliance).
- Non-transferable: Tokens cannot be transferred (e.g. soulbound).
- Transfer hook: Program invoked on transfer (e.g. custom logic, fees).
- CPI guard: Restricts which programs can receive tokens via CPI.
Usage
- Create mint with desired extensions and extension data; token accounts for that mint get the same extension behavior.
- Use @solana-program/token-2022 (or legacy @solana/spl-token with Token-2022 program ID) and extension-specific APIs for metadata, transfer fee, confidential transfer, etc.
- Metaplex Token Metadata is a separate standard (off-chain or on-chain metadata); use Metaplex docs for NFTs and rich metadata.
Key points
- Extensions are configured at mint creation and cannot be added later to that mint.
- For transfer hooks / CPI guard, ensure the receiving program and CPI flow match the extension rules.
<!-- Source references:
- https://solana.com/docs/tokens/extensions
- https://github.com/solana-foundation/solana-com
-->