portal-query-solana-instructions
npx skills add https://github.com/subsquid-labs/agent-skills --skill portal-query-solana-instructions
Agent 安装分布
Skill 文档
When to Use This Skill
Use this skill when you need to:
- Track Solana program interactions (Jupiter swaps, Raydium pools, etc.)
- Monitor SPL token transfers
- Analyze wallet activity on Solana
- Filter by specific program functions (using discriminators)
- Track account interactions with programs
Solana instructions are the equivalent of EVM transactions/logs – they capture on-chain program calls.
Query Structure
Basic Solana instruction query structure:
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
"d8": ["0xe445a52e51cb9a1d"],
"a0": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
}
}
}
Field explanations:
type: "solana"– Required for Solana chainsfromBlock/toBlock– Block range (required, uses slot numbers)instructions– Array of instruction filter objectsprogramId– Program address (INDEXED – fast)d1/d2/d4/d8– Discriminators (INDEXED – function selectors)a0-a31– Account filters by position (INDEXED)mentionsAccount– Account appears anywhere (INDEXED)
Understanding Solana Instructions
Solana instruction structure:
Instruction:
ââ programId: "JUP6Lkb..." (Program being called)
ââ accounts: ["EPjFWdd5...", "So11111..."] (Account keys involved)
ââ data: "0xe445a52e..." (Instruction data)
Key concepts:
- programId – The program being called (like contract address in EVM)
- accounts – Array of account public keys involved in the instruction
- data – Instruction data (includes discriminator + parameters)
Understanding Discriminators
Discriminators are Solana’s function selectors (similar to EVM sighash).
For Anchor programs:
- First 8 bytes of data = discriminator
- Computed from function name
- Used to identify which function is being called
Discriminator types:
d1– First 1 byte of datad2– First 2 bytes of datad4– First 4 bytes of datad8– First 8 bytes of data (most common for Anchor)
Example (Jupiter swap):
Function: sharedAccountsRoute
Discriminator (d8): 0x5703feb8e7573909
â ï¸ Important: Discriminator values are computed from the actual program IDL and may differ between program versions. Always verify discriminator values against the specific program version you’re querying. The examples shown are for reference and may need to be updated.
Computing discriminator (Anchor):
import { sha256 } from '@noble/hashes/sha256';
function getDiscriminator(name: string): string {
const hash = sha256(Buffer.from(`global:${name}`));
return '0x' + Buffer.from(hash).slice(0, 8).toString('hex');
}
getDiscriminator('sharedAccountsRoute');
// Compute discriminator from your program's IDL
Examples
Example 1: Track Jupiter Swap Instructions
Use case: Monitor all Jupiter swap instructions on Solana.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
"d8": ["0x5703feb8e7573909"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true,
"transactionHash": true
},
"transaction": {
"feePayer": true,
"fee": true,
"err": true
}
}
}
Dataset: solana-mainnet
Program: Jupiter Aggregator V6
Function: sharedAccountsRoute (common swap function)
Notes:
d8discriminator identifies the specific function- Discriminator values must be verified against the program’s IDL
accountsarray shows all involved accounts (token accounts, pools, etc.)transactionHashlinks to transaction details
Example 2: Track SPL Token Transfers
Use case: Monitor SPL token transfer instructions.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
"d1": ["0x03"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
}
}
}
Dataset: solana-mainnet
Program: SPL Token Program
Instruction: Transfer (discriminator: 0x03)
Notes:
- SPL Token uses 1-byte discriminators
accounts[0]= source accountaccounts[1]= destination accountaccounts[2]= authority- Decode
datato get amount
Example 3: Track Wallet Activity (All Instructions)
Use case: Monitor all program interactions for a specific wallet.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"mentionsAccount": ["9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
},
"transaction": {
"feePayer": true,
"blockTime": true
}
}
}
Notes:
mentionsAccountmatches if the account appears ANYWHERE in accounts array- More expensive than
a0-a31(position-specific) filters - Use for comprehensive wallet tracking
Example 4: Filter by Specific Account Position
Use case: Track instructions where a specific token appears as first account.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
"a0": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
}
}
}
Notes:
a0= first account in accounts arraya1= second account,a2= third, etc. (up to a31)- More efficient than
mentionsAccount - Use when you know the account position
Example 5: Track Raydium Pool Interactions
Use case: Monitor Raydium AMM swap instructions.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"],
"d8": ["0xf8c69e91e17587c8"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true,
"transactionHash": true
}
}
}
Dataset: solana-mainnet
Program: Raydium AMM V4
Function: swap (example discriminator)
Notes:
- Raydium uses Anchor program (8-byte discriminators)
accountsarray includes pool accounts, token accounts, etc.
Example 6: Query Orca Whirlpool Instructions
Use case: Monitor all instructions from Orca Whirlpool program with block metadata.
{
"type": "solana",
"fromBlock": 280000000,
"toBlock": 280000003,
"instructions": [{
"programId": ["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"]
}],
"fields": {
"block": {
"number": true,
"timestamp": true
},
"instruction": {
"programId": true,
"accounts": true,
"data": true,
"transactionIndex": true
}
}
}
Dataset: solana-mainnet
Program: Orca Whirlpool
Notes:
- Retrieves all on-chain activity for the Whirlpool program
- Block metadata enables temporal analysis
transactionIndexhelps reconstruct execution order
Example 7: Filter Orca Swaps by Discriminator
Use case: Isolate specific swap instruction types using 8-byte discriminators.
{
"type": "solana",
"fromBlock": 280000000,
"toBlock": 280000003,
"instructions": [{
"programId": ["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"],
"d8": ["0xf8c69e91e17587c8"]
}],
"fields": {
"instruction": {
"programId": true,
"data": true,
"accounts": true
}
}
}
Dataset: solana-mainnet
Program: Orca Whirlpool
Function: Swap instruction
Notes:
- Instruction discriminators are typically first 8 bytes of data
- Enables granular filtering for specific operations
- Reduces result volume for focused analysis
Example 8: Filter Swaps by Specific Pool
Use case: Isolate transactions for a particular trading pair (e.g., USDC/SOL pool).
{
"type": "solana",
"fromBlock": 280000000,
"toBlock": 280000003,
"instructions": [{
"programId": ["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"],
"d8": ["0xf8c69e91e17587c8"],
"a0": ["7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm"]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
}
}
}
Dataset: solana-mainnet
Program: Orca Whirlpool
Pool: USDC/SOL pool address
Notes:
- Use position-based account filters (
a0–a31) to filter by account at specific positions - Pool addresses are stable identifiers for specific trading pairs
- Account filtering reduces data volume significantly
- More efficient than protocol-wide queries
Example 9: Track USDC Token Balance Changes
Use case: Monitor USDC token balance changes across blocks.
{
"type": "solana",
"fromBlock": 280000000,
"toBlock": 280000003,
"fields": {
"block": {
"number": true,
"timestamp": true
},
"tokenBalance": {
"account": true,
"preMint": true,
"postMint": true,
"preAmount": true,
"postAmount": true,
"preOwner": true,
"postOwner": true
}
},
"tokenBalances": [{
"preMint": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"]
}]
}
Dataset: solana-mainnet
Token: USDC (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)
Notes:
- Use
preMintfilter to target specific token types - Compare
preAmount/postAmountto identify transfers - Track ownership changes via
preOwner/postOwnerfields - Block metadata provides temporal context
Example 10: Monitor Transactions by Fee Payer
Use case: Track all transactions from a specific wallet address.
{
"type": "solana",
"fromBlock": 280000000,
"toBlock": 280000003,
"fields": {
"transaction": {
"signatures": true,
"feePayer": true,
"err": true
}
},
"transactions": [{
"feePayer": ["9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"]
}]
}
Dataset: solana-mainnet
Notes:
- Filter arrays support multiple addresses
err: nullindicates successful transaction- Requesting only necessary fields improves performance
- Useful for wallet activity tracking
Example 11: Track Program Deployments
Use case: Monitor new program deployments using BPF Upgradeable Loader.
{
"type": "solana",
"fromBlock": 270000044,
"toBlock": 270000047,
"instructions": [{
"programId": ["BPFLoaderUpgradeab1e11111111111111111111111"]
}],
"fields": {
"block": {
"number": true,
"timestamp": true
},
"transaction": {
"signatures": true,
"feePayer": true
},
"instruction": {
"programId": true,
"accounts": true,
"transactionIndex": true
}
}
}
Dataset: solana-mainnet
Program: BPF Upgradeable Loader
Notes:
- Captures deployment activities by filtering BPF Loader instructions
- First account typically represents the deployed program
- Block metadata enables temporal tracking
- Fee payer identifies deployer
Example 12: Track Failed Instructions
Use case: Find transactions with failed instructions.
{
"type": "solana",
"fromBlock": 250000000,
"toBlock": 250001000,
"instructions": [{
"programId": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
"isCommitted": [false]
}],
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
},
"transaction": {
"err": true,
"feePayer": true
}
}
}
Notes:
isCommitted: [false]filters for failed instructionsisCommitted: [true]filters for successful instructionstransaction.errcontains error details
Key Concepts
1. Solana Instruction Fields
Available instruction fields:
{
"programId": true, // Program being called
"accounts": true, // Array of account public keys
"data": true, // Instruction data (hex string)
"computeUnitsConsumed": true, // CU used by this instruction
"transactionHash": true, // Transaction signature
"transactionIndex": true, // Transaction position in block
"instructionAddress": true, // Instruction position in transaction
"isCommitted": true, // Success/failure status
"d1": true, // First 1 byte of data
"d2": true, // First 2 bytes of data
"d4": true, // First 4 bytes of data
"d8": true // First 8 bytes of data
}
2. INDEXED Fields for Filtering
Fast filterable fields:
programId– INDEXED (always filter by this first)d1, d2, d4, d8– INDEXED (discriminators)a0througha31– INDEXED (account positions)mentionsAccount– INDEXED (slower than a0-a31)isCommitted– INDEXED (success/failure)
Performance tips:
- Always filter by
programIdfirst (most selective) - Add discriminator filter (
d8for Anchor programs) - Use
a0-a31for position-specific account filters - Use
mentionsAccountonly when position unknown
3. Transaction Context
Include transaction fields for full context:
{
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true
},
"transaction": {
"feePayer": true, // Transaction signer
"fee": true, // Transaction fee (lamports)
"err": true, // Error object (null = success)
"blockTime": true, // Unix timestamp
"signatures": true, // Transaction signatures
"accountKeys": true // All account keys in transaction
}
}
}
Notes:
feePayer= transaction initiator (wallet)err: null= transaction succeedederr: {...}= transaction failed (contains error details)blockTime= Unix timestamp (seconds)
4. Account Position Filtering Strategy
Use a0-a31 when:
- You know the account position in the instruction
- You’re tracking a specific program’s function
- You need maximum performance
Use mentionsAccount when:
- Account position varies
- You want comprehensive wallet tracking
- Position is unknown or changes across functions
Example – SPL Token Transfer:
{
"instructions": [{
"programId": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
"a0": ["SourceTokenAccount..."] // Source account always at position 0
}]
}
5. Discriminator Formats
Different programs use different discriminator lengths:
Anchor programs (most common):
- Use 8-byte discriminators (
d8) - Example: Jupiter, Raydium, Mango
SPL Token Program:
- Use 1-byte discriminators (
d1) - Example: Transfer = 0x03, Approve = 0x04
Native programs:
- May use 4-byte discriminators (
d4) - Or no discriminator at all
Always check program documentation for correct discriminator format.
Common Mistakes
â Mistake 1: Using Wrong Discriminator Length
{
"instructions": [{
"programId": ["JUP6..."],
"d1": ["0xe4"] // â Jupiter uses d8, not d1
}]
}
Fix: Use correct discriminator length:
{
"instructions": [{
"programId": ["JUP6..."],
"d8": ["0xe445a52e51cb9a1d"] // â
8-byte discriminator
}]
}
â Mistake 2: Filtering Without programId
{
"instructions": [{
"d8": ["0xe445a52e51cb9a1d"] // â No programId filter
}]
}
Fix: Always filter by programId first:
{
"instructions": [{
"programId": ["JUP6..."], // â
Filter by program
"d8": ["0xe445a52e51cb9a1d"]
}]
}
â Mistake 3: Using EVM-Style Block Numbers
{
"type": "solana",
"fromBlock": 19500000, // â This is an EVM block number
"toBlock": 19500100
}
Fix: Use Solana slot numbers:
{
"type": "solana",
"fromBlock": 250000000, // â
Solana slot number
"toBlock": 250001000
}
Note: Solana uses “slots” not “blocks” – current slot is ~250M+ (as of 2024).
â Mistake 4: Forgetting to Include Transaction Fields
{
"fields": {
"instruction": {
"programId": true,
"accounts": true
}
// â Missing transaction fields
}
}
Fix: Include transaction context:
{
"fields": {
"instruction": {
"programId": true,
"accounts": true
},
"transaction": { // â
Add transaction fields
"feePayer": true,
"err": true
}
}
}
â Mistake 5: Confusing Account Position
{
"instructions": [{
"a1": ["TokenAccount..."] // â Wrong position
}]
}
Fix: Check program documentation for correct account ordering:
{
"instructions": [{
"a0": ["TokenAccount..."] // â
Correct position (first account)
}]
}
Response Format
Portal returns JSON Lines (one JSON object per line):
{"header":{"slot":250000000,"hash":"...","parentHash":"...","timestamp":1234567890}}
{"instructions":[{"programId":"JUP6...","accounts":["EPjF...","So11..."],"data":"0xe445a52e...","transactionHash":"5J7X..."}],"transactions":[{"feePayer":"9WzD...","fee":5000,"err":null}]}
{"instructions":[{"programId":"JUP6...","accounts":["USDC...","SOL..."],"data":"0xe445a52e...","transactionHash":"3K9Y..."}],"transactions":[{"feePayer":"8QwE...","fee":5000,"err":null}]}
Parsing:
- Split response by newlines
- Parse each line as JSON
- First line is block header
- Subsequent lines contain instructions and related transaction data
Performance Tips
1. Filter Selectivity Order
Most selective to least selective:
programId+d8+a0(best)programId+d8programId+mentionsAccountprogramIdonly (broad)- No filters (avoid)
Example progression:
// Best performance
{
"instructions": [{
"programId": ["JUP6..."],
"d8": ["0xe445a52e51cb9a1d"],
"a0": ["USDC..."]
}]
}
2. Block Range Strategy
Optimal slot ranges:
- Real-time monitoring: 1,000-10,000 slots (~8-80 minutes)
- Historical analysis: 100,000-1,000,000 slots
- Full history: Use multiple queries with pagination
Note: Solana processes ~2 slots/second, so ranges translate differently than EVM.
3. Field Selection
Request only needed fields:
// Minimal (fastest transfer)
{
"fields": {
"instruction": {
"programId": true,
"data": true
}
}
}
// Full analysis (larger payload)
{
"fields": {
"instruction": {
"programId": true,
"accounts": true,
"data": true,
"computeUnitsConsumed": true,
"transactionHash": true
},
"transaction": {
"feePayer": true,
"fee": true,
"err": true,
"blockTime": true
}
}
}
Related Skills
- portal-dataset-discovery – Find correct Solana dataset name
- portal-query-evm-logs – EVM equivalent (for comparison)
- portal-query-evm-transactions – EVM equivalent (for comparison)
Additional Resources
- API Documentation: https://beta.docs.sqd.dev/api/catalog/solana/stream
- Schema Reference: https://github.com/subsquid/sqd-portal/blob/master/resources/schemas.json
- Solana Program Library: https://spl.solana.com/
- Anchor Framework: https://www.anchor-lang.com/
- Current Slot Number: https://explorer.solana.com/
Official Subsquid Documentation
Core Documentation
- llms.txt – Quick reference for Portal API Solana querying
- llms-full.txt – Complete Portal documentation
- skill.md – Comprehensive Solana instructions query guide
API Resources
- Solana OpenAPI Schema – Complete Solana query specification
- Available Datasets – All supported Solana networks
- Solana Stream API – Instructions query documentation