# Table of Contents - [Blueshift | Anchor for Dummies | Anchor 101](#blueshift-anchor-for-dummies-anchor-101) - [Blueshift | Anchor for Dummies | Testing your Program](#blueshift-anchor-for-dummies-testing-your-program) - [Blueshift | Anchor for Dummies | Anchor Accounts](#blueshift-anchor-for-dummies-anchor-accounts) - [Blueshift | Anchor for Dummies | Program Deployment](#blueshift-anchor-for-dummies-program-deployment) - [Blueshift | Anchor for Dummies | Conclusion](#blueshift-anchor-for-dummies-conclusion) - [Blueshift | Anchor for Dummies | Anchor Instructions](#blueshift-anchor-for-dummies-anchor-instructions) - [Blueshift | Anchor for Dummies | Client Side Development](#blueshift-anchor-for-dummies-client-side-development) - [Blueshift | Anchor for Dummies | Advanced Anchor](#blueshift-anchor-for-dummies-advanced-anchor) --- # Blueshift | Anchor for Dummies | Anchor 101 [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Anchor 101 lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Anchor 101 ========== ![Introduction to Anchor](https://learn.blueshift.gg/graphics/course-banners/anchor-for-dummies.png) What is Anchor ---------------- Anchor is the premier framework for Solana smart-contract development, offering a complete workflow for writing, testing, deploying, and interacting with onchain programs. ### Key advantages * **Reduced boilerplate**: Anchor abstracts the repetitive work of account management, instruction serialization, and error handling so you can focus on just the core business logic. * **Built-in security**: Rigorous checks such as account-ownership verification and data validation run out of the box, mitigating many common vulnerabilities before they surface. ### Anchor Macros * `declare_id!()`: Declares at what onchain address the program lives. * `#[program]`: Marks the module that contains every instruction entrypoint and business-logic function. * `#[derive(Accounts)]`: Lists the accounts an instruction requires and enforces their constraints automatically. * `#[error_code]`: Defines custom, human-readable error types that make debugging clearer and faster. Together, these procedural macros abstract away low-level byte management, allowing you to deliver secure, production-grade Solana programs with far less effort. ### Program Structure Let's begin with a bare-bones version of a program to explain in detail what each macro actually does: rust declare_id!("22222222222222222222222222222222222222222222"); #[program] pub mod blueshift_anchor_vault { use super::*; pub fn deposit(ctx: Context, amount: u64) -> Result<()> { // ... Ok(()) } pub fn withdraw(ctx: Context) -> Result<()> { // ... Ok(()) } } #[derive(Accounts)] pub struct VaultAction<'info> { // ... } #[error_code] pub enum VaultError { // ... } This will transform into focused modules instead of cramming everything into the `lib.rs` for more structured programs. The program folder tree will look roughly like this: src ├── instructions │ ├── instruction1.rs │ ├── mod.rs │ ├── instruction2.rs │ └── instruction3.rs ├── errors.rs ├── lib.rs └── state.rs #### `declare_id!()` The `declare_id!()` macro assigns to your program its onchain address; a unique public key derived from the keypair in the project's `target` folder. That keypair signs and deploys the compiled `.so` binary containing all program logic and data. **Note:** We use the placeholder `222222...` in Blueshift examples because of our internal test suite. In production, Anchor will generate a fresh program ID for you when you run the standard build and deploy commands. #### `#[program]` & `#[derive(Accounts)]` Every instruction has its own **Context** struct that lists all the accounts and, optionally, any data the instruction will need. In this example, both `deposit` and `withdraw` share the same accounts; for that reason, we're going to create a single account struct called `VaultAction` to keep things more efficient and easy. #### A closer look at `#[derive(Accounts)]` macro rust #[derive(Accounts)] pub struct VaultAction<'info> { #[account(mut)] pub signer: Signer<'info>, #[account(\ mut,\ seeds = [b"vault", signer.key().as_ref()],\ bump,\ )] pub vault: SystemAccount<'info>, pub system_program: Program<'info, System>, } As we can see from the code snippet, the `#[derive(Accounts)]` macro serves three critical responsibilities: * Declares all the accounts a specific instruction needs. * Enforce constraint checks automatically, blocking many bugs and potential exploits at runtime. * Generates helper methods that let you access and mutate accounts safely. It accomplishes this through a combination of account types and inline attributes. **Account types in our example** * `Signer<'info>`: Verifies the account signed the transaction; essential for security and for CPIs that demand a signature. * `SystemAccount<'info>`: Confirms ownership of the account by the System Program. * `Program<'info, System>`: Ensures the account is executable and matches the System Program ID, enabling CPIs such as account creation or lamport transfers. **Inline attributes you'll encounter** * `mut`: Flags the account as mutable; mandatory whenever its lamport balance or data may change. * `seeds & bump`: Verifies the account is a Program-Derived Address (PDA) generated from the provided seeds plus a bump byte. **Note** PDAs are important because: * When used by the program that owns them, PDAs can sign CPIs on the program's behalf. * They give you deterministic, verifiable addresses for persisting program state. #### `#[error_code]` The `#[error_code]` macro lets you define clear, custom errors inside the program. rust #[error_code] pub enum VaultError { #[msg("Vault already exists")] VaultAlreadyExists, #[msg("Invalid amount")] InvalidAmount, } Each enum variant can carry a `#[msg(...)]` attribute that logs a descriptive string whenever the error occurs; far more helpful than a raw numeric code during debugging. [Next LessonAnchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/anchor-101/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Anchor 101 --- # Blueshift | Anchor for Dummies | Testing your Program [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Testing your Program lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Testing Your Program ==================== Thorough testing prevents financial losses, builds user trust, and ensures your program behaves correctly under all conditions. > Test rigorously before mainnet deployment. TypeScript Tests ------------------ TypeScript testing is the most common approach since you'll need TypeScript for your dApp client anyway. This lets you develop tests and client code simultaneously. We covered client-side setup in detail [here](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) . > Every Anchor CLI project includes a test folder with a TypeScript file ready for testing. TypeScript tests offer key advantages: * Mirror real client interactions * Test complex workflows and edge cases * Provide immediate feedback on API changes * Validate success and error scenarios Run tests with: bash anchor test > Run on localnet by setting the cluster to localnet in your configuration. This spins up a local validator and adds 1,000 SOL to the provider wallet. If you need additional accounts with data inside of them, look at running a [Local Validator](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program#running-a-local-validator) Mollusk Tests --------------- When you need granular control over testing environments or complex program state setup, Mollusk provides the solution. Mollusk is a Rust testing framework built specifically for Solana programs. It enables you to: * Test program logic in isolation without network overhead * Set up complex account states easily * Run tests faster than full integration tests * Mock specific blockchain conditions and edge cases We covered Mollusk testing thoroughly [here](https://learn.blueshift.gg/en/courses/testing-with-mollusk) . Create a new Anchor program with Mollusk: bash anchor init --test-template mollusk Run tests using: bash anchor test LiteSVM Tests --------------- When you need the same granular control over your program state, like Mollusk, but in Typescript LiteSVM provides the optimal solution. LiteSVM is a lightweight testing framework that runs the Solana Virtual Machine directly in your test process. It enables you to: * Execute tests significantly faster than traditional frameworks like `solana-program-test` * Manipulate account states and sysvars with precision * Test across multiple languages: TypeScript, Rust, and Python * Mock complex blockchain conditions and edge cases effortlessly LiteSVM eliminates validator overhead by embedding the VM within your tests, delivering the speed needed for rapid development cycles without sacrificing testing accuracy. We covered LiteSVM testing thoroughly [here](https://learn.blueshift.gg/en/courses/testing-with-litesvm) . You can set up your Anchor provider and use the client-side setup we saw [previously](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) with the `anchor-litesvm` package. Installe the `anchor-litesvm` package. bash npm install git:https://github.com/LiteSVM/anchor-litesvm Then change the default Anchor provider to `LiteSVMProvider` like this: ts import { fromWorkspace, LiteSVMProvider } from "anchor-litesvm"; test("anchor", async () => { const client = fromWorkspace("target/types/.ts"); const provider = new LiteSVMProvider(client); const program = new Program(IDL, provider); // program.methods.. }) Running a Local Validator --------------------------- Mirror mainnet behavior locally using a validator that acts as your personal blockchain sandbox. This happens automatically when you set your cluster to localnet. The local validator operates a simplified Solana ledger with native programs pre-installed. By default, it only stores test data and lacks access to existing mainnet accounts—limiting testing with established protocols. ### Configuring Your Local Validator Customize your validator in `Anchor.toml` under the `[test]` section: [test] startup_wait = 10000 The `startup_wait` flag delays validator startup, useful when cloning multiple accounts that increase load time. ### Cloning Mainnet Accounts Clone existing mainnet accounts and programs using `[test.validator]` configuration: [test.validator] url = "https://api.mainnet-beta.solana.com" [[test.validator.clone]] address = "7NL2qWArf2BbEBBH1vTRZCsoNqFATTddH6h8GkVvrLpG" [[test.validator.clone]] address = "2RaN5auQwMdg5efgCaVqpETBV8sacWGR8tkK4m9kjo5r" [[test.validator.clone]] address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" The `clone` section copies accounts from the specified cluster. When an account links to a program managed by the "BPF upgradeable loader", Anchor automatically clones the associated program data account. ### Loading Local Account Data Load local accounts from JSON files using the `account` flag: [[test.validator.account]] address = "Ev8WSPQsGb4wfjybqff5eZNcS3n6HaMsBkMk9suAiuM" filename = "some_account.json" This approach works well for testing with pre-configured account states or specific configurations that don't exist on mainnet. Running Surfnet ----------------- Testing Solana programs that rely on Cross-Program Invocations (CPIs) traditionally requires developers to dump accounts and programs from mainnet, like we saw in the Local Validator section. This process works for a few accounts, but becomes completely unfeasible when testing CPIs into complex programs like Jupiter, which can depend on 40+ accounts and 8+ programs. Surfnet serves as a drop-in replacement for solana-test-validator that enables developers to simulate programs locally using mainnet accounts fetched on-demand To use it, just install surfpool using the official [Installation Page](https://docs.surfpool.run/install) and then run: bash surfpool start You can now connect to Surfnet by targeting the local validator: ts import { Connection } from "@solana/web3.js"; const connection = new Connection("http://localhost:8899", "confirmed"); We covered Surfnet setup and usage thoroughly [here](https://learn.blueshift.gg/en/courses/testing-with-surfpool) . [Next LessonProgram Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/testing-your-program/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Testing your Program --- # Blueshift | Anchor for Dummies | Anchor Accounts [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Anchor Accounts lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Accounts ======== We saw the `#[account]` macro, but naturally on Solana there are different types of accounts. For this reason it is worth taking a moment to see how generally accounts on Solana work, but more in depth, how they work with Anchor. General Overview ------------------ On Solana, every piece of state lives in an account; picture the [ledger](https://solana.com/docs/references/terminology#ledger) as one giant table where each row shares the same base schema: rust pub struct Account { /// lamports in the account pub lamports: u64, /// data held in this account #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] pub data: Vec, /// the program that owns this account and can mutate its lamports or data. pub owner: Pubkey, /// `true` if the account is a program; `false` if it merely belongs to one pub executable: bool, /// the epoch at which this account will next owe rent (currently deprecated and is set to `0`) pub rent_epoch: Epoch, } All accounts on Solana share the same base layout. What sets them apart is: 1. The owner: The program that has exclusive rights to modify the account's data and lamports. 2. The data: Used by the owner program to distinguish between different account types. When we talk about Token Program Accounts, what we mean is an account where the `owner` is the Token Program. Unlike a System Account whose data field is empty, a Token Program Account can be either a **Mint** or a **Token** account. We use discriminators to distinguish between them. Just as the Token Program can own accounts, so can any other program even our own. Program Accounts ------------------ Program accounts are the foundation of state management in Anchor programs. They allow you to create custom data structures that are owned by your program. Let's explore how to work with them effectively. ### Account Structure and Discriminators Every program account in Anchor needs a way to identify its type. This is handled through discriminators, which can be either: 1. **Default Discriminators**: An 8-byte prefix generated using `sha256("account:")[0..8]` for accounts, or `sha256("global:")[0..8]` for instructions. The seeds use PascalCase for accounts and snake\_case for instructions. Anchor Discriminator Calculator Account/Instruction Name Account sha256("account:" + PascalCase(seed))\[0..8\] \[0, 0, 0, 0, 0, 0, 0, 0\] 2. **Custom Discriminators**: Starting with Anchor `v0.31.0`, you can specify your own discriminator: rust #[account(discriminator = 1)] // single-byte pub struct Escrow { … } **Important Notes about Discriminators**: * They must be unique across your program * Using `[1]` prevents using `[1, 2, …]` as these also start with `1` * `[0]` cannot be used as it conflicts with uninitialized accounts ### Creating Program Accounts To create a program account, you first define your data structure: rust use anchor_lang::prelude::*; #[derive(InitSpace)] #[account(discriminator = 1)] pub struct CustomAccountType { data: u64, } Key points about program accounts: * Maximum size is 10,240 bytes (10 KiB) * For larger accounts, you'll need `zero_copy` and chunked writes * The `InitSpace` derive macro automatically calculates the required space * Total space = `INIT_SPACE` + `DISCRIMINATOR.len()` The total space in bytes needed for the account is the sum of `INIT_SPACE` (size of all the fields combined) and the discriminator size (`DISCRIMINATOR.len()`). Solana accounts require a rent deposit in lamports, which depends on the size of the account. Knowing the size helps us calculate how many lamports we need to deposit to make the account open. Here's how we're going to initiate the account in our `Account` struct: rust #[account(\ init,\ payer = ,\ space = // CustomAccountType::INIT_SPACE + CustomAccountType::DISCRIMINATOR.len(),\ )] pub account: Account<'info, CustomAccountType>, Here are some of the fields used in the `#[account]` macro, beyond the `seeds` and `bump` fields that we have already covered, and what they do: * `init`: tells Anchor to create the account * `payer`: which signer funds the rent (here, the maker) * `space`: how many bytes to allocate. This is where the rent calculation happens as well After creation, you can modify the account's data. If you need to change its size, use reallocation: rust #[account(\ mut, // Mark as mutable\ realloc = , // New size\ realloc::payer = , // Who pays for the change\ realloc::zero = // Whether to zero new space\ )] **Note**: When reducing account size, set `realloc::zero = true` to ensure old data is properly cleared. Lastly, when the account is no longer needed, we can close it to recover rent: rust #[account(\ mut, // Mark as mutable\ close = , // Where to send remaining lamports\ )] pub account: Account<'info, CustomAccountType>, We then can add PDAs, deterministic addresses derived from seeds and a program ID that are particularly useful for creating predictable account addresses, into these constraints like this: rust #[account(\ seeds = , // Seeds for derivation\ bump // Standard bump seed\ )] pub account: Account<'info, CustomAccountType>, **Note**: that PDAs are deterministic: same seeds + program + bump always produce the same address and that the bump ensures the address is off the ed25519 curve Since calculating the bump can "burn" a lot of CUs, it's always good to save it into the account or pass it into the instruction and validate it without having to calculate like this: rust #[account(\ seeds = ,\ bump = \ )] pub account: Account<'info, CustomAccountType>, And it's possible to derive a PDA from another program by passing the address of the program it's derived from like this: rust #[account(\ seeds = ,\ bump = ,\ seeds::program = \ )] pub account: Account<'info, CustomAccountType>, ### Lazy Account Starting from Anchor 0.31.0, `LazyAccount` provides a more performant way to read account data. Unlike the standard `Account` type that deserializes the entire account onto the stack, `LazyAccount` is a read-only, heap allocated account that uses only 24 bytes of stack memory and lets you selectively load specific fields. Start by enabling the feature in your `Cargo.toml`: anchor-lang = { version = "0.31.1", features = ["lazy-account"] } Now we can use it like this: rust #[derive(Accounts)] pub struct MyInstruction<'info> { pub account: LazyAccount<'info, CustomAccountType>, } #[account(discriminator = 1)] pub struct CustomAccountType { pub balance: u64, pub metadata: String, } pub fn handler(ctx: Context) -> Result<()> { // Load specific field let balance = ctx.accounts.account.get_balance()?; let metadata = ctx.accounts.account.get_metadata()?; Ok(()) } > `LazyAccount` is read-only. Attempting to mutate fields will panic since you're working with references, not stack-allocated data. When CPIs modify the account, cached values become stale. For this reason you need to use the `unload()` function to refresh: rust // Load the initial value let initial_value = ctx.accounts.my_account.load_field()?; // Do CPI... // We still have a reference to the account from `initial_value`, drop it before `unload` drop(initial_value); // Load the updated value let updated_value = ctx.accounts.my_account.unload()?.load_field()?; Token Accounts ---------------- The Token Program, part of the Solana Program Library (SPL), is the built-in toolkit for minting and moving any asset that isn't native SOL. It has instructions to create tokens, mint new supply, transfer balances, burn, freeze, and more. This program owns two key account types: * **Mint Account**: stores the metadata for one specific token: supply, decimals, mint authority, freeze authority, and so on * **Token Account**: holds a balance of that mint for a particular owner. Only the owner can reduce the balance (transfer, burn, etc.), but anyone can send tokens to the account, increasing its balance ### Token Accounts in Anchor Natively, the core Anchor crate only bundles CPI and Accounts helpers for the System Program. If you want the same hand-holding for SPL tokens you pull in the `anchor_spl` crate. `anchor_spl` adds: * Helper builders for every instruction in both the SPL Token and Token-2022 programs * Type wrappers that make it painless to verify and deserialize Mint and Token accounts Let's look at how the `Mint` and `Token` accounts are structured: rust #[account(\ mint::decimals = ,\ mint::authority = ,\ mint::freeze_authority = \ mint::token_program = \ )] pub mint: Account<'info, Mint>, #[account(\ mut,\ associated_token::mint = ,\ associated_token::authority = ,\ associated_token::token_program = \ )] pub maker_ata_a: Account<'info, TokenAccount>, `Account<'info, Mint>` and `Account<'info, TokenAccount>` tell Anchor to: * confirm the account really is a Mint or Token account * deserialize its data so you can read fields directly * enforce any extra constraints you specify (`authority`, `decimals`, `mint`, `token_program`, etc.) These token-related accounts follow the same `init` pattern used earlier. Since Anchor knows their fixed byte size, we don't need to specify a `space` value, only the payer funding the account. Anchor also offers `init_if_needed` macro: it checks whether the token account already exists and, if not, creates it. That shortcut isn't safe for every account type, but it's perfectly suited to token accounts, so we'll rely on it here. As mentioned, `anchor_spl` creates helpers for both the **Token** and **Token2022** programs, with the latter introducing Token Extensions. The main challenge is that even though these accounts achieve similar goals and have comparable structures, they can't be deserialized and checked the same way since they're owned by two different programs. We could create more "advanced" logic to handle these different account types, but fortunately Anchor supports this scenario through **InterfaceAccounts**: rust use anchor_spl::token_interface::{Mint, TokenAccount}; #[account(\ mint::decimals = ,\ mint::authority = ,\ mint::freeze_authority = \ )] pub mint: InterfaceAccounts<'info, Mint>, #[account(\ mut,\ associated_token::mint = ,\ associated_token::authority = ,\ associated_token::token_program = \ )] pub maker_ata_a: InterfaceAccounts<'info, TokenAccount>, The key difference here is that we're using `InterfaceAccounts` instead of `Account`. This allows our program to work with both Token and Token2022 accounts without needing to handle the differences in their deserialization logic. The interface provides a common way to interact with both types of accounts while maintaining type safety and proper validation. This approach is particularly useful when you want your program to be compatible with both token standards, as it eliminates the need to write separate logic for each program. The interface handles all the complexity of dealing with different account structures behind the scenes. If you want to learn more about how to use `anchor-spl` you can follow the [SPL-Token Program with Anchor](https://learn.blueshift.gg/en/courses/spl-token-with-anchor) or [Token2022 Program with Anchor](https://learn.blueshift.gg/en/courses/token-2022-with-anchor) courses. Additional Accounts Type -------------------------- Naturally, System Accounts, Program Accounts and Token Accounts are not the only types of account that we can have in anchor. So we're going to see here other types of Account that we can have: ### Signer The `Signer` type is used when you need to verify that an account has signed a transaction. This is crucial for security as it ensures that only authorized accounts can perform certain actions. You'll use this type whenever you need to guarantee that a specific account has approved a transaction, such as when transferring funds or modifying account data that requires explicit permission. Here's how you can use it: rust #[derive(Accounts)] pub struct InstructionAccounts<'info> { #[account(mut)] pub signer: Signer<'info>, } The `Signer` type automatically checks if the account has signed the transaction. If it hasn't, the transaction will fail. This is particularly useful when you need to ensure that only specific accounts can perform certain operations. ### AccountInfo & UncheckedAccount `AccountInfo` and `UncheckedAccount` are low-level account types that provide direct access to account data without automatic validation. They are identical in functionality, but `UncheckedAccount` is the preferred choice as its name better reflects its purpose. These types are useful in three main scenarios: 1. Working with accounts that lack a defined structure 2. Implementing custom validation logic 3. Interacting with accounts from other programs that don't have Anchor type definitions Since these types bypass Anchor's safety checks, they are inherently unsafe and require explicit acknowledgment using the `/// CHECK` comment. This comment serves as documentation that you understand the risks and have implemented appropriate validation. Here's an example of how to use them: rust #[derive(Accounts)] pub struct InstructionAccounts<'info> { /// CHECK: This is an unchecked account pub account: UncheckedAccount<'info>, /// CHECK: This is an unchecked account pub account_info: AccountInfo<'info>, } ### Option The `Option` type in Anchor allows you to make accounts optional in your instruction. When an account is wrapped in `Option`, it can either be provided or omitted in the transaction. This is particularly useful for: * Building flexible instructions that can work with or without certain accounts * Implementing optional parameters that may not always be needed * Creating backward-compatible instructions that can work with new or old account structures When an `Option` account is set to `None`, Anchor will use the Program ID as the account address. This behavior is important to understand when working with optional accounts. Here's how to implement it: rust #[derive(Accounts)] pub struct InstructionAccounts<'info> { pub optional_account: Option>, } ### Box The `Box` type is used to store accounts on the heap rather than the stack. This is necessary in several scenarios: * When dealing with large account structures that would be inefficient to store on the stack * When working with recursive data structures * When you need to work with accounts that have a size that can't be determined at compile time Using `Box` helps manage memory more efficiently in these cases by allocating the account data on the heap. Here's an example: rust #[derive(Accounts)] pub struct InstructionAccounts<'info> { pub boxed_account: Box>, } ### Program The `Program` type is used to validate and interact with other Solana programs. Anchor can easily identify program accounts because they have their `executable` flag set to `true`. This type is particularly useful when: * You need to make Cross-Program Invocations (CPIs) * You want to ensure you're interacting with the correct program * You need to verify program ownership of accounts There are two main ways to use the `Program` type: 1. Using built-in program types (recommended when available): rust use anchor_spl::token::Token; #[derive(Accounts)] pub struct InstructionAccounts<'info> { pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, } 2. Using a custom program address when the program type isn't available: rust // Address of the Program const PROGRAM_ADDRESS: Pubkey = pubkey!("22222222222222222222222222222222222222222222") #[derive(Accounts)] pub struct InstructionAccounts<'info> { #[account(address = PROGRAM_ADDRESS)] /// CHECK: this is fine since we're checking the address pub program: UncheckedAccount<'info>, } **Note**: When working with token programs, you might need to support both the Legacy Token Program and Token-2022 Program. In such cases, use the `Interface` type instead of `Program`: rust use anchor_spl::token_interface::TokenInterface; #[derive(Accounts)] pub struct InstructionAccounts<'info> { pub program: Interface<'info, TokenInterface>, } Custom Account Validation --------------------------- Anchor provides a powerful set of constraints that can be applied directly in the `#[account]` attribute. These constraints help ensure account validity and enforce program rules at the account level, before your instruction logic runs. Here are the available constraints: ### Address Constraint The `address` constraint verifies that an account's public key matches a specific value. This is essential when you need to ensure you're interacting with a known account, such as a specific PDA or a program account: rust #[account(\ address = , // Basic usage\ address = @ CustomError // With custom error\ )] pub account: Account<'info, CustomAccountType>, ### Owner Constraint The `owner` constraint ensures that an account is owned by a specific program. This is a critical security check when working with program-owned accounts, as it prevents unauthorized access to accounts that should be managed by a particular program: rust #[account(\ owner = , // Basic usage\ owner = @ CustomError // With custom error\ )] pub account: Account<'info, CustomAccountType>, ### Executable Constraint The `executable` constraint verifies that an account is a program account (has its `executable` flag set to `true`). This is particularly useful when making Cross-Program Invocations (CPIs) to ensure you're interacting with a program rather than a data account: rust #[account(executable)] pub account: Account<'info, CustomAccountType>, ### Mutable Constraint The `mut` constraint marks an account as mutable, allowing its data to be modified during the instruction. This is required for any account that will be updated, as Anchor enforces immutability by default for safety: rust #[account(\ mut, // Basic usage\ mut @ CustomError // With custom error\ )] pub account: Account<'info, CustomAccountType>, ### Signer Constraint The `signer` constraint verifies that an account has signed the transaction. This is crucial for security when an account needs to authorize an action, such as transferring funds or modifying data. It's a more explicit way to require signatures compared to using the `Signer` type: rust #[account(\ signer, // Basic usage\ signer @ CustomError // With custom error\ )] pub account: Account<'info, CustomAccountType>, ### Has One Constraint The `has_one` constraint verifies that a specific field on the account struct matches another account's public key. This is useful for maintaining relationships between accounts, such as ensuring a token account belongs to the correct owner: rust #[account(\ has_one = data @ Error::InvalidField\ )] pub account: Account<'info, CustomAccountType>, ### Custom Constraint When the built-in constraints don't meet your needs, you can write a custom validation expression. This allows for complex validation logic that can't be expressed with other constraints, such as checking account data length or validating relationships between multiple fields: rust #[account(\ constraint = data == account.data @ Error::InvalidField\ )] pub account: Account<'info, CustomAccountType>, These constraints can be combined to create powerful validation rules for your accounts. By placing validation at the account level, you keep your security checks close to the account definitions and avoid scattering `require!()` calls throughout your instruction logic. Remaining Accounts -------------------- Sometimes when writing programs, the fixed structure of instruction accounts doesn't provide the flexibility your program needs. Remaining accounts solve this by allowing you to pass additional accounts beyond the defined instruction structure, enabling dynamic behavior based on runtime conditions. ### Implementation Guideline Traditional instruction definitions require you to specify exactly which accounts will be used: rust #[derive(Accounts)] pub struct Transfer<'info> { pub from: Account<'info, TokenAccount>, pub to: Account<'info, TokenAccount>, pub authority: Signer<'info>, } This works great for single operations, but what if you want to perform multiple token transfers in one instruction? You'd need to call the instruction multiple times, increasing transaction costs and complexity. Remaining accounts let you pass additional accounts that aren't part of the fixed instruction structure meaning that your program could iterate through these accounts and apply repeating logic dynamically. Instead of requiring separate instructions for each transfer, you can design one instruction that handles "N" transfers: rust #[derive(Accounts)] pub struct BatchTransfer<'info> { pub from: Account<'info, TokenAccount>, pub to: Account<'info, TokenAccount>, pub authority: Signer<'info>, } pub fn batch_transfer(ctx: Context, amounts: Vec) -> Result<()> { // Handle the first transfer using fixed accounts transfer_tokens(&ctx.accounts.from, &ctx.accounts.to, amounts[0])?; let remaining_accounts = &ctx.remaining_accounts; // CRITICAL: Validate remaining accounts schema // For batch transfers, we expect pairs of accounts require!( remaining_accounts.len() % 2 == 0, TransferError::InvalidRemainingAccountsSchema ); // Process remaining accounts in groups of 2 (from_account, to_account) for (i, chunk) in remaining_accounts.chunks(2).enumerate() { let from_account = &chunk[0]; let to_account = &chunk[1]; let amount = amounts[i + 1]; // Apply the same transfer logic to remaining accounts transfer_tokens(from_account, to_account, amount)?; } Ok(()) } Batching instructions means: * Smaller instruction size: the repeating accounts and data doesn't need to be included * Efficiency: each CPI costs 1000 CU, meaning that somebody that uses your program could invoke it only one time instead of 3 if they need to do batch instructions > Remaining accounts are passed as `UncheckedAccount`, meaning Anchor doesn't perform any validation on them. Always validate the `RemainingAccountSchema` and the underlying account. ### Client Side Implementation We can easily pass remaining accounts using the Anchor SDK generated once we do `anchor build`. Since those are "raw" accounts we'll need to specify if they need to be passed as signer and/or mutable like this: ts await program.methods.someMethod().accounts({ // some accounts }) .remainingAccounts([\ {\ isSigner: false,\ isWritable: true,\ pubkey: new Pubkey().default\ }\ ]) .rpc(); [Next LessonAnchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/anchor-accounts/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Anchor Accounts --- # Blueshift | Anchor for Dummies | Program Deployment [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Program Deployment lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Program Deployment ================== Once your program is complete, deploy it to devnet or mainnet to enable user interaction. Program Deployment -------------------- Build your program to generate the necessary deployment files: anchor build This creates a `target/deploy` folder containing: * `.so`: your program's bytecode * `-keypair.json`: a generated keypair for deployment > You can replace this keypair with a vanity address by substituting the keypair in the `-keypair.json` file. Retrieve the program address: solana address -k target/deploy/-keypair.json Update the `declare_id!()` function in `lib.rs` with this address, then configure your `Anchor.toml` with the right target cluster and program ID: [provider] cluster = "devnet" [programs.devnet] = "" Deploy your program: anchor deploy This deploys using the specified address, cluster, and wallet from `Anchor.toml` as the fee payer. ### Deployment Failures Failed deployments create intermediate buffer accounts that hold lamports. You'll see recovery instructions like: ================================================================================== Recover the intermediate account's ephemeral keypair file with `solana-keygen recover` and the following 12-word seed phrase: ================================================================================== valley flat great hockey share token excess clever benefit traffic avocado athlete ================================================================================== To resume a deploy, pass the recovered keypair as the [BUFFER_SIGNER] to `solana program deploy` or `solana program write-buffer'. Or to recover the account's lamports, pass it as the [BUFFER_ACCOUNT_ADDRESS] argument to `solana program drain`. ================================================================================== To recover your balance: * **Resume deployment** by recovering the keypair: solana-keygen recover -o Enter the 12-word seed phrase when prompted, then deploy with the buffer: solana program deploy ./target/deploy/.so --program-id ./target/deploy/-keypair.json --buffer ./target/deploy/-keypair.json * **Close** the buffer to reclaim lamports: solana program close
### Deployment through Solana CLI You can deploy your programs directly using Solana CLI instead of Anchor: solana program deploy ./target/deploy/.so --program-id ./target/deploy/-keypair.json During network congestion, use these flags to improve deployment success: * `--with-compute-unit-price`: Set compute unit price in micro-lamports * `--use-rpc`: Send transactions to RPC instead of validator TPUs * `--max-sign-attempts`: Maximum retry attempts after blockhash expiration Program Upgrade ----------------- By default, `anchor deploy` creates a new program ID. To upgrade an existing program while preserving its address and associated accounts: anchor upgrade target/deploy/.so --program-id If the new executable is larger than the deployed version, extend the program account first: solana program extend ./target/deploy/.so ### Upgrade through Solana CLI The process remains the same: extend if needed, then deploy: solana program deploy ./target/deploy/.so --program-id ./target/deploy/-keypair.json ### Making Programs Immutable You can remove the upgrade authority to make your program immutable: solana program set-upgrade-authority --final > This action is irreversible. Once set, the program cannot be updated. ### Migrating Programs Migration transfers a program from one address to another. The CLI closes the old program and redeploys to the new location: solana program migrate ./target/deploy/.json > Migration breaks all existing PDAs since they derive from the old Program ID. Users must update their applications to use the new address and change authority over any token account which authority is a PDA. Uploading an IDL ------------------ An Interface Description Language (IDL) file provides a standardized JSON description of your program's instructions and accounts, enabling easier client integration. Upload the IDL onchain to help developers integrate your program: anchor idl init --filepath target/idl/.json ### Upgrading the IDL After redeploying your program, update the onchain IDL: anchor idl upgrade --filepath target/idl/.json Verified Builds ----------------- Verified builds ensure that the executable program deployed on Solana matches the source code in your repository. This verification enables developers and users to confirm the onchain program corresponds exactly to the public codebase. The verification process compares the hash of the onchain program against the locally built program from source code, detecting any discrepancies between versions. Building programs with the Solana CLI can embed machine-specific code into binaries. Compiling the same program on different machines may produce different executables. To address this, build inside a Docker container with pinned dependencies for reproducible results. Anchor provides CLI commands that handle building and Docker configuration: anchor build --verifiable Verify a build against a program deployed on mainnet: anchor verify -p > The `` corresponds to the name defined in your program's `Cargo.toml` file. [Next LessonClient Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/program-deployment/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) --- # Blueshift | Anchor for Dummies | Conclusion [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Conclusion lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Conclusion ========== Congratulations! You've completed the Introduction to Anchor course. You now have a solid foundation in understanding how Anchor works, from its core concepts to practical implementation details. What You've Learned ------------------- Throughout this course, you've gained essential knowledge about: * The fundamentals of Anchor framework * How to structure Anchor programs * Understanding program discriminators * Best practices for building secure Solana programs Next Steps ---------- You're now ready to start building your first program with Anchor! The best way to solidify your knowledge is through hands-on practice. We encourage you to: 1. Head to the [Challenges section](https://learn.blueshift.gg/en/challenges) 2. Start with the beginner-friendly exercises 3. Build and test your first Anchor program 4. Join our community to share your progress and get help Remember, every great developer started with their first program. Don't be afraid to experiment and make mistakes; that's how we learn and grow! Congrats, you've finished this course![View Other CoursesView Other Courses](https://learn.blueshift.gg/en/courses) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/conclusion/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Conclusion --- # Blueshift | Anchor for Dummies | Anchor Instructions [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Anchor Instructions lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Instructions & CPIs =================== Instructions are the building blocks of Solana programs, defining the actions that can be performed. In Anchor, instructions are implemented as functions with specific attributes and constraints. Let's explore how to work with them effectively. Instruction Structure ----------------------- In Anchor, instructions are defined using the `#[program]` module and individual instruction functions. Here's the basic structure: rust use anchor_lang::prelude::*; #[program] pub mod my_program { use super::*; pub fn initialize(ctx: Context, data: u64) -> Result<()> { // Instruction logic here Ok(()) } } ### Instruction Context Every instruction function receives a `Context` struct as its first parameter. This context contains: * `accounts`: The accounts passed to the instruction * `program_id`: The program's public key * `remaining_accounts`: Any additional accounts not explicitly defined in the context struct * `bumps`: The `bumps` field is particularly useful when working with PDAs, as it provides the bump seeds that were used to derive the PDA addresses (only if you're deriving them in the account struct) That can be accessed by doing: rust // Accessing accounts ctx.accounts.account_1 ctx.accounts.account_2 // Accessing program ID ctx.program_id // Accessing remaining accounts for remaining_account in ctx.remaining_accounts { // Process remaining account } // Accessing bumps for PDAs let bump = ctx.bumps.pda_account; ### Instruction Discriminator Like accounts, instructions in Anchor use discriminators to identify different instruction types. The default discriminator is an 8-byte prefix generated using `sha256("global:")[0..8]`. The instruction name should be in snake\_case. Anchor Discriminator Calculator Account/Instruction Name Instruction sha256("global:" + snake\_case(seed))\[0..8\] \[0, 0, 0, 0, 0, 0, 0, 0\] ### Custom Instruction Discriminator You can also specify a custom discriminator for your instructions: rust #[instruction(discriminator = 1)] pub fn custom_discriminator(ctx: Context) -> Result<()> { // Instruction logic Ok(()) } Instruction Scaffold ---------------------- You can write your instruction in different ways, in this section we're going to teach some of the styles and ways you can actually set them up ### Instruction Logic Instruction logic can be organized in different ways, depending on your program's complexity and your preferred coding style. Here are the main approaches: 1. **Inline Instruction Logic** For simple instructions, you can write the logic directly in the instruction function: rust pub fn initialize(ctx: Context, amount: u64) -> Result<()> { // Transfer tokens logic // Close token account logic Ok(()) } 2. **Separate Module Implementation** For very complex programs, you can organize the logic in separate modules: rust // In a separate file: transfer.rs pub fn execute(ctx: Context, amount: u64) -> Result<()> { // Transfer tokens logic // Close token account logic Ok(()) } // In your lib.rs pub fn transfer(ctx: Context, amount: u64) -> Result<()> { transfer::execute(ctx, amount) } 3. **Separate Context Implementation** For more complex instructions, you can move the logic to the context struct's implementation: rust // In a separate file: transfer.rs pub fn execute(ctx: Context, amount: u64) -> Result<()> { ctx.accounts.transfer_tokens(amount)?; ctx.accounts.close_token_account()?; Ok(()) } impl<'info> Transfer<'info> { /// Transfers tokens from source to destination account pub fn transfer_tokens(&mut self, amount: u64) -> Result<()> { // Transfer tokens logic Ok(()) } /// Closes the source token account after transfer pub fn close_token_account(&mut self) -> Result<()> { // Close token account logic } } // In your lib.rs pub fn transfer(ctx: Context, amount: u64) -> Result<()> { transfer::execute(ctx, amount) } ### Instruction Parameters Instructions can accept parameters beyond the context. These parameters are serialized and deserialized automatically by Anchor. Here are the key points about instruction parameters: 1. **Basic Types** Anchor supports all Rust primitive types and common Solana types: rust pub fn complex_instruction( ctx: Context, amount: u64, pubkey: Pubkey, vec_data: Vec, ) -> Result<()> { // Instruction logic Ok(()) } 2. **Custom Types** You can use custom types as parameters, but they must implement `AnchorSerialize` and `AnchorDeserialize`: rust #[derive(AnchorSerialize, AnchorDeserialize)] pub struct InstructionData { pub field1: u64, pub field2: String, } pub fn custom_type_instruction( ctx: Context, data: InstructionData, ) -> Result<()> { // Instruction logic Ok(()) } ### Best Practices 1. **Keep Instructions Focused**: Each instruction should do one thing well. If an instruction is doing too much, consider splitting it into multiple instructions. 2. **Use Context Implementation**: For complex instructions, use the context implementation approach to: * Keep your code organized * Make it easier to test * Improve reusability * Add proper documentation 3. **Error Handling**: Always use proper error handling and return meaningful error messages: rust #[error_code] pub enum TransferError { #[msg("Insufficient balance")] InsufficientBalance, #[msg("Invalid amount")] InvalidAmount, } impl<'info> Transfer<'info> { pub fn transfer_tokens(&mut self, amount: u64) -> Result<()> { require!(amount > 0, TransferError::InvalidAmount); require!( self.source.amount >= amount, TransferError::InsufficientBalance ); // Transfer logic Ok(()) } } 4. **Documentation**: Always document your instruction logic, especially when using context implementation: rust impl<'info> Transfer<'info> { /// # Transfers tokens /// /// Transfers tokens from source to destination account pub fn transfer_tokens(&mut self, amount: u64) -> Result<()> { // Implementation Ok(()) } } Cross-Program Invocations (CPIs) ---------------------------------- Cross Program Invocations (CPI) refer to the process of one program invoking instructions of another program, which enables the composability of Solana programs. Anchor provides a convenient way to make CPIs through the `CpiContext` and program-specific builders. **Note**: You can find all the System Program CPI by using the main anchor crate and doing: `use anchor_lang::system_program::*`; and for the one relative to the SPL token program we'll need to import the anchor\_spl crate and do: `use anchor_spl::token::*` ### Basic CPI Structure Here's how to make a basic CPI: rust use anchor_lang::solana_program::program::invoke_signed; use anchor_lang::system_program::{transfer, Transfer}; pub fn transfer_lamport(ctx: Context, amount: u64) -> Result<()> { let cpi_accounts = Transfer { from: ctx.accounts.from.to_account_info(), to: ctx.accounts.to.to_account_info(), }; let cpi_program = ctx.accounts.system_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); transfer(cpi_ctx, amount)?; Ok(()) } ### CPI with PDA Signers When making CPIs that require PDA signatures, use `CpiContext::new_with_signer`: rust use anchor_lang::solana_program::program::invoke_signed; use anchor_lang::system_program::{transfer, Transfer}; pub fn transfer_lamport_with_pda(ctx: Context, amount: u64) -> Result<()> { let seeds = &[\ b"vault".as_ref(),\ &[ctx.bumps.vault],\ ]; let signer = &[&seeds[..]]; let cpi_accounts = Transfer { from: ctx.accounts.vault.to_account_info(), to: ctx.accounts.recipient.to_account_info(), }; let cpi_program = ctx.accounts.system_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); transfer(cpi_ctx, amount)?; Ok(()) } Error Handling ---------------- Anchor provides a robust error handling system for instructions. Here's how to implement custom errors and handle them in your instructions: rust #[error_code] pub enum MyError { #[msg("Custom error message")] CustomError, #[msg("Another error with value: {0}")] ValueError(u64), } pub fn handle_errors(ctx: Context, value: u64) -> Result<()> { require!(value > 0, MyError::CustomError); require!(value < 100, MyError::ValueError(value)); Ok(()) } [Next LessonTesting your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/anchor-instructions/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Anchor Instructions --- # Blueshift | Anchor for Dummies | Client Side Development [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Client Side Development lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Client Side Development ======================= Most dApps use TypeScript to interact with deployed Solana programs. Understanding how to integrate your program client-side is essential for building functional applications. Anchor Client SDK ------------------- Anchor simplifies client interaction with Solana programs through an Interface Description Language (IDL) file that mirrors your program's structure. When combined with Anchor's TypeScript library (@coral-xyz/anchor), the IDL provides a streamlined approach to building instructions and transactions. ### Setup The `@coral-xyz/anchor` package installs automatically when creating an Anchor program. After running anchor build, Anchor generates: * An IDL at `target/idl/.json` * A TypeScript SDK at `target/types/.ts` These files abstract away much of the underlying complexity. Transfer them to your TypeScript client using this structure: src ├── anchor │ ├── .json │ └── .ts └── integration.ts > The `integration.ts` file contains program interaction logic. The `.json` file is the IDL, and `.ts` contains generated TypeScript types. To use the wallet adapter with Anchor's TypeScript SDK, create a Provider object that combines the Connection (localhost, devnet, or mainnet) and the Wallet (the address that pays for and signs transactions). Set up the Wallet and Connection: ts import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react"; const { connection } = useConnection(); const wallet = useAnchorWallet(); > The `useWallet` hook from `@solana/wallet-adapter-react` is incompatible with the Wallet object that Anchor's Provider expects. This is why we're using the `useAnchorWallet` hook. Create the Provider object and set it as the default: import { AnchorProvider, setProvider } from "@coral-xyz/anchor"; const provider = new AnchorProvider(connection, wallet, { commitment: "confirmed", }); setProvider(provider); ### Program Anchor's Program object creates a custom API for interacting with Solana programs. This API serves as the central interface for all onchain program communication: * Send transactions, * Fetch deserialized accounts, * Decode instruction data, * Subscribe to account changes, * Listen to events Create the Program object by importing the types and IDL: ts import from "./.json"; import type { } from "./.ts"; import { Program, Idl } from "@coral-xyz/anchor"; const program = new Program( as ); If you haven't set a default provider, specify it explicitly: ts const program = new Program( as , provider); Once configured, use the Anchor Methods Builder to construct instructions and transactions. The `MethodsBuilder` uses the IDL to provide a streamlined format for building transactions that invoke program instructions. The basic `MethodsBuilder` pattern: ts await program.methods .instructionName(instructionDataInputs) .accounts({}) .signers([]) .rpc(); > The API uses camelCase naming instead of Rust's snake\_case convention. Call instructions using dot syntax with the instruction name, passing arguments as comma-separated values. Pass additional signers beyond the provider using `.signers()`. ### Accounts Use dot syntax to call `.accounts` on the `MethodsBuilder`, passing an object with each account the instruction expects based on the IDL. > From Anchor 0.30.0, accounts that can be resolved automatically (like PDAs or explicit addresses) are included in the IDL and aren't required in the `.accounts` call (`.accountPartial()` becomes the default). To pass all accounts manually, use `.accountsStrict()`. ### Transactions The default method for sending transactions through Anchor is `.rpc()`, which sends the transaction directly to the blockchain. For scenarios requiring backend signing (like creating a transaction on the frontend with the user's wallet, then securely signing with a backend keypair), use `.transaction()`: ts const transaction = await program.methods .instructionName(instructionDataInputs) .accounts({}) .transaction(); //... Sign the transaction in the backend // Send the transaction to the chain await sendTransaction(transaction, connection); To bundle multiple Anchor instructions, use `.instruction()` to get instruction objects: ts // Create first instruction const instructionOne = await program.methods .instructionOneName(instructionOneDataInputs) .accounts({}) .instruction(); // Create second instruction const instructionTwo = await program.methods .instructionTwoName(instructionTwoDataInputs) .accounts({}) .instruction(); // Add both instructions to one transaction const transaction = new Transaction().add(instructionOne, instructionTwo); // Send transaction await sendTransaction(transaction, connection); Fetch and Filter Accounts --------------------------- When your program creates hundreds of accounts, tracking them becomes challenging. The Program object provides methods to efficiently fetch and filter program accounts. Fetch all addresses of a specific account type: ts const accounts = await program.account.counter.all(); Filter specific accounts using the `memcmp` flag: ts const accounts = await program.account.counter.all([\ {\ memcmp: {\ offset: 8,\ bytes: bs58.encode(new BN(0, "le").toArray()),\ },\ },\ ]); > This fetches all `Counter` accounts where the first field equals 0. For checking if account data changed, fetch deserialized account data for a specific account using fetch: ts const account = await program.account.counter.fetch(ACCOUNT_ADDRESS); Fetch multiple accounts simultaneously: ts const accounts = await program.account.counter.fetchMultiple([\ ACCOUNT_ADDRESS_ONE,\ ACCOUNT_ADDRESS_TWO,\ ]); Events and Webhooks --------------------- Rather than fetching onchain data every time users connect their wallets, set up systems that listen to the blockchain and store relevant data in a database. Two main approaches exist for listening to onchain events: * Polling: The client repeatedly checks for new data at intervals. The server responds with the latest data regardless of changes, potentially returning duplicate information. * Streaming: The server pushes data to the client only when updates occur. This provides more efficient, real-time data transfer since only relevant changes are transmitted. For streaming Anchor instructions, use webhooks that listen to events and send them to your server when they occur. For example, update a database entry whenever an NFT sale happens on your marketplace. > For extremely low-latency applications where 5ms differences matter, webhooks may not provide sufficient speed. Anchor provides two macros for emitting events: * `emit!()`: Emits events directly to program logs using the `sol_log_data()` syscall, encoding event data as base64 strings prefixed with "Program Data" * `emit_cpi!()`: Emits events through Cross Program Invocations (CPIs). Event data is encoded and included in the CPI's instruction data instead of program logs ### `emit!()` macro Program implementation: rust use anchor_lang::prelude::*; declare_id!("8T7MsCZyzxboviPJg5Rc7d8iqEcDReYR2pkQKrmbg7dy"); #[program] pub mod event { use super::*; pub fn emit_event(_ctx: Context, input: String) -> Result<()> { emit!(CustomEvent { message: input }); Ok(()) } } #[derive(Accounts)] pub struct EmitEvent {} #[event] pub struct CustomEvent { pub message: String, } Client-side event listening with Anchor SDK helpers for base64 decoding: ts import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; import { Event } from "../target/types/event"; describe("event", () => { // Configure the client to use the local cluster. anchor.setProvider(anchor.AnchorProvider.env()); const program = anchor.workspace.Event as Program; it("Emits custom event", async () => { // Set up listener before sending transaction const listenerId = program.addEventListener("customEvent", event => { // Process the event data console.log("Event Data:", event); }); }); }); ### `emit_cpi!()` macro Program implementation: rust use anchor_lang::prelude::*; declare_id!("2cDQ2LxKwQ8fnFUz4LLrZ157QzBnhPNeQrTSmWcpVin1"); #[program] pub mod event_cpi { use super::*; pub fn emit_event(ctx: Context, input: String) -> Result<()> { emit_cpi!(CustomEvent { message: input }); Ok(()) } } #[event_cpi] #[derive(Accounts)] pub struct EmitEvent {} #[event] pub struct CustomEvent { pub message: String, } Client-side event decoding: ts import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; import { EventCpi } from "../target/types/event_cpi"; describe("event-cpi", () => { // Configure the client to use the local cluster. anchor.setProvider(anchor.AnchorProvider.env()); const program = anchor.workspace.EventCpi as Program; it("Emits custom event", async () => { // Fetch the transaction data const transactionData = await program.provider.connection.getTransaction( transactionSignature, { commitment: "confirmed" }, ); // Decode the event data from the CPI instruction data const eventIx = transactionData.meta.innerInstructions[0].instructions[0]; const rawData = anchor.utils.bytes.bs58.decode(eventIx.data); const base64Data = anchor.utils.bytes.base64.encode(rawData.subarray(8)); const event = program.coder.events.decode(base64Data); console.log(event); }); }); [Next LessonAdvanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/client-side-development/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) Blueshift | Anchor for Dummies | Client Side Development --- # Blueshift | Anchor for Dummies | Advanced Anchor [](https://learn.blueshift.gg/en) [](https://learn.blueshift.gg/en) [Courses](https://learn.blueshift.gg/en) [Challenges](https://learn.blueshift.gg/en/challenges) Connect WalletConnect Wallet Anchor Anchor for Dummies Anchor for Dummies ================== Advanced Anchor lessons [Anchor 101](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-101) [Anchor Accounts](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-accounts) [Anchor Instructions](https://learn.blueshift.gg/en/courses/anchor-for-dummies/anchor-instructions) [Testing your Program](https://learn.blueshift.gg/en/courses/anchor-for-dummies/testing-your-program) [Program Deployment](https://learn.blueshift.gg/en/courses/anchor-for-dummies/program-deployment) [Client Side Development](https://learn.blueshift.gg/en/courses/anchor-for-dummies/client-side-development) [Advanced Anchor](https://learn.blueshift.gg/en/courses/anchor-for-dummies/advanced-anchor) [Conclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Advanced Anchor =============== Sometimes, the abstraction done with Anchor makes it impossible to build out the logic that our program needs. For this reason, in this section we're going to talk about how to use some advanced concept to work with out program. Feature Flags --------------- Software engineers routinely need distinct environments for local development, testing, and production. Feature flags provide an elegant solution by enabling conditional compilation and environment-specific configurations without maintaining separate codebases. ### Cargo Features Cargo features offer a powerful mechanism for conditional compilation and optional dependencies. You define named features in your `Cargo.toml`'s `[features]` table, then enable or disable them as needed: * Enable features via command line: --features feature\_name * Enable features for dependencies directly in Cargo.toml This gives you fine-grained control over what gets included in your final binary. ### Feature Flags in Anchor Anchor programs commonly use feature flags to create different behaviors, constraints, or configurations based on the target environment. Use the `cfg` attribute to conditionally compile code: rust #[cfg(feature = "testing")] fn function_for_testing() { // Compiled only when "testing" feature is enabled } #[cfg(not(feature = "testing"))] fn function_for_production() { // Compiled only when "testing" feature is disabled } Feature flags excel at handling environment differences. Since not all tokens deploy across Mainnet and Devnet, you often need different addresses for different networks: rust #[cfg(feature = "localnet")] pub const TOKEN_ADDRESS: &str = "Local_Token_Address_Here"; #[cfg(not(feature = "localnet"))] pub const TOKEN_ADDRESS: &str = "Mainnet_Token_Address_Here"; This approach eliminates deployment configuration errors and streamlines your development workflow by switching environments at compile time rather than runtime. This is how you would set it up in your `Cargo.toml` file: toml [features] default = ["localnet"] localnet = [] After that, you can specify the flag you want to build your program with like this: # Uses default (localnet) anchor build # Build for mainnet anchor build --no-default-features > Once you build the program, the binary will just have the conditional flag you enabled meaning that once you test and deploy it will respect that condition. Working with Raw Accounts --------------------------- Anchor streamlines account handling by automatically deserializing accounts through the account context: rust #[derive(Accounts)] pub struct Instruction<'info> { pub account: Account<'info, MyAccount>, } #[account] pub struct MyAccount { pub data: u8, } However, this automatic deserialization becomes problematic when you need conditional account processing, such as deserializing and modifying an account only when specific criteria are met. For conditional scenarios, use `UncheckedAccount` to defer validation and deserialization until runtime. This prevents hard errors when accounts might not exist or when you need to validate them programmatically: rust #[derive(Accounts)] pub struct Instruction<'info> { /// CHECK: Validated conditionally in instruction logic pub account: UncheckedAccount<'info>, } #[account] pub struct MyAccount { pub data: u8, } pub fn instruction(ctx: Context, should_process: bool) -> Result<()> { if should_process { // Deserialize the account data let mut account = MyAccount::try_deserialize(&mut &ctx.accounts.account.to_account_info().data.borrow_mut()[..]) .map_err(|_| error!(InstructionError::AccountNotFound))?; // Modify the account data account.data += 1; // Serialize back the data to the account account.try_serialize(&mut &mut ctx.accounts.account.to_account_info().data.borrow_mut()[..])?; } Ok(()) } Zero Copy Accounts -------------------- Solana's runtime enforces strict memory limits: 4KB for stack memory and 32KB for heap memory. Additionally, the stack grows by 10KB for each loaded account. These constraints make traditional deserialization impossible for large accounts, requiring zero-copy techniques for efficient memory management. When accounts exceed these limits, you'll encounter stack overflow errors like: `Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes` For medium-sized accounts, you can use `Box` to move data from stack to heap (as mentioned in the introduction), but larger accounts require implementing `zero_copy`. Zero-copy bypasses automatic deserialization entirely by using raw memory access. To define an account type that uses zero-copy, annotate the struct with `#[account(zero_copy)]`: rust #[account(zero_copy)] pub struct Data { // 10240 bytes - 8 bytes account discriminator pub data: [u8; 10_232], } The `#[account(zero_copy)]` attribute automatically implements several traits required for zero-copy deserialization: * `#[derive(Copy, Clone)]`, * `#[derive(bytemuck::Zeroable)]`, * `#[derive(bytemuck::Pod)]`, and * `#[repr(C)]` > Note: To use zero-copy, add the `bytemuck` crate to your dependencies with the `min_const_generics` feature to work with arrays of any size in your zero-copy types. To deserialize a zero-copy account in your instruction context, use `AccountLoader<'info, T>`, where `T` is your zero-copy account type: rust #[derive(Accounts)] pub struct Instruction<'info> { pub data_account: AccountLoader<'info, Data>, } ### Initializing a Zero Copy Account There are two different approaches for initialization, depending on your account size: For accounts under 10,240 bytes, use the `init` constraint directly: rust #[derive(Accounts)] pub struct Initialize<'info> { #[account(\ init,\ // 10240 bytes is max space to allocate with init constraint\ space = 8 + 10_232,\ payer = payer,\ )] pub data_account: AccountLoader<'info, Data>, #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, } > The init constraint is limited to 10,240 bytes due to CPI limitations. Under the hood, init makes a CPI call to the System Program to create the account. For accounts requiring more than 10,240 bytes, you must first create the account separately by calling the System Program multiple times, adding 10,240 bytes per transaction. This allows you to create accounts up to Solana's maximum size of 10MB (10,485,760 bytes), bypassing the CPI limitation. After creating the account externally, use the `zero` constraint instead of `init`. The `zero` constraint verifies the account hasn't been initialized by checking that its discriminator is unset: rust #[account(zero_copy)] pub struct Data { // 10,485,780 bytes - 8 bytes account discriminator pub data: [u8; 10_485_752], } #[derive(Accounts)] pub struct Initialize<'info> { #[account(zero)] pub data_account: AccountLoader<'info, Data>, } For both initialization methods, call `load_init()` to get a mutable reference to the account data and set the account discriminator: rust pub fn initialize(ctx: Context) -> Result<()> { let account = &mut ctx.accounts.data_account.load_init()?; // Set your account data here // account.data = something; Ok(()) } ### Loading a Zero Copy Account Once initialized, use `load()` to read the account data: rust #[derive(Accounts)] pub struct ReadOnly<'info> { pub data_account: AccountLoader<'info, Data>, } pub fn read_only(ctx: Context) -> Result<()> { let account = &ctx.accounts.data_account.load()?; // Read your data here // let value = account.data; Ok(()) } Working with Raw CPIs ----------------------- Anchor abstracts cross-program invocation (CPI) complexity, but understanding the underlying mechanics is crucial for advanced Solana development. Every instruction consists of three core components: a `program_id`, an `accounts` array, and `instruction_data` bytes that the Solana Runtime processes via the `sol_invoke` syscall. At the system level, Solana executes CPIs through this syscall: rust /// Solana BPF syscall for invoking a signed instruction. fn sol_invoke_signed_c( instruction_addr: *const u8, account_infos_addr: *const u8, account_infos_len: u64, signers_seeds_addr: *const u8, signers_seeds_len: u64, ) -> u64; The Runtime receives pointers to your instruction data and account information, then executes the target program with these inputs. Here's how you'd invoke an Anchor program using raw Solana primitives: rust pub fn manual_cpi(ctx: Context) -> Result<()> { // Construct instruction discriminator (8-byte SHA256 hash prefix) let discriminator = sha256("global:instruction_name")[0..8] // Build complete instruction data let mut instruction_data = discriminator.to_vec(); instruction_data.extend_from_slice(&[additional_instruction_data]); // Your instruction parameters // Define account metadata for the target program let accounts = vec![\ AccountMeta::new(ctx.accounts.account_1.key(), true), // Signer + writable\ AccountMeta::new_readonly(ctx.accounts.account_2.key(), false), // Read-only\ AccountMeta::new(ctx.accounts.account_3.key(), false), // Writable\ ]; // Collect account infos for the syscall let account_infos = vec![\ ctx.accounts.account_1.to_account_info(),\ ctx.accounts.account_2.to_account_info(),\ ctx.accounts.account_3.to_account_info(),\ ]; // Create the instruction let instruction = solana_program::instruction::Instruction { program_id: target_program::ID, accounts, data: instruction_data, }; // Execute the CPI solana_program::program::invoke(&instruction, &account_infos)?; Ok(()) } // For PDA-signed CPIs, use invoke_signed instead: pub fn pda_signed_cpi(ctx: Context) -> Result<()> { // ... instruction construction same as above ... let signer_seeds = &[\ b"seed",\ &[bump],\ ]; solana_program::program::invoke_signed( &instruction, &account_infos, &[signer_seeds], )?; Ok(()) } ### CPI to another Anchor Program Anchor's `declare_program!()` macro enables type-safe cross-program invocations without adding the target program as a dependency. The macro generates Rust modules from a program's IDL, providing CPI helpers and account types for seamless program interactions. Place the target program's IDL file in an `/idls` directory anywhere in your project structure: project/ ├── idls/ │ └── target_program.json ├── programs/ │ └── your_program/ └── Cargo.toml Then use the macro to generate the necessary modules: use anchor_lang::prelude::*; declare_id!("YourProgramID"); // Generate modules from IDL declare_program!(target_program); // Import the generated types use target_program::{ accounts::Counter, // Account types cpi::{self, accounts::*}, // CPI functions and account structs program::TargetProgram, // Program type for validation }; #[program] pub mod your_program { use super::*; pub fn call_other_program(ctx: Context) -> Result<()> { // Create CPI context let cpi_ctx = CpiContext::new( ctx.accounts.target_program.to_account_info(), Initialize { payer: ctx.accounts.payer.to_account_info(), counter: ctx.accounts.counter.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), }, ); // Execute the CPI using generated helper target_program::cpi::initialize(cpi_ctx)?; Ok(()) } } #[derive(Accounts)] pub struct CallOtherProgram<'info> { #[account(mut)] pub payer: Signer<'info>, #[account(mut)] pub counter: Account<'info, Counter>, // Uses generated account type pub target_program: Program<'info, TargetProgram>, pub system_program: Program<'info, System>, } > You can CPI another Anchor program by adding in the your `Cargo.toml` under `[dependencies]`: `callee = { path = "../callee", features = ["cpi"] }` after building your program doing `anchor build -- --features cpi` and using `callee::cpi::()`. This is not recommended since it could give you a circular dependency error. \` [Next LessonConclusion](https://learn.blueshift.gg/en/courses/anchor-for-dummies/conclusion) Contents [View Source](https://github.com/blueshift-gg/blueshift-dashboard/tree/master/src/app/content/courses/anchor-for-dummies/advanced-anchor/en.mdx) Blueshift © 2025Commit: 86a8094 [](https://x.com/blueshift) [](https://github.com/blueshift-gg) [](https://discord.gg/blueshift) ---