- Program Derived Addresses (PDAs) are the foundation of every serious Anchor program — canonical bump handling and seed design must be correct before anything else.
- Cross-Program Invocations (CPIs) allow your program to sign for its own PDAs and call SPL Token, Metaplex, or any other on-chain program.
- Zero-copy accounts let you manage large on-chain state (up to Solana’s 10 MB limit) without loading entire accounts into memory during instruction execution.
- Security vulnerabilities in Anchor programs almost always trace back to four root causes: missing signer checks, unchecked owners, bump misuse, and arbitrary CPI exposure.
- Chialisp developers will recognize parallel patterns throughout — PDAs map closely to puzzle hashes, and CPIs echo delegated spending conditions. Both systems enforce security at the execution layer.
This advanced Solana Anchor tutorial targets developers who already know the basics — you have written a counter or a voting program, you understand the account model, and you have run anchor test before. What this guide covers is the step up: the patterns that ship in every real DeFi protocol, NFT marketplace, and staking vault on Solana today. Master PDAs as authorities, CPIs with signer seeds, zero-copy for large state, and a security checklist grounded in what auditors actually flag.
Why Advanced Anchor Patterns Are Different
Most Anchor tutorials stop at #[derive(Accounts)], a single state account, and a basic instruction. That is enough to understand the framework. It is not enough to ship a program you would trust with real funds or real users. Advanced Anchor development is about composability and safety — your program does not live in isolation. It calls other programs, owns accounts on behalf of users, and holds authority over token vaults. The moment your program holds any authority, the security requirements go up sharply.
Chialisp developers reading this will notice something: Chia’s smart coin model already bakes many of these safety assumptions into its design. A Chialisp puzzle cannot spend a coin unless it produces the correct conditions at the consensus level — there is no equivalent of a missing signer check. Solana’s account model is more flexible, which is exactly what makes disciplined Anchor patterns non-optional. Flexibility without discipline is where vulnerabilities live. Understanding both models side by side gives you a sharper lens on why each constraint exists.
Program Derived Addresses: Advanced PDA Patterns in Anchor
You already know that a PDA is an address that no private key controls — it lives off the elliptic curve and can only be “signed for” by the program that owns it. Advanced Anchor work is about what you do with that property. PDAs stop being data buckets and start being authorities — over token accounts, over metadata, over upgrade authorities, over cross-program escrows.
Canonical Bump Handling
Every PDA derivation involves a bump seed — a single byte that Solana adjusts until it finds an address that falls off the ed25519 curve. The canonical bump is the highest valid bump for a given set of seeds, and it is the one find_program_address returns. The critical discipline: store the canonical bump in your account the moment you initialize it, and always use that stored bump when you reconstruct the PDA in subsequent instructions. Never call find_program_address again during execution — it is expensive, and worse, it opens you to bump canonicalization attacks where a caller provides a non-canonical bump that still derives the same address but bypasses constraints you intended.
In Anchor, the seeds and bump constraints on #[account] handle this automatically when you use bump = your_account.bump. That one line enforces that the account’s stored bump matches the PDA derivation. It is a small constraint that rules out an entire class of bugs.
PDAs as Vault Authorities
The most powerful use of a PDA is as a vault authority — a program-controlled signer that holds tokens or SOL on behalf of the protocol. The pattern: create a PDA with seeds tied to the vault’s purpose (e.g., [b"vault", user.key().as_ref()]), make that PDA the token account’s authority, and whenever the protocol needs to move tokens, reconstruct the PDA in a CPI context and sign with the seeds. No private key is involved. The program is the authority, and only the program’s own logic can trigger a transfer.
This is the pattern behind every Solana escrow, every staking vault, and every AMM pool. In Chialisp terms, this maps to a puzzle that can only be spent when specific solution conditions are met — the “authority” is the puzzle logic itself, not an external key. Chialisp’s approach to smart contract authority and Anchor’s PDA vault pattern solve the same trust problem from opposite architectural directions: one locks spending conditions at the coin level, the other locks signing authority at the program level.
Which Advanced Anchor Pattern Do You Need?
| What You Are Building | Primary Pattern | Key Anchor Constraint | Chialisp Parallel |
|---|---|---|---|
| Token escrow / staking vault | PDA as vault authority | seeds, bump, token::authority = vault_pda | Puzzle-gated coin spending |
| User-isolated state (profile, position) | PDA per user | seeds = [b"user", user.key()] | Coin with user-specific puzzle hash |
| Calling SPL Token or Metaplex | CPI with PDA signer | CpiContext::new_with_signer | Delegated inner puzzle / spend condition |
| Protocol-to-protocol composability | Anchor interfaces / raw CPI | Program<'info, TargetProgram> | Cross-puzzle spend delegation |
| Large on-chain state (>10 KB) | Zero-copy accounts | #[account(zero_copy)] | Extended solution data in CLVM |
| Security audit prep | Full constraints review | Signer, owner, bump, has_one checks | Condition assertion in CLVM output |
Cross-Program Invocations: Making Anchor Programs Talk to Each Other
Cross-Program Invocations are how Solana achieves composability. Your program calls another program’s instruction mid-execution, passing accounts and data as if the user had called that program directly. CPIs are what let a DEX call SPL Token to transfer funds, what lets a staking program call a rewards program to mint tokens, and what lets any program act as an authority over a PDA it controls.
CpiContext::new_with_signer and PDA Signing
The key distinction in advanced CPI work is between a regular CPI and one where your PDA needs to sign. When your program’s PDA is an authority — say, the authority over a token vault — and you want to trigger a token transfer, you cannot provide a private key. Instead, you reconstruct the PDA seeds at runtime and pass them to CpiContext::new_with_signer. Solana’s runtime verifies that those seeds, combined with your program ID, derive the PDA that is listed as a signer in the instruction. If the derivation matches, the CPI is authorized.
The code structure looks like this in practice: you collect your signer seeds as a slice of byte slices, wrap them in an outer slice, and pass that to new_with_signer alongside the accounts context and the target program. The runtime does the rest. This is the pattern that makes protocol-controlled liquidity, protocol-controlled minting, and program-owned escrows possible on Solana.
Calling Non-Anchor Programs: Raw CPIs and Instruction Discriminators
Not every program you need to call uses Anchor. The Metaplex Token Metadata program, older SPL programs, and many third-party programs expose raw Solana instructions with manually packed data. Calling these requires you to construct the instruction data yourself — often starting with an 8-byte discriminator derived from the instruction name’s SHA256 hash, followed by Borsh-serialized arguments.
Anchor provides helper types like Program<'info, T> and traits like Id to validate a target program’s address before you invoke it. For programs that implement the Anchor Interface type, you get type-safe CPI calls. For everything else, you build the AccountMeta vector manually and use invoke or invoke_signed from the solana_program crate directly. The key discipline: always validate the target program’s address against a known, hardcoded pubkey before invoking. An arbitrary CPI — where a caller can pass any program and trigger your signer — is one of the most exploited vulnerability classes in Solana’s history.
Chialisp’s delegated puzzle mechanism handles composability differently: an inner puzzle can be passed at spend time as part of the solution, and the outer puzzle wraps it with its own conditions. The composability is baked into the coin structure rather than called at runtime. Both approaches are powerful; both require careful constraint validation at the boundary where external input is accepted.
The Anchor documentation makes clear that while CPIs enable composability between programs, the runtime places the responsibility for validating every signer and every account owner squarely on the developer — it will not perform these checks automatically. — Anchor Docs, Cross Program Invocation, anchor-lang.com/docs/basics/cpi
Zero-Copy Accounts: Advanced State Management in Anchor
Solana’s standard account deserialization reads the full account data into memory and runs Borsh deserialization before your instruction executes. For accounts with large structs — think an orderbook with 1,000 price levels, a vesting schedule with hundreds of entries, or a complex AMM state account — that deserialization overhead adds up fast and can push you into compute unit limits.
The zero_copy Attribute and the zero vs init Pattern
Zero-copy accounts bypass Borsh deserialization entirely. With #[account(zero_copy)], Anchor gives your instruction a raw pointer into the account’s data buffer, and you interpret the bytes in place using Rust’s repr(C) layout. No heap allocation, no deserialization pass — your instruction works directly with memory-mapped account data.
The initialization pattern for zero-copy is a two-step process. First, allocate the account using the System Program — this is a regular account creation with the correct data size, but you use the zero constraint in Anchor rather than init. The zero constraint tells Anchor the account is already allocated and zeroed but not yet assigned the Anchor discriminator. Second, in your initialization instruction, set the discriminator and populate the initial state fields. After that, all subsequent instructions use the zero-copy account directly via a raw byte reference.
Account sizing matters here more than anywhere else. Zero-copy accounts are typically large — you are using zero-copy precisely because the account is too big for standard deserialization. Solana’s maximum account size is 10 MB, and accounts cannot be resized after creation without using the realloc constraint (available in recent Anchor versions). Plan your account layout carefully at design time: pad structs for future fields, use fixed-size arrays instead of vectors, and document the byte layout in your code for auditors.
Security Hardening Your Anchor Program: The Patterns Auditors Check First
Every Anchor security audit runs through a standard checklist of vulnerability patterns. These are not theoretical — they have caused real losses on Solana mainnet. The good news: Anchor’s constraint system catches most of them at compile time if you use the constraints correctly. The risk comes when developers skip constraints because “it feels safe” or because they are optimizing for compute units.
Every shortcut in account validation is a potential attack surface. One missing signer check can drain a vault.
Signer Checks, Owner Validation, and has_one
The three most common first-pass vulnerabilities are: missing #[account(signer)] on accounts that should authorize instructions, missing owner checks on accounts that could be substituted with attacker-controlled accounts of the same shape, and missing has_one or constraint checks that verify relationships between accounts. Anchor’s has_one macro is specifically designed for the last case — it verifies that a field in one account matches the pubkey of another account passed to the instruction. A missing has_one = authority on a vault account, for example, means any caller can pass their own authority pubkey and drain the vault if the signer check is the only guard.
Bump Canonicalization, PDA Sharing, and Type Cosplay
Bump canonicalization attacks exploit programs that accept a caller-provided bump instead of using the stored canonical bump. Always store the bump at initialization and reference it with bump = account.bump in subsequent instructions. PDA sharing is a related issue: if two different instructions derive a PDA using the same seeds but different intents, an attacker can pass one instruction’s PDA to the other. The fix is to include a discriminator string in your seeds — [b"vault", user.key().as_ref()] rather than just [user.key().as_ref()] — so PDAs for different purposes cannot collide.
Type cosplay is an account substitution attack: an attacker crafts an account with the same data layout as your expected account type but a different Anchor discriminator. Anchor’s automatic discriminator check (the 8-byte SHA256 hash prepended to every account) blocks this when you use typed accounts like Account<'info, MyState>. The vulnerability appears when developers use AccountInfo directly and skip the discriminator check for performance reasons. Unless you have a specific reason to use raw AccountInfo, always use Anchor’s typed account wrappers.
In Chialisp, type cosplay is structurally impossible — a coin’s identity is its puzzle hash, and the puzzle hash is committed at creation. You cannot substitute a different puzzle for the same coin. This is one area where Chia’s architecture provides a built-in guarantee that Anchor developers must enforce manually through discriminator checks.
Solana Anchor vs Chialisp: Advanced Smart Contract Patterns Compared
| Concept | Solana / Anchor | Chialisp / Chia Network |
|---|---|---|
| Account / state model | Mutable accounts owned by programs; account model | Immutable coins with puzzle hashes; UTXO-based |
| Program authority | PDA signs for its own accounts via seed reconstruction | Inner puzzle controls spending conditions at consensus |
| Cross-program calls | CPI: invoke another program mid-instruction | Delegated inner puzzles; condition assertions across coins |
| Account type safety | Anchor discriminator (8-byte SHA256); enforced by typed accounts | Puzzle hash is the identity; structurally enforced |
| Signer authorization | Must be explicitly checked via Anchor constraints | Condition assertions enforced by CLVM at consensus |
| Large state | Zero-copy accounts, up to 10 MB | Solution data in CLVM; large puzzles via puzzle reveals |
| Security baseline | Developer-enforced via Anchor constraint system | Consensus-enforced; fewer manual checks required |
| Composability model | Runtime CPI; any program can call any program | Compile-time puzzle composition; delegated inner puzzles |
An Advanced Anchor Practice Path That Actually Works
The best way to internalize these patterns is to build through them in sequence rather than jumping between topics. Start with a program that manages user state through per-user PDAs — one PDA per user, initialized with canonical bump storage, and a simple update instruction that validates the PDA derivation on every call. This one program alone will surface most of the constraint patterns you need.
Next, extend that program to hold a token vault. Make a second PDA the authority over a token account, and implement a withdraw instruction that uses CpiContext::new_with_signer to transfer tokens. This forces you to wire up the signer seeds correctly and validates that your PDA authority pattern is sound. If you can drain and refill that vault through your program — and only through your program — the core CPI pattern is working.
The third step is a second program: a simple “puppet” program that exposes a single instruction, and a “puppet master” program that calls it via CPI. Build both from scratch without copying tutorial code. The act of constructing the AccountMeta vector manually and building the CPI context makes the runtime’s account passing model concrete in a way that reading about it cannot.
Finally, take the largest account in your program and refactor it to zero-copy. Measure the compute unit cost before and after. The difference for accounts above 1 KB is usually significant enough to justify the added complexity in any production program.
Conclusion
Advanced Solana Anchor development comes down to one discipline: every account that enters your instruction must be fully validated before you act on it. PDAs give you program-controlled authority — use canonical bumps and store them at initialization. CPIs give you composability — use new_with_signer for PDA signing and validate target program addresses before invoking. Zero-copy accounts give you scale — plan your layout at design time and use the zero constraint correctly. And security hardening is not an optional step you add before audit — it is the default way you write every constraint from day one. If you come from a Chialisp background, you will notice that many of Solana’s manual security constraints are design-enforced in Chia’s UTXO model; the comparison sharpens your understanding of both. Start building, run the checklist on everything you write, and ship programs you would trust with your own funds.
Solana Anchor Tutorial Advanced FAQs
What is the best solana anchor tutorial for advanced developers?
The best starting points for an advanced solana anchor tutorial are the official Anchor Book (book.anchor-lang.com), which covers PDAs, CPIs, and zero-copy with dense but accurate documentation, and the Anchor by Example repository on GitHub, which provides runnable code for patterns like on-chain voting, escrow, and account management. Pair those with a dedicated CPI tutorial that builds two interacting programs (puppet and puppet master) to solidify the cross-program calling model.
How do PDAs work as signing authorities in Anchor?
A PDA cannot hold a private key, so when your program needs the PDA to sign a CPI, you reconstruct the PDA’s seeds at runtime and pass them to CpiContext::new_with_signer. Solana’s runtime verifies the derivation matches the PDA listed as a signer, and if it does, the CPI is authorized without any private key involvement.
What is the difference between zero and init in Anchor zero-copy accounts?
The init constraint allocates a new account and sets its Anchor discriminator in one step, and it works with standard Borsh-deserialized accounts. The zero constraint is used for zero-copy accounts that have already been allocated (typically by the System Program in a prior instruction) but not yet assigned an Anchor discriminator — it tells Anchor to write the discriminator and attach the zero-copy type without running a full deserialization.
What security checks should I run on a solana anchor tutorial advanced project before mainnet?
Before deploying any advanced solana anchor tutorial project to mainnet, verify these five points: every instruction has explicit signer checks on all authority accounts; every PDA uses a stored canonical bump via bump = account.bump; every account uses typed Anchor wrappers rather than raw AccountInfo; every CPI validates the target program’s address against a hardcoded pubkey; and has_one or constraint verifies all account relationships. Running the SlowMist Solana smart contract security checklist against your code is a practical final step before audit.
How does Chialisp handle security compared to Anchor?
Chialisp enforces security at the consensus layer — a coin can only be spent when its puzzle produces valid conditions, and those conditions are verified by every node on the Chia network without any manual programmer checks. Anchor requires the developer to enforce security through explicit constraints in the #[derive(Accounts)] struct; the framework provides the tools, but using them correctly is the developer’s responsibility. The result is that Anchor is more flexible but demands more discipline, while Chialisp is more constrained by design.
Solana Anchor Tutorial Advanced Citations
- Anchor Lang — Official Anchor Framework Documentation: https://www.anchor-lang.com/
- The Anchor Docs — Cross Program Invocation: https://www.anchor-lang.com/docs/basics/cpi
- The Anchor Docs — Program Derived Address: https://www.anchor-lang.com/docs/basics/pda
- Solana Documentation — Cross Program Invocations: https://solana.com/docs/core/cpi
- Solana Cookbook — Programs Reference: https://solanacookbook.com/references/programs.html
- Anchor by Example — GitHub Repository: https://github.com/coral-xyz/anchor/tree/master/examples
- SlowMist — Solana Smart Contract Security Best Practices: https://github.com/slowmist/solana-smart-contract-security-best-practices
- Anchor Lang — docs.rs API Reference: https://docs.rs/anchor-lang/latest/anchor_lang/
- Chialisp — Official Smart Coin Documentation: https://chialisp.com/
- Chiatribe — Chialisp Transforming Blockchain: https://chiatribe.com/chialisp-transforming-blockchain-unlocking-next-gen-smart-contracts/
- Solana Foundation — Program Security: https://solana.com/developers/guides/getstarted/intro-to-native-rust
- Coral XYZ — Anchor GitHub Source: https://github.com/coral-xyz/anchor
