Версії для прикладів у «Структура Anchor-проєкту: каталоги та файли» перевірено 2 серпня 2026 року. Стабільна гілка Anchor v1 має релізи 1.0.x і орієнтується на Solana 3.x; Anchor v2 у документації позначений як alpha. Приклади для Anchor 0.29–0.32 залишаються лише відтворюваними прикладами для зафіксованого legacy-середовища: їх не слід переносити в новий проєкт без міграції залежностей і повторного тестування.

Після виконання anchor init ви отримуєте готовий каркас проєкту. Кожен каталог і файл у ньому має чітке призначення. Розуміння цієї структури дозволяє впорядковано додавати модулі, писати тести та налаштовувати розгортання без хаотичного копіювання файлів. Нижче — детальний розбір кожного елемента з орієнтиром на Anchor 0.30.1, Rust 1.77.0 та кластер Devnet.

Каталог programs/

Це ядро проєкту: саме тут reside смарт-контракти (у термінології Solana — програми) мовою Rust. Кожна підпапка всередині programs/ — це окрема програма з власним Cargo.toml. У найпростішому випадку після ініціалізації ви побачите одну підпапку, назва якої збігається з іменем проєкту.

Організація модулів у lib.rs

Файл programs/<name>/src/lib.rs</name> — точка входу програми. Саме тут Anchor-макрос #[program] реєструє всі інструкції (functions), які будуть доступні зовні через RPC-виклики. Типова структура lib.rs для однофайлової програми виглядає так:

use anchor_lang::prelude::*;

declare_id!("YOUR_PROGRAM_ID_HERE");

#[program]
pub mod my_program {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize {}

Коли програма росте, утримувати все в одному файлі стає незручно. Anchor не нав'язує сувору архітектуру, але практика показує, що розбиття на модулі суттєво полегшує навігацію.

Розділення на файли: state.rs, instructions.rs, errors.rs

Поширений і зручний патерн — винести визначення стану, інструкції та помилки в окремі файли. Приклад структури src/:

