SOLSTACKAcademy

Advanced· Lesson 6 of 8· 4 min

Reading an Anchor program from the outside

Most Solana programs are written with Anchor, and Anchor leaves a trail: an IDL, predictable discriminators and typed constraints. With those you can read what a program does without its source.

Anchor is the framework behind most programs on the chain. It generates the parsing, the account checks and an interface definition — the IDL — from annotated Rust. Explorers use the IDL to turn raw instruction bytes into names and arguments, which is why a Jupiter swap shows as “Route V2” with typed fields rather than hex.

The program account

Solana Explorer's program account page for Jupiter: executable, executable data account, upgradeable, verified build status and upgrade authority
  1. Explorer labels well-known programs. The address below is the only identity.
  2. Executable: this account's data is code the runtime may run.
  3. The bytecode lives in a second account, the program-data account, owned by the upgradeable loader.
  4. Upgradeable: the code can be replaced. A program with the upgrade authority set to none is frozen.
  5. No verified build: nobody has proven the on-chain bytes match published source.
  6. Who can upgrade it: here a multisig, so several parties must agree to change the code.
Jupiter's program account, captured 19 Sep 2026 · explorer.solana.com

Three things on this page decide how much to trust a program: whether it is upgradeable and by whom, whether the build is verified against public source, and whether its upgrade authority is a single wallet, a multisig or nothing. “Immutable” is the strongest claim; “multisig with a public roster” is the common one for live protocols.

The IDL

The IDL is a JSON document listing every instruction with its arguments and the accounts it needs, every account type the program stores with its fields, and the program's error codes and events. Anchor can publish it on-chain in an account derived from the program id; explorers fetch it from there or from a registry. Jupiter's is on-chain, which is why the page below exists.

Solana Explorer's Program IDL tab for Jupiter: the instructions table with arguments and per-account flags for mutable, signer and PDA
  1. Instructions: what the program can be asked to do.
  2. PDAs: the seeds the program derives its own accounts from.
  3. An instruction's name. Hash it and you get the discriminator that selects it.
  4. Its arguments, typed. This is the Borsh layout that follows the discriminator.
  5. Account flags: mutable means the handler writes it; signer means it must have signed.
  6. PDA: the program derives and checks this address itself. You cannot substitute another.
Jupiter's IDL on Solana Explorer, captured 19 Sep 2026 · explorer.solana.com

Discriminators

Instruction data on an Anchor program starts with eight bytes that identify the handler: the first eight bytes of sha256("global:<instruction_name>"). Stored accounts start with eight bytes from sha256("account:<TypeName>"), which is how a program refuses an account of the wrong type even when the size matches. Given a program's IDL you can compute every discriminator and identify any instruction in raw data.

JavaScript
import { createHash } from "crypto";
const disc = (ns, name) => createHash("sha256").update(`${ns}:${name}`).digest().subarray(0, 8);

disc("global", "release")   // → the 8 bytes that start every `release` instruction
disc("account", "Lock")     // → the 8 bytes that start every Lock account

Constraints: what is checked before the handler runs

mutThe account will be written; it must be marked writable in the transaction.
signerThe account must have signed. This is how “only the owner can…” is enforced.
seeds = [...], bumpThe account must be the PDA of those seeds under this program. Prevents substitution.
has_one = ownerA field in this account must equal another account passed in. Ties a vault to its lock.
init, payer, spaceCreates the account in this instruction, funded by the payer, with a fixed size.
close = destinationDeletes the account afterwards and sends its rent to the destination.
token::mint, token::authorityThe account must be a token account for that mint, with that authority.
constraint = exprAn arbitrary check, such as a clock comparison. The error names it.

All of these run before the handler's own code. When a transaction fails with a constraint error, no logic ran at all; an account was not what the program required.

Reading stored state

Account types in the IDL are Borsh-serialised structs. With the IDL, an explorer's “Anchor Account” view or Account Inspector decodes them field by field. Solstack's Token Locker stores one Lock account per lock:

Code
Lock {
  creator, recipient, mint, vault,      // who, for whom, what, where it sits
  lock_id, name,
  total_amount, released_amount,        // progress
  cliff_ts, cliff_amount, end_ts, period, // the schedule
  created_ts, cancelable, transferable, bump
}

Errors

Anchor's own errors have fixed codes: constraint failures in the 2000s (2000 ConstraintMut, 2001 ConstraintHasOne, 2002 ConstraintSigner, 2006 ConstraintSeeds), account problems in the 3000s (3002 AccountDiscriminatorMismatch, 3007 AccountOwnedByWrongProgram, 3012 AccountNotInitialized). A program's own errors start at 6000 (0x1770) in IDL order — the locker's first is ZeroAmount, its sixth NothingToRelease. The log line custom program error: 0x1775 therefore means error 6005 of whichever program was running.

Programs without an IDL

Native programs (System, Token, Stake) are not Anchor; explorers hard-code their layouts. A third-party program with no IDL shows as “Unknown Instruction” and you are left with the accounts, the data bytes and the logs. That is a signal in itself: a serious program publishes its IDL and, ideally, a verified build.

What to remember

  • The program account tells you if code can change, by whom, and whether the build is verified. Read it before trusting anything else.
  • The IDL lists instructions, arguments, account flags, stored types and errors. Explorers decode with it.
  • Discriminators are the first 8 bytes of sha256 over global:name or account:Name.
  • Constraints run before any handler code. A constraint error means an account was not what was required.
  • Custom errors start at 6000 in IDL order; Anchor's own are in the 2000s and 3000s.

Try it