Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Solana development skill covering @solana/kit v5, Anchor programs, LiteSVM testing, and security patterns.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/anchor/migrating-v0.32-to-v1.md
1---2title: Anchor v0.32 → v1 Migration Guide3description: Step-by-step checklist for upgrading an Anchor program workspace from v0.32.x to v1. Covers dependency bumps, CPI context changes, duplicate mutable account errors, legacy IDL account closure, declare_program! renames, interface-instructions removal, CLI commands, and new v1 features.4---56# Anchor v0.32 → v1 Migration78Full upgrade checklist for an Anchor workspace from v0.32.x to v1. Triage which items apply, then work through them in order.910Items marked **[COMPILE]** will prevent the program from building if not addressed. Items marked **[TS]** affect TypeScript clients. Items marked **[CLI]** affect developer workflow. Items marked **[DEPLOY]** must happen in the right order relative to deployment. Items marked **[CLIENT]** affect the Rust `anchor-client` crate.1112---1314## Applying the Migration (order matters)1516IDL housekeeping and the program code upgrade are **independent tracks** that can be done in parallel, but have one hard constraint: legacy IDL accounts must be closed with the **v0.32 CLI before deploying v1**.1718### Before deploying v1 (old program still live)1920A1. **Re-publish IDL to the new v1 location** *(v1 CLI)* — `anchor idl init` / `anchor idl upgrade`, or use `program-metadata` CLI directly (see §5).21A2. **Update and publish clients** — update any clients that fetch the on-chain IDL to read from the new v1 location, then deploy them.22A3. **Close legacy IDL accounts** *(v0.32 CLI)* on every cluster (see §5). Deploying the v1 binary or upgrading the CLI first makes this impossible.23> **Client notice:** for minimal downtime, follow this order — new IDL first, then clients, then close legacy accounts. Clients depending on the old location will continue to work until A3.2425### Program code upgrade (requires v1 CLI)26270. **Update toolchain** — bring Anchor CLI, AVM, and Solana CLI to the required versions first (see §0).281. **Audit** — run `cargo check` with bumped deps and collect all errors before fixing anything.292. **Fix compile errors in order** — deps → CPI context → duplicate accounts → `declare_program!` renames → multiple `#[error_code]` blocks → `Context` lifetime annotations.303. **`anchor build`** — confirms Rust is clean.314. **Update TS** — rename package imports, rerun `yarn install` / `npm install`.325. **Run tests** — `anchor test` (surfpool) or `anchor test -- --features some-feature`.336. **Deploy** — `anchor deploy`.3435---3637## 0. Check and update toolchain [CLI]3839Verify your current versions before touching any code:4041```bash42anchor --version # target: 1.0.043avm --version44solana --version # recommended: 3.1.1045rustc --version # must support edition 2021; 1.75+ recommended46```4748**Update AVM and Anchor CLI:**4950If your current `avm` supports `self-update`:51```bash52avm self-update53avm install 1.0.054avm use 1.0.055```5657Otherwise bootstrap via `cargo`:58```bash59cargo install avm --git https://github.com/solana-foundation/anchor --tag v1.0.0 --locked60avm install 1.0.061avm use 1.0.062```6364**Without AVM** — install `anchor-cli` directly:65```bash66cargo install --git https://github.com/solana-foundation/anchor --tag v1.0.0 anchor-cli --locked67```6869**Update Solana CLI** (if below 3.x):70```bash71sh -c "$(curl -sSfL https://release.anza.xyz/v3.1.10/install)"72solana --version # confirm 3.1.1073```7475---7677## 1. Update dependencies [COMPILE]7879**`Cargo.toml` (workspace root and program crate):**8081```toml82# Before83anchor-lang = "0.32.1"84anchor-spl = "0.32.1"85solana-program = "2"8687# After88anchor-lang = "1.0.0"89anchor-spl = "1.0.0"90solana-program = "^3" # and any other solana-* crate that appears directly91```9293- All `solana-*` crates that appear in `[dependencies]` must be `^3` or higher.9495**Add `resolver = "2"` to the workspace root `Cargo.toml`:**9697```toml98[workspace]99members = ["programs/*"]100resolver = "2" # required for edition 2021 members101```102103Without `resolver = "2"`, Cargo uses the v1 feature resolver, which unifies features across all targets. This causes spurious dependency conflicts and unexpected feature activation when mixing Solana/Anchor crates — manifesting as duplicate type errors or missing trait implementations that disappear once the resolver is set correctly. Any workspace containing at least one `edition = "2021"` crate should have this set.104- The `cargo update` workarounds for 0.32 (`base64ct --precise 1.6.0`, `constant_time_eq --precise 0.4.1`, `blake3 --precise 1.5.5`) are no longer needed — remove them.105- If you transitively depended on `solana-sdk` for signing, use `solana-signer` directly.106107See [compatibility-matrix.md](../compatibility-matrix.md) for the full Anchor v1 ↔ Solana CLI version table.108109**Dev dependencies — `litesvm` / `anchor-litesvm`:**110111When you bump `solana-*` crates to `^3`, also bump `litesvm` in `[dev-dependencies]`. The correct version depends on which minor series of the granular `solana-*` crates your workspace resolves to:112113| litesvm | Solana granular crates era | Key markers |114|---------|---------------------------|-------------|115| `0.8.2` | `~3.0` | `solana-hash ~3.0`, `solana-vote-interface 4.0`, `solana-system-interface 2.0` |116| `0.9.1` | `~3.1`–`~3.3` | `solana-hash 4.0`, `solana-vote-interface 5.0`, `solana-system-interface 3.0` |117| `>0.10.0` | `3.3+` | follow latest releases when on cutting-edge solana-* deps |118119```toml120# [dev-dependencies] — pick the row that matches your solana-* versions121litesvm = "0.8.2" # solana-* ~3.0122# litesvm = "0.9.1" # solana-* ~3.1–3.3 (solana-hash 4.0, solana-vote-interface 5.0)123124# If using the Anchor wrapper:125anchor-litesvm = "0.3" # requires anchor-lang ^1.0.0 and litesvm ^0.8.2126```127128> **Tip:** run `cargo tree -d` after bumping — a duplicate `solana-*` in the dependency tree at two incompatible minor versions is the most common sign you've picked the wrong `litesvm` version.129130**`package.json` [TS] — full package rename table:**131132| Before (`@coral-xyz/…`) | After (`@anchor-lang/…`) |133|-------------------------|--------------------------|134| `@coral-xyz/anchor` | `@anchor-lang/core` |135| `@coral-xyz/spl-token` | `@anchor-lang/spl-token` |136| `@coral-xyz/anchor-errors` | `@anchor-lang/errors` |137| `@coral-xyz/borsh` | `@anchor-lang/borsh` |138| `@coral-xyz/anchor-cli` | `@anchor-lang/cli` |139140```json141// Before142{143"@coral-xyz/anchor": "^0.32.1",144"@coral-xyz/spl-token": "^0.32.1"145}146147// After148{149"@anchor-lang/core": "^1.0.0",150"@anchor-lang/spl-token": "^1.0.0"151}152```153154```typescript155// Before156import * as anchor from "@coral-xyz/anchor";157import { Program, AnchorProvider, BN } from "@coral-xyz/anchor";158import { Idl } from "@coral-xyz/anchor/dist/cjs/idl"; // deep import159160// After161import * as anchor from "@anchor-lang/core";162import { Program, AnchorProvider, BN } from "@anchor-lang/core";163import { Idl } from "@anchor-lang/core"; // IDL types live at root now164```165166Find all occurrences:167```bash168grep -r "@coral-xyz" --include="*.ts" --include="*.js" --include="package.json" .169grep -r "dist/cjs/idl" --include="*.ts" --include="*.js" .170```171172---173174## 2. Fix CPI context construction [COMPILE]175176`CpiContext::new` and `CpiContext::new_with_signer` no longer accept a program `AccountInfo` as the first argument. Pass the program's **`Pubkey`** (program ID) directly instead. Remove the program account from the accounts struct.177178```rust179// Before (v0.32)180#[derive(Accounts)]181pub struct TransferTokens<'info> {182#[account(mut)]183pub from: Account<'info, TokenAccount>,184#[account(mut)]185pub to: Account<'info, TokenAccount>,186pub authority: Signer<'info>,187pub token_program: Program<'info, Token>, // <-- needed to pass AccountInfo188}189190pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {191let cpi_accounts = Transfer {192from: ctx.accounts.from.to_account_info(),193to: ctx.accounts.to.to_account_info(),194authority: ctx.accounts.authority.to_account_info(),195};196let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);197token::transfer(cpi_ctx, amount)198}199200// After (v1) — program ID as first argument; program field removed from struct201#[derive(Accounts)]202pub struct TransferTokens<'info> {203#[account(mut)]204pub from: Account<'info, TokenAccount>,205#[account(mut)]206pub to: Account<'info, TokenAccount>,207pub authority: Signer<'info>,208// token_program no longer needed for CPI209}210211pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {212let cpi_accounts = Transfer {213from: ctx.accounts.from.to_account_info(),214to: ctx.accounts.to.to_account_info(),215authority: ctx.accounts.authority.to_account_info(),216};217let cpi_ctx = CpiContext::new(Token::id(), cpi_accounts);218token::transfer(cpi_ctx, amount)219}220221// PDA-signed CPI222// Before223let cpi_ctx = CpiContext::new_with_signer(ctx.accounts.token_program.to_account_info(), cpi_accounts, signer_seeds);224// After225let cpi_ctx = CpiContext::new_with_signer(Token::id(), cpi_accounts, signer_seeds);226```227228Well-known IDs: `Token::id()`, `System::id()`, `system_program::ID`. For external programs declared with `declare_program!`, use `my_program::ID`.229230---231232## 3. Resolve duplicate mutable account errors [COMPILE]233234Anchor now rejects instructions where the same account appears more than once as mutable.235236```237error: duplicate mutable account `vault` — use `dup` constraint if intentional238```239240**Option A — prevent aliasing with a constraint (accidental duplication):**241```rust242#[account(243mut,244constraint = token_b.key() != token_a.key() @ MyError::SameAccount245)]246pub token_b: Account<'info, TokenAccount>,247```248249**Option B — allow intentional duplication:**250```rust251#[account(mut, dup)]252pub destination: Account<'info, TokenAccount>,253```254255Checked types: `Account`, `LazyAccount`, `InterfaceAccount`, `Migration`. Read-only types and `UncheckedAccount` are not checked. Accounts under `init_if_needed` are now included in the check.256257---258259## 4. Update `declare_program!` usages [COMPILE]260261**Rename `utils` module to `parsers`:**262```rust263// Before264use my_external_program::utils::*;265use my_external_program::utils::parse_instruction;266267// After268use my_external_program::parsers::*;269use my_external_program::parsers::parse_instruction;270```271272```bash273grep -r "::utils::" --include="*.rs" .274```275276**Remove `interface-instructions` feature and `#[interface]` attribute:**277278The feature and attribute are gone. Use `#[instruction(discriminator = <const>)]` instead.279280```toml281# Before (Cargo.toml)282anchor-lang = { version = "0.32.1", features = ["interface-instructions"] }283284# After — feature removed entirely285anchor-lang = "1.0.0"286```287288```rust289// Before290#[interface(spl_transfer_hook_interface::execute)]291pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> { Ok(()) }292293// After — use the interface crate's discriminator constant directly294#[instruction(discriminator = spl_transfer_hook_interface::instruction::ExecuteInstruction::SPL_DISCRIMINATOR)]295pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> { Ok(()) }296```297298---299300## 5. Close legacy IDL accounts and re-publish [DEPLOY]301302> **⚠️ Do this just before deploying the v1 binary.** Once a v1 binary is live, the legacy IDL instructions are gone — rent in those accounts becomes permanently inaccessible.303304**Step 1 — close the legacy IDL account on every cluster:**305306> This must be run with the **Anchor CLI v0.32** while the **v0.32 binary is still deployed**. The v1 CLI's `idl` commands target the Program Metadata program and cannot interact with legacy IDL accounts. Upgrading the CLI before closing means you lose the ability to recover that rent.307308```bash309# with anchor-cli 0.32.x still installed310anchor idl close --provider.cluster devnet <PROGRAM_ID>311anchor idl close --provider.cluster mainnet-beta <PROGRAM_ID>312```313314**Step 2** — deploy the v1 binary: `anchor deploy`.315316**Step 3 — re-publish the IDL via Program Metadata.**317318Two options — pick one:319320**Option A: Anchor CLI** (resolved from workspace, no program ID needed):321```bash322anchor idl init --filepath target/idl/my_program.json # first publish323anchor idl upgrade --filepath target/idl/my_program.json # subsequent updates324```325326**Option B: `program-metadata` CLI** (usable immediately after closing, independent of the Anchor CLI and deploy cycle):327```bash328npm install -g @solana-program/program-metadata329program-metadata upload idl target/idl/my_program.json --program-id <PROGRAM_ID>330```331332Option B is useful when you want to push an updated IDL without going through a full `anchor deploy`, or when working outside an Anchor workspace. See the [program-metadata README](https://github.com/solana-program/program-metadata) for the full command reference and options.333334**What changes in v1:** programs have no `idl_create_buffer`, `idl_write`, `idl_set_buffer` entrypoints. IDL lives in a Program Metadata account managed by a separate on-chain program. Already-deployed v0.32 programs that were not closed retain their legacy IDL account; v1 tooling can read them but cannot manage them.335336---337338## 6. Update `AccountInfo` usage [WARNING]339340Using raw `AccountInfo<'info>` in `#[derive(Accounts)]` now emits a compile-time warning. These are warnings, not errors — migration can be incremental.341342| Old | New |343|-----|-----|344| `AccountInfo<'info>` (unknown data) | `UncheckedAccount<'info>` + `/// CHECK:` comment |345| `AccountInfo<'info>` (token account) | `InterfaceAccount<'info, TokenAccount>` |346| `AccountInfo<'info>` (executable program) | `Program<'info, MyProgram>` or `Interface<'info, T>` |347348### `UncheckedAccount::clone()` vs `.to_account_info()`349350In anchor v1, `.clone()` on `UncheckedAccount<'info>` returns `UncheckedAccount<'info>`, not `AccountInfo<'info>`. Any function or CPI account struct that expects `AccountInfo<'info>` will now fail:351352```rust353// Error: mismatched types — expected AccountInfo<'_>, found UncheckedAccount<'_>354let ctx_accounts = MyCpiAccounts {355some_account: ctx.accounts.some_unchecked.clone(),356..357};358359// Fix: use .to_account_info() explicitly360let ctx_accounts = MyCpiAccounts {361some_account: ctx.accounts.some_unchecked.to_account_info(),362..363};364```365366This commonly surfaces when constructing CPI account structs or calling helper functions that accept `AccountInfo<'info>` directly. Find occurrences:367368```bash369grep -rn "\.clone()" --include="*.rs" . | grep "UncheckedAccount\|merkle_tree\|tree_authority"370```371372---373374## 7. Suppress `unexpected_cfgs` warnings from macros [WARNING]375376Anchor and Solana derive macros emit code gated on cfg flags (`anchor-debug`, `custom-heap`, `custom-panic`) that Rust's `unexpected_cfgs` lint doesn't know about. This produces a wall of warnings after a clean build.377378**Option A — declare them as features in each program's `[features]`** (more targeted, recommended):379380```toml381# programs/my_program/Cargo.toml382[features]383anchor-debug = []384custom-heap = []385custom-panic = []386# keep your existing features — add these alongside them387```388389Declaring them as features tells Cargo they are valid cfg values, so the compiler stops warning about them without blanket-suppressing the entire `unexpected_cfgs` lint.390391**Option B — suppress workspace-wide via lints** (blunter, but simpler for large workspaces):392393```toml394# Cargo.toml (workspace root)395[workspace.lints.rust]396unexpected_cfgs = { level = "allow" }397```398399Then opt every program crate into the workspace lints:400401```toml402# programs/my_program/Cargo.toml403[lints]404workspace = true405```406407> **Note on Option B:** Do not use the `check-cfg` list form (`check-cfg = ['cfg(anchor-debug)', ...]`) — cfg names containing hyphens are rejected by the compiler with `invalid '--check-cfg' argument`. Use `level = "allow"` without the list.408409```bash410# find all program Cargo.toml files that still need updating411grep -rL "workspace = true" programs/*/Cargo.toml412```413414---415416## 8. Handle IDL external account exclusion [IDL]417418External account types (e.g. SPL Token `Mint`, `TokenAccount`) are no longer inlined in the generated IDL. Clients that relied on your IDL to deserialize third-party accounts must now use those programs' own clients.419420```typescript421// Before — type came from your program's IDL automatically422const mintAccount = await program.account.mint.fetch(mintAddress);423424// After — use the token program's own client425import { getMint } from "@solana/spl-token";426const mintAccount = await getMint(connection, mintAddress);427```428429---430431## 9. Switch the test runner [CLI]432433`anchor test` and `anchor localnet` now use **surfpool** by default.434435```toml436# Anchor.toml — opt out to standard validator437[tooling]438validator = "solana"439440# Or configure surfpool441[surfpool]442startup_wait = 5000443log_level = "info" # default is "none"444block_production_mode = "clock" # or "transaction"445datasource_rpc_url = "https://api.mainnet-beta.solana.com" # optional fork446```447448Add to `.gitignore`:449```450.surfpool/451```452453CI — surfpool must be installed explicitly:454```yaml455- name: Install surfpool456run: curl -sL https://run.surfpool.run/ | bash457```458459---460461## 10. Remove external `solana` CLI dependency [CLI]462463Anchor no longer shells out to `solana`. Update CI pipelines and scripts.464465| Before | After |466|--------|-------|467| `solana address` | `anchor address` |468| `solana balance` | `anchor balance` |469| `solana airdrop` | `anchor airdrop` |470| `solana program deploy` | `anchor deploy` |471| `solana logs` | `anchor logs` |472473Keep the `solana` CLI install step only if you use it directly (keypair generation, cluster switching, etc.).474475---476477## 11. Clean up `Anchor.toml` and removed CLI commands [CLI]478479**Remove `[registry]` from `Anchor.toml`:**480```toml481# Before — remove this entire section482[registry]483url = "https://anchor.projectserum.com"484```485486**Remove `arch` build options from `Anchor.toml`** (if present — `arch = "sbf"` etc. are no longer recognised):487```bash488grep -n "arch" Anchor.toml489```490491**`anchor login` is removed.** Remove it from CI scripts and `Makefile` targets; the `[registry]` section it served is gone.492493---494495## 12. Disallow multiple `#[error_code]` blocks [COMPILE]496497Having more than one `#[error_code]` block in a single program is now a compile-time error. Merge all error enums into one.498499```rust500// Before — two separate blocks compiled fine501#[error_code]502pub enum InitError {503AlreadyInitialized,504}505506#[error_code]507pub enum UpdateError {508InvalidAmount,509}510511// After — single merged enum512#[error_code]513pub enum MyProgramError {514AlreadyInitialized,515InvalidAmount,516}517```518519If you used `offset = N` to avoid code collisions between separate enums, that attribute continues to work on the merged single enum.520521```bash522grep -r "#\[error_code\]" --include="*.rs" .523```524525---526527## 13. Update `Context` lifetime annotations [COMPILE]528529`Context` was simplified from four lifetime parameters (`'a, 'b, 'c, 'info`) to one (`'info`). Most programs use `Context<MyAccounts>` without explicit lifetimes and are unaffected. If you annotated the lifetimes explicitly, remove the extra three.530531```rust532// Before (v0.32)533pub fn my_handler<'a, 'b, 'c, 'info>(534ctx: Context<'a, 'b, 'c, 'info, MyAccounts<'info>>,535) -> Result<()> { ... }536537// After (v1)538pub fn my_handler<'info>(ctx: Context<'info, MyAccounts<'info>>) -> Result<()> { ... }539// or simply (when the lifetime is inferred)540pub fn my_handler(ctx: Context<MyAccounts<'_>>) -> Result<()> { ... }541```542543```bash544grep -rn "Context<'" --include="*.rs" .545```546547---548549## 14. Update Borsh 1.x serialization usage [COMPILE]550551Anchor v1 depends on **borsh 1.x**, which removed several APIs present in borsh 0.10.552553### `try_to_vec()` removed554555`BorshSerialize::try_to_vec()` no longer exists. Replace every call with `borsh::to_vec(&value)?`.556557```rust558// Before559let data = my_struct.try_to_vec()?;560let hash = hashv(&[metadata.try_to_vec()?.as_slice()]);561562// After563let data = borsh::to_vec(&my_struct)?;564let hash = hashv(&[borsh::to_vec(metadata)?.as_slice()]);565```566567```bash568grep -rn "try_to_vec" --include="*.rs" .569```570571### Enum explicit discriminants conflict with anchor derive macros572573In borsh 1.x, enums with explicit integer discriminants require `#[borsh(use_discriminant=true)]`. However, this attribute conflicts with `#[derive(AnchorSerialize, AnchorDeserialize)]`, producing:574575```576error: multiple `borsh` attributes not allowed on a single item577error: cannot find attribute `borsh` in this scope578```579580**Fix**: If the explicit discriminants match the default ordinal values (0, 1, 2, …), simply remove them. The serialized layout is identical.581582```rust583// Before — conflicts with anchor derive macros584#[derive(AnchorSerialize, AnchorDeserialize)]585pub enum MyType {586Variant1 = 0,587Variant2 = 1,588}589590// After — remove explicit discriminants; borsh ordinal encoding is the same591#[derive(AnchorSerialize, AnchorDeserialize)]592pub enum MyType {593Variant1,594Variant2,595}596```597598If discriminant values *don't* match ordinal order (e.g., `Foo = 5, Bar = 10`) you must implement borsh serialization manually instead of relying on the derive macro.599600```bash601grep -rn " = [0-9]" --include="*.rs" programs/ # find enums with explicit discriminants602```603604---605606## 15. Update Solana SDK 3.x API changes [COMPILE]607608Anchor v1 uses **Solana SDK 3.x**, which has breaking API changes beyond just the version bump.609610### `anchor_lang::solana_program` re-export gaps611612In anchor v0.31, `anchor_lang::solana_program` re-exported the full `solana-program` crate. In v1 several sub-modules are no longer re-exported:613614| Module | Old import | New import |615|--------|-----------|------------|616| `keccak` | `anchor_lang::solana_program::keccak` | `solana_program::keccak` |617| `hash` | `anchor_lang::solana_program::hash` | `solana_program::hash` |618| `ed25519_program` | `anchor_lang::solana_program::ed25519_program` | `solana_program::ed25519_program` |619| `sysvar::instructions` | `anchor_lang::solana_program::sysvar::instructions` | `solana_program::sysvar::instructions` |620| `instruction::Instruction` | `anchor_lang::solana_program::instruction::Instruction` | `solana_program::instruction::Instruction` |621| `program::invoke_signed` | `anchor_lang::solana_program::program::invoke_signed` | `solana_program::program::invoke_signed` |622623**`system_instruction` quirk:** `system_instruction` is *not* accessible as `solana_program::system_instruction` in SDK 3.x when you depend on `solana-program` directly — that sub-module was removed from the crate root. However, it is still re-exported by Anchor: `anchor_lang::solana_program::system_instruction` continues to work. Use that path rather than importing from `solana_program` directly.624625```rust626// Fails in SDK 3.x with direct solana-program dep627use solana_program::system_instruction;628629// Works — Anchor still re-exports it630use anchor_lang::solana_program::system_instruction;631```632633**Fix**: Add `solana-program = { workspace = true }` to the program's `Cargo.toml` and import directly from `solana_program`.634635```toml636# program/Cargo.toml637[dependencies]638anchor-lang = { workspace = true }639solana-program = { workspace = true } # add this640```641642```rust643// Before644use anchor_lang::{prelude::*, solana_program::keccak};645use anchor_lang::{prelude::*, solana_program::sysvar::instructions::ID as IX_ID};646647// After648use anchor_lang::prelude::*;649use solana_program::keccak;650use solana_program::sysvar::instructions::ID as IX_ID;651```652653```bash654grep -rn "anchor_lang::solana_program" --include="*.rs" programs/655```656657### `AccountInfo::realloc` renamed to `resize`658659The `realloc(new_len, zero_init)` method was renamed to `resize(new_len)` in Solana SDK 3.x. The `zero_init` parameter is gone (new space is always zeroed).660661```rust662// Before663account_info.realloc(new_len, false)?;664account_info.realloc(0, false).map_err(Into::into)665666// After667account_info.resize(new_len)?;668account_info.resize(0).map_err(Into::into)669```670671```bash672grep -rn "\.realloc(" --include="*.rs" .673```674675### `MAX_PERMITTED_DATA_INCREASE` path change676677```rust678// Before679use solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;680// or681use anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE;682683// After684use solana_program::account_info::MAX_PERMITTED_DATA_INCREASE;685```686687### `CpiContext::new` takes `Pubkey`, not `AccountInfo`688689In anchor v1, the first argument to `CpiContext::new` / `CpiContext::new_with_signer` changed from `AccountInfo` to `Pubkey`.690691```rust692// Before693CpiContext::new(ctx.accounts.system_program.to_account_info(), cpi_accounts)694CpiContext::new_with_signer(ctx.accounts.system_program.to_account_info(), cpi_accounts, signer_seeds)695696// After — pass the program's Pubkey directly697CpiContext::new(System::id(), cpi_accounts)698CpiContext::new_with_signer(System::id(), cpi_accounts, signer_seeds)699// or use the constant700CpiContext::new(system_program::ID, cpi_accounts)701CpiContext::new_with_signer(solana_program::system_program::ID, cpi_accounts, signer_seeds)702```703704---705706## 16. Audit external program CPI crates [COMPILE]707708Any external CPI crate compiled against **anchor 0.31** will produce dozens of trait-bound errors in your workspace because the `AccountDeserialize`, `AccountSerialize`, `Owner`, and `Id` traits changed in anchor v1. Common culprits: `bubblegum-cpi`, `account-compression-cpi`, `tuktuk-program`.709710**Symptoms:**711```712error[E0277]: the trait bound `Noop: anchor_lang::Id` is not satisfied713error[E0277]: the trait bound `SplAccountCompression: anchor_lang::Id` is not satisfied714error[E0277]: `Program<'info, T>: anchor_lang::Id` not satisfied715```716717### Step 1 — identify affected crates718719```bash720cargo tree 2>&1 | grep -E "anchor-lang|anchor-spl" | sort -u721```722723Look for any dependency still pulling in `anchor-lang 0.x`. Each such crate needs to be updated before your workspace will compile.724725### Step 2 — check for an updated release726727For each affected crate, check whether the upstream maintainer has already published an anchor v1-compatible version:728729```bash730cargo search <crate-name>731```732733Or check the crate's repository for a release or branch targeting anchor v1. If a compatible version exists, bump the version specifier in `Cargo.toml` and you're done.734735### Step 3 — update the crate yourself (if you own the repo)736737If you control the affected crate in a separate repository, apply the same migration steps from this guide to that crate first (deps, CPI context, borsh, SDK API changes), publish or reference it via a git dep, then return here.738739```toml740# Temporary git dep while waiting for a crates.io release741my-cpi-crate = { git = "https://github.com/my-org/my-cpi-crate", branch = "anchor-v1" }742```743744### Step 4 (last resort) — vendor the crate locally745746If no update is available and you don't control the upstream repo, vendor a minimal local copy. Create a `vendor/` crate that uses `declare_program!` against the program's IDL JSON, add it as a workspace member, and point your workspace dependency to the path.747748> **Do not use `[patch]`** for workspace members — cargo will see two versions of the same crate and report `multiple 'crate-name' packages in this workspace`. Use `path = ...` in `[workspace.dependencies]` instead.749750Apply the standard anchor v1 fixes to any vendored source (realloc → resize, try_to_vec → borsh::to_vec, CpiContext::new, etc.) as you go.751752---753754## 17. Migrate `spl-token` / `spl-token-2022` / `spl-associated-token-account` direct dependencies [COMPILE]755756`spl-token 7.x`, `spl-token-2022 7.x`, and `spl-associated-token-account 6.x` depend on `solana-program 2.x`. If your program has a direct `[dependencies]` entry for any of these crates, you'll see type mismatches because your workspace uses `solana-program 3.x`:757758```759error[E0308]: mismatched types760expected `Pubkey` (solana-program 3.x)761found `__Pubkey` (solana-program 2.x, re-exported via spl-token)762```763764This also affects any import path through these crates:765```rust766use spl_token::solana_program::instruction::Instruction; // wrong Instruction type767use spl_token::ID; // wrong Pubkey type768```769770### Preferred fix — migrate to the interface crates771772The interface crates (`spl-token-interface`, `spl-token-2022-interface`, `spl-associated-token-account-interface`) are slim, solana-program-3.x-compatible crates that expose the IDs, instruction builders, and account types you need without dragging in the old SDK version.773774**If you depend on `spl-token`** → migrate to `spl-token-interface 2.0`:775776```toml777# Cargo.toml778spl-token-interface = "2.0" # replaces spl-token = "7.x"779```780781```rust782// Before783use spl_token::ID as TOKEN_PROGRAM_ID;784use spl_token::instruction::transfer;785786// After787use spl_token_interface::ID as TOKEN_PROGRAM_ID;788use spl_token_interface::instruction::transfer;789```790791**If you depend on `spl-token-2022`** → migrate to `spl-token-2022-interface 2.1`:792793```toml794# Cargo.toml795spl-token-2022-interface = "2.1" # replaces spl-token-2022 = "7.x"796```797798```rust799// Before800use spl_token_2022::ID as TOKEN_2022_PROGRAM_ID;801802// After803use spl_token_2022_interface::ID as TOKEN_2022_PROGRAM_ID;804```805806**If you depend on `spl-associated-token-account`** → migrate to `spl-associated-token-account-interface 2.0`:807808```toml809# Cargo.toml810spl-associated-token-account-interface = "2.0" # replaces spl-associated-token-account = "6.x"811```812813```rust814// Before815use spl_associated_token_account::ID as ATA_PROGRAM_ID;816use spl_associated_token_account::get_associated_token_address;817818// After819use spl_associated_token_account_interface::ID as ATA_PROGRAM_ID;820use spl_associated_token_account_interface::get_associated_token_address;821```822823### Fallback — use `anchor_spl` re-exports824825If you only need program IDs, ATAs, or account structs and don't call instruction builders directly, `anchor_spl` re-exports compatible versions and requires no extra dependency entry:826827```rust828use anchor_spl::token::ID as TOKEN_PROGRAM_ID;829use anchor_spl::token_2022::ID as TOKEN_2022_PROGRAM_ID;830use anchor_spl::associated_token::ID as ATA_PROGRAM_ID;831use anchor_spl::associated_token::get_associated_token_address;832use solana_program::instruction::Instruction; // not via spl_token833```834835```bash836grep -rn "spl_token::\|spl_token_2022::\|spl_associated_token_account::" --include="*.rs" programs/837grep -rn "spl-token\|spl-token-2022\|spl-associated-token-account" --include="Cargo.toml" programs/838```839840---841842## What's New in v1843844Worth adopting during migration:845846- **`Migration<'info, From, To>`** — safe account schema migrations between layouts.847- **`LazyAccount`** — heap-allocated read-only access, auto-optimized for unit-variant enums and empty arrays.848- **Relaxed seeds syntax** — PDA seeds accept richer Rust expressions beyond literals and `.as_ref()`.849- **`FnMut` event closures** — event listeners now accept `FnMut`, allowing mutable captures.850- **Generic `Program<'info>`** — usable without a type parameter for executable-only validation when the concrete program type is not statically known: `pub program: Program<'info>`.851- **`declare_program!` without `anchor_lang`** — `anchor_client` alone is now sufficient for client-side `declare_program!` usage; no need to pull in `anchor_lang` as a dependency.852- **Owner re-checked on `.reload()`** — `account.reload()` now re-validates the account owner the same as initial load. Programs that previously reloaded accounts owned by a different program will now error.853- **`common::close` accepts references** — no need to call `.to_account_info()` at every `common::close(...)` call site.854- **`Owners` in prelude** — `anchor_lang::prelude::Owners` is re-exported; remove any manual `use` statement for it.855- **Borsh 1.5.7** — both Rust and TypeScript Borsh implementations upgraded. Ensure your `borsh` entry in `Cargo.toml` is compatible.856- **Lifecycle hooks** — add a `[hooks]` section to `Anchor.toml` to run shell commands at `pre_build`, `post_build`, `pre_test`, `post_test`, `pre_deploy`, `post_deploy`.857