  • state.rs — структури акаунтів (з макросом #[derive(Accounts)]) та серіалізовні дані (з #[account]). Це місце, де ви описуєте, які поля зберігатиме ваш акаунт і які обмеження на нього накладаються.
  • instructions.rs — логіка кожної інструкції. Файл імпортує структури з state.rs і містить функції всередині блоку #[program] або як окремі модулі.
  • errors.rs — кастомні коди помилок через #[error_code]. Відокремлення дозволяє централізовано керувати переліком помилок і уникати циклічних залежностей.
  • lib.rs — залишається точкою входу, яка збирає модулі разом.

Приклад errors.rs:

use anchor_lang::prelude::*;

#[error_code]
pub enum MyProgramError {
    #[msg("Недостатньо коштів для операції")]
    InsufficientFunds,
    #[msg("Невірний власник акаунта")]
    InvalidOwner,
}

Після створення файлів не забудьте додати mod state;, mod instructions;, mod errors; у lib.rs.

Каталог tests/

Каталог tests/ містить інтеграційні тести. Вони не виконуються всередині Rust-компілятора програми — це окремі TypeScript-файли, які запускаються через anchor test і взаємодіють з програмою через RPC, як це робив би реальний клієнт.

Інтеграційні тести на TypeScript

Типовий файл tests/my_program.ts починається з імпорту Anchor-клієнта та IDL (Interface Definition Language), який генерується автоматично під час збірки:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { MyProgram } from "../target/types/my_program";
import { assert } from "chai";

describe("my_program", () => {
  anchor.setProvider(anchor.AnchorProvider.env());
  const program = anchor.workspace.MyProgram as Program<MyProgram>;

  it("Ініціалізує програму", async () => {
    const tx = await program.methods.initialize().rpc();
    console.log("Транзакція:", tx);
  });
});

IDL зберігається у target/idl/ після виконання anchor build. Тести читають його звідти, тому перед запуском тестів обов'язково виконайте збірку.

Як тести взаємодіють з локальним validator

Команда anchor test виконує три кроки послідовно:

  1. Запускає локальний solana-test-validator у фоновому режимі.
  2. Розгортає вашу програму на цьому локальному валідаторі.
  3. Запускає Mocha-тести з TypeScript, підключаючись до локального RPC-ендпоінту (зазвичай http://127.0.0.1:8899).

Після завершення тестів валідатор зупиняється автоматично. Якщо вам потрібно залишити валідатор працюючим для ручної перевірки (наприклад, через Solana Explorer у локальному режимі), використовуйте anchor test --skip-build після попереднього ручного запуску валідатора.

Обмеження: локальний валідатор не відтворює повну поведінку мережі Devnet. Зокрема, таймінг слотів, навантаження на мережу та деякі edge-case-сценарії можуть відрізнятися. Тому позитивний результат локальних тестів не гарантує ідентичну поведінку на Devnet.

Каталог migrations/

Каталог migrations/ містить скрипти розгортання. У стандартному шаблоні Anchor тут є єдиний файл deploy.sh.

Скрипти розгортання (deploy)

Типовий вміст deploy.sh мінімальний:

#!/bin/bash
anchor deploy

У реальних проєктах цей скрипт розширюють: додають ініціалізацію конкретних акаунтів, створення токен-майнт-акаунтів, завантаження початкових даних тощо. Наприклад, якщо вашій програмі потрібен SPL Token mint, його створення логічно розмістити саме тут.

Залежність від Anchor.toml

Скрипт deploy.sh не приймає параметри безпосередньо — він покладається на конфігурацію з Anchor.toml. Зокрема, команда anchor deploy читає параметр [programs.<cluster>], щоб визначити, куди саме розгортати байткод, і wallet, щоб підписати транзакцію. Якщо Anchor.toml налаштований на localnet, а ви очікуєте розгортання на Devnet, скрипт розгорне програму не туди.

Файл Anchor.toml

Це центральний конфігураційний файл проєкту. Anchor CLI читає його перед будь-якою командою: build, test, deploy, run.

Ключові параметри: cluster, wallet, scripts

Мінімальний робочий Anchor.toml для локальної розробки:

[features]
seeds = false

[programs.localnet]
my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

Розбір ключових полів:

  • [programs.<cluster>] — мапінг імені програми на її Program ID (адреса в мережі). Кластер у ключі (наприклад, localnet, devnet) має збігатися зі значенням cluster у секції [provider]. Якщо збігу немає — Anchor не знайде Program ID і видасть помилку.
  • cluster — цільовий кластер: localnet, devnet, testnet або URL кастомного RPC.
  • wallet — шлях до файлу з ключовою парою (keypair), який підписуватиме транзакції. Для Devnet це має бути акаунт із SOL (отримати через faucet).
  • scripts.test — команда, яку виконує anchor test. Зазвичай це запуск ts-mocha.

Як налаштувати для Devnet

Для переходу на Devnet потрібно внести дві зміни:

  1. Додати секцію [programs.devnet] з Program ID, який ви отримали після першого розгортання (або згенерували через solana-keygen pubkey).
  2. Змінити cluster на devnet.
[programs.devnet]
my_program = "YourRealDevnetProgramIDHere"

[provider]
cluster = "devnet"
wallet = "~/.config/solana/devnet-wallet.json"

Після цього anchor deploy відправить програму на Devnet, а anchor test спробує підключитися до публічного Devnet-RPC. Важливо: для тестів на Devnet ваш гаманець має мати достатньо SOL для оплати транзакцій, а тести повинні бути написані з урахуванням реальної затримки мережі (додайте commitment: "confirmed" або збільште таймаути).

Файл Cargo.toml програми

Кожна програма в programs/ має власний Cargo.toml — стандартний файл конфігурації для Rust-проєктів. Anchor додає сюди специфічні залежності та налаштування.

Залежності: anchor-lang, spl-token

Типовий programs/my_program/Cargo.toml:

[package]
name = "my_program"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "my_program"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.30.1"
spl-token = "4.0.0"

Ключові моменти:

  • crate-type: cdylib потрібен для компіляції у WebAssembly (формат, який використовує Solana Runtime). lib — для локальних тестів та генерації IDL.
  • anchor-lang: основна бібліотека Anchor. Версія має збігатися з версією Anchor CLI (anchor --version). Несумісність версій призводить до неочевидних помилок компіляції.
  • spl-token: додається, якщо програма працює з SPL Token. Якщо ви не використовуєте токени, не додавайте цю залежність — вона збільшує розмір байткоду.

Якщо вам потрібні додаткові SPL-програми (наприклад, spl-associated-token-account), додавайте їх окремо і стежте за сумісністю версій. Бібліотеки з простору spl-* мають бути узгоджені між собою.

Фічі та профілі компіляції

Секція [features] у Cargo.toml програми керує поведінкою компіляції:

  • no-entrypoint — виключає стандартну точку входу Solana. Використовується, коли програма викликається через CPI (Cross-Program Invocation) і не повинна приймати прямі транзакції.
  • no-idl — не генерує IDL при збірці. Корисно для production, якщо ви не хочете розкривати інтерфейс програми.
  • no-log-ix-name — виключає логування імен інструкцій, що зменшує розмір байткоду.

Щодо профілів компіляції: у стандартному шаблоні Anchor не додає кастомні профілі до Cargo.toml програми. Проте для production-збірки з оптимізаціями можна додати:

[profile.release]
overflow-checks = true
lto = true
codegen-units = 1

Це зменшує розмір байткоду та покращує продуктивність, але збільшує час компіляції. Для розробки на Devnet стандартних налаштувань достатньо.

Обмеження, про яке варто знати: максимальний розмір програми на Solana становить 200 КБ (з урахуванням усіх залежностей). Якщо після додавання spl-token та інших крейтів ви наближаєтесь до цієї межі, перевірте розмір через solana program show <PROGRAM_ID> після розгортання на Devnet.

Наступний логічний крок після освоєння структури проєкту — зрозуміти, як Anchor перевіряє коректність переданих акаунтів під час виклику інструкцій. Це тема наступного розділу: Anchor constraints: обмеження та перевірки в програмах.

Джерела