A developer deploys a token swap contract on Solana that interacts with a liquidity pool, which itself calls a fee distribution program, which in turn invokes a staking contract. When a transaction fails silently or executes with unexpected side effects, understanding the complete call chain becomes critical. The transaction hash alone does not reveal which program failed, what parameters were passed at each stage, or whether fees were deducted correctly. Debugging this kind of nested execution requires access to the full instruction tree, program invocation sequences, and the exact state changes at each step.
Cross-Program Invocation, or CPI, is how Solana smart contracts call other programs and coordinate complex operations across the network. Unlike single-program transactions, CPIs create a call stack where each program can invoke others, passing data and signing authority along the way. The challenge for developers is that while Solana’s on-chain execution is deterministic and fast, the CPI structure can be opaque when viewed through standard transaction data. Solscan provides the developer tools and real-time transaction tracking necessary to visualize these interactions, trace execution flows, and identify where a complex contract interaction breaks down.
Why CPI visibility matters for contract debugging
In traditional blockchain explorers, a transaction might appear as a single atomic unit. You see the wallet address that initiated it, the program it called, and perhaps a status code indicating success or failure. For Solana development, this abstraction conceals the real execution model. When a program invokes another program via CPI, the invoked program runs with specific permissions and data, potentially triggering further invocations. If any step fails, the entire transaction can be reverted, but the error messages may not clearly indicate where the reversion originated.
The consequence is that developers often waste hours submitting transactions, reviewing log output locally, and redeploying code before identifying a CPI bug. A CPI might fail because the invoked program did not have the required account, the signer authority was not properly delegated, the passed data was malformed, or the invoked program itself triggered a downstream CPI that failed. Without proper visualization, the call stack becomes a guessing game.
Solscan addresses this by exposing the instruction-level detail that Solana validators already execute. Each transaction is broken down into its constituent instructions and CPIs, showing which program was invoked, which accounts were accessed, which were writable, and which signed the transaction. For a developer building on Solana, this level of transparency is not a luxury feature. It is the baseline for understanding what actually happened when a transaction hit the chain.
The practical result is faster debugging cycles. Instead of relying on local testing or mainnet-beta iteration, a developer can submit a problematic transaction to devnet or testnet, pull up solscan, and immediately see the instruction tree. Account access patterns, data sizes, and program invocation order become visible. This is particularly valuable when contracts are composable: a program that works correctly in isolation may fail when called as a CPI from another contract because of unexpected account requirements or permission assumptions.
Reading the CPI call tree in Solscan’s transaction details
When you navigate to a transaction hash in Solscan and scroll to the instruction section, you will see an indented list showing the hierarchy of program invocations. The root instruction is at the leftmost level, and each nested CPI is indented further. This tree structure directly maps to the call stack during execution. A token swap program might invoke a liquidity pool program, which invokes a fee vault program, which invokes a token program. Each level can be expanded to show the accounts involved and the encoded instruction data.
The account list for each instruction is crucial. Programs access accounts to read state, write state, or verify signatures. Solscan displays each account with a label indicating its role: signer, writable, read-only, or program account. If a CPI fails because a required account is missing, the account list will show which accounts were actually passed. Comparing the actual list to the contract documentation or source code often reveals the mistake immediately.
The instruction data itself is shown in encoded form, usually as base64 or hex. For standard programs like the token program or system program, Solscan can decode the instruction and display human-readable fields such as the mint address, token amount, or recipient. For custom programs, the data remains encoded unless Solscan has IDL (Interface Definition Language) data for that program. Developers should ensure their program IDLs are uploaded to the Anchor registry or accessible through other means so that Solscan can parse and display the instructions cleanly.
The real power emerges when tracing an error. If a CPI fails, Solscan will show which instruction in the tree caused the failure and often display the error message or return code. Some errors are descriptive: “Custom program error: 0x1f” maps to a specific error variant in the contract code. Others are more opaque: a program exception or an account not found. By examining the failed instruction and the accounts it accessed, the developer can immediately hypothesize what went wrong and test a fix.
Identifying account permission issues and signer delegation
One of the most common CPI failures stems from incorrect account permissions. A program may attempt to invoke another program to write to an account that is not marked as writable in the CPI instruction. Or it may attempt to sign as an account that is not a signer. In Solana’s execution model, these are strict requirements, and violations cause the transaction to fail.
Solscan makes these violations visible by showing the writable and signer flags for each account in each instruction. If a contract intends to debit a token account, that account must be marked writable. If a contract is supposed to sign on behalf of a program-derived address (PDA), the PDA must have been created with the program as its authority, and the program must invoke the CPI with the PDA included and the program’s signer flag set.
Signer delegation is a particularly subtle aspect of CPI. A program does not have a private key and cannot sign transactions in the traditional sense. Instead, if the program derives an address using a fixed seed and the program ID (a PDA), it can include itself as a signer in a CPI by using invoke_signed. Solscan will show this in the instruction data and account list. The signer flag on the program account indicates that the program is asserting its authority for that CPI.
When a CPI involving signer delegation fails, the most common cause is that the seed or program ID used to derive the PDA does not match what was used to create the original account. Solscan cannot automatically detect this mismatch, but it can show the program that is asserting the signer flag, allowing the developer to verify the derivation logic locally. This is why understanding both the on-chain data visible in solscan and the source code is essential for CPI debugging.
Tracking state changes and account balance updates
A transaction may complete successfully but leave accounts in an unexpected state. An escrow program might receive tokens but not release them to the intended recipient. A staking contract might claim rewards but not update the reward counter. These are logical bugs, not execution failures, and they require a different debugging approach than CPI call tracing.
Solscan’s transaction details page shows the accounts affected by a transaction and, in many cases, the state changes that occurred. For token programs, this includes balance changes. For custom programs, the state changes depend on what the program writes and whether Solscan has the IDL to decode it. The account data section will display the before and after states if available, allowing the developer to confirm whether the contract logic worked as intended.
When debugging state changes, correlate the account modifications with the CPI sequence. If a token balance decreased but was not debited from the expected account, trace which account was actually written to in the CPI. If a program state account was updated but the update was incomplete, check whether a downstream CPI failed or whether the program returned early. Solscan provides the data; mapping it to intent requires knowledge of the contract’s business logic.
For complex transactions involving many accounts, Solscan’s account access patterns become a useful summary. The platform shows which accounts were accessed and in what order, condensing a large number of accounts into a readable flow. This helps identify whether all necessary accounts were included, whether any account was accessed multiple times unexpectedly, or whether an account was passed but never actually used.
Using developer tools for API-driven CPI analysis
For developers building monitoring systems, automated testing frameworks, or complex analysis pipelines, solscan offers API access to transaction and blockchain data. The API returns structured data about transactions, including the full instruction tree, account information, and state changes. This allows programmatic analysis of CPIs without manual inspection of the web interface.
An automated test harness can submit a transaction to devnet, poll the solscan API for the transaction result, and validate that the CPI call tree matches expectations. If a new program version introduces an unexpected CPI or changes the account order, the test can detect it before the code reaches production. Similarly, a monitoring system can subscribe to transaction streams, filter for transactions involving a specific program, and alert if a CPI fails or if an account balance deviates from expected values.
The API documentation on solscan provides endpoints for querying transactions, blocks, accounts, tokens, and more. The response format is JSON, making it straightforward to integrate into standard development tools. Rate limits and authentication are documented clearly, and the free tier provides sufficient quota for development and small-scale monitoring.
For developers using frameworks like Anchor, which abstract away much of the CPI complexity, the API allows validation that the abstraction is doing what you expect. You can verify that the PDAs derived in Rust match the actual addresses accessed on-chain, that the account order is correct, and that the instruction data encodes the intended parameters. This bridges the gap between local testing and on-chain reality.
Common CPI pitfalls and how Solscan reveals them
Missing accounts in a CPI is one of the most common mistakes. A developer copies instruction arguments from documentation but forgets to include a required account. When the CPI executes, the program returns an error because it expected an account at a specific index that was not provided. In Solscan, the account list will be shorter than expected, making this obvious.
Incorrect account order is another subtle bug. Some programs expect accounts in a specific sequence. If the order is wrong, the program interprets the account references incorrectly, potentially treating a signer as read-only or writing to a read-only account. Solscan shows the exact order and flags, so comparing to the program documentation or IDL will quickly identify the issue.
Signature verification failures occur when a CPI attempts to verify a signature but the signer is not included in the accounts list or is not marked as a signer. The program will return a signature verification error. Solscan will show that the required account is present and marked as a signer or not, immediately suggesting the problem.
Insufficient lamports for rent or fees will cause a transaction to fail when a program attempts to create an account or debit a wallet. Solscan shows the final state of all accounts involved, so you can verify that accounts have positive balances or that rent-exempt amounts were maintained. If a CPI fails and an account ends up with zero balance, you have found the root cause.
Best practices for CPI development and debugging
Test CPIs locally first using a Solana test validator or devnet, and deploy to a public network only after validation. Use Solscan during development to inspect transactions and verify that the CPI tree matches your expectations. When a transaction fails, immediately pull up solscan and examine the full instruction sequence, account list, and any error messages. Do not attempt to debug based on local logs alone, because the on-chain behavior may differ.
Document your contract’s account requirements and CPI signatures clearly. Create IDL files for custom programs and ensure they are accessible to Solscan so that instruction data is human-readable. This benefits both your own debugging and anyone else who needs to understand your contract’s interactions.
Use program-derived addresses (PDAs) consistently. If your program controls an account, use a PDA derived from a fixed seed and the program ID. Include the PDA as a signer in any CPIs that need to act on behalf of the program. Solscan will show the signer flag, allowing you to confirm that you are using invoke_signed correctly.
Test error cases explicitly. CPIs can fail for reasons beyond your control: liquidity may be exhausted, a called program may be paused, or the network may be congested. Design your contracts to handle these gracefully, and test them by simulating failures locally. When errors occur on-chain, Solscan will show the exact point of failure, but your contract should never rely on Solscan to discover bugs that testing should have caught.
For complex transactions involving many programs, consider breaking them into smaller, testable units. A transaction with five nested CPIs is harder to debug than five transactions with one or two CPIs each. While gas costs and confirmation time may favor larger batches, the development and maintenance cost of complex CPIs is real. Make that trade-off consciously.
Real-time transaction tracking for production monitoring
Once a contract is live, Solscan becomes a production monitoring tool. You can set up alerts for transactions involving your program, monitor for unexpected errors, and verify that CPIs are executing as intended. The real-time transaction tracking feature allows you to search for recent transactions affecting your program or a specific account, providing immediate visibility into on-chain activity.
If a user reports that a transaction failed or that their balance is incorrect, a developer can search solscan for the transaction hash and immediately see the full execution trace. This is much faster than asking the user to provide logs or trying to reconstruct the failure from memory. The transparency provided by solscan reduces mean-time-to-resolution for production issues.
For token contracts, NFT programs, or any contract that manages user assets, regular audits of on-chain state using solscan help identify discrepancies. Compare the balances shown in solscan with your off-chain database. If they diverge, the on-chain data is authoritative, and you need to investigate the CPI sequence that caused the divergence.
As your Solana application scales, the ability to quickly inspect transactions becomes even more critical. Solscan’s fast, reliable interface ensures that you can debug issues without delays. For developers serious about production stability, making solscan part of your standard incident response workflow is essential.
Frequently asked questions
How does Solscan display nested CPIs in the transaction tree?
Solscan shows CPIs as an indented instruction list, with parent instructions above and child CPIs indented beneath them. Each instruction displays the program invoked, the accounts accessed, their flags (writable, signer, program), and the encoded instruction data. If an IDL is available, the data is decoded into human-readable fields. Expanding each instruction reveals the full details needed to trace execution and identify failures.
Why would a CPI fail even though it works in my local tests?
Local tests may not exactly replicate on-chain state, account balances, or program versions. A CPI might fail on-chain due to insufficient lamports, a missing account not required in your test environment, incorrect signer delegation, or a different version of a called program. Use solscan to inspect the actual on-chain transaction, check the account list and state, and compare to your local assumptions. This usually reveals the discrepancy immediately.
Can I use Solscan’s API to automatically verify my CPI interactions?
Yes. The Solscan API provides structured access to transaction data, including the full CPI call tree, account information, and state changes. You can build test harnesses that submit transactions, query the API for the result, and validate that the instruction sequence, account order, and final state match expectations. This is particularly useful for regression testing after contract updates.
What does it mean when Solscan shows a signer flag on a program account in a CPI?
A signer flag on a program account indicates that the program is using invoke_signed to assert its authority for that CPI. This means the program is signing on behalf of a program-derived address (PDA). The developer should verify that the PDA was derived using the correct seed and program ID, and that the program has the authority to sign for that account.