Deze quickstart gebruikt het
Anchor framework startersproject gegenereerd
door anchor init. Je maakt het project lokaal aan, voert de tests uit, bouwt
het programma en doorloopt de programmacode die Anchor generiert.
Vereisten
Voordat je begint, installeer de Solana-ontwikkeltools. De installatie omvat Rust, de Solana CLI en de Anchor CLI.
Gebruik Anchor CLI versie 1.1.2 of hoger voor dit sjabloon. Controleer je geïnstalleerde versie:
$anchor --version
Maak het project aan
Voer de volgende opdrachten uit in je terminal:
$anchor init my-program$cd my-program
Het startersproject bevat één Solana-programma onder programs/my-program. Het
programma bevat twee instructies: één om een telleraccount te initialiseren en
één om de teller te verhogen.
Sommige onderdelen van het sjabloon demonstreren veelvoorkomende Solana-programmapatronen: het afleiden van PDA-accountadressen, het uitvoeren van een Cross Program Invocation (CPI) om SOL over te maken, en het gebruik van aangepaste foutcontroles om een instructie te stoppen wanneer een conditie mislukt.
Programma Bouwen
Voer anchor build uit om het startersprogramma te compileren:
$anchor build
Het gecompileerde programma wordt geschreven naar target/deploy/my_program.so.
Wanneer het programma wordt uitgerold, wordt de inhoud van dit .so-bestand
opgeslagen in een account onchain.
Test uitvoeren
Voer de standaardtest uit:
$anchor test
De Anchor.toml van dit sjabloon gebruikt de Rust-testopdracht:
skip_local_validator = true[scripts]test = "cargo test"
De test laadt het gecompileerde programma in
LiteSVM, maakt een betaler aan, verstuurt de
initialize en increment instructies, en controleert vervolgens de
status van het telleraccount.
Het uitvoeren van anchor test compileert het programma ook, dus u hoeft
anchor build niet eerst uit te voeren bij lokaal testen.
Programma implementeren
Lokale tests bieden de snelste feedbackcyclus. Wanneer u klaar bent om te implementeren op een netwerk, bijvoorbeeld devnet, bouw dan eerst en implementeer daarna op een cluster.
Voor het implementeren van een Solana-programma is SOL vereist, omdat het programma wordt opgeslagen in een account, en het account moet betalen voor de ruimte die het gebruikt. Op devnet kunt u gratis devnet SOL aanvragen via de Solana Faucet of met de Solana CLI:
$solana airdrop 2 --url devnet
$anchor build$anchor deploy --provider.cluster devnet
Bronbestanden
De map src bevat het Solana-programma. De
Programmastructuur-documentatie
van Anchor legt de kernmacro's uit die hier worden gebruikt, waaronder
declare_id!, #[program], #[derive(Accounts)] en
#[account]. Dit gedeelte beschrijft de sjabloonbestanden.
lib.rs
lib.rs is het toegangspunt van het programma. Het verbindt de bronbestanden,
definieert het programmaadres en definieert de programma-instructies die
gebruikers kunnen aanroepen.
pub mod constants;pub mod error;pub mod instructions;pub mod state;use anchor_lang::prelude::*;pub use constants::*;pub use instructions::*;pub use state::*;declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");#[program]pub mod my_program {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {crate::instructions::initialize::handle_initialize(ctx)}pub fn increment(ctx: Context<Increment>) -> Result<()> {crate::instructions::increment::handle_increment(ctx)}}
[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"
declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");
Hetzelfde programmaadres komt voor in de configuratie en de code. Anchor.toml
vertelt Anchor welk adres moet worden ingezet of aangeroepen voor een cluster.
declare_id! definieert het programmaadres in het programma voor
beveiligingscontroles.
constants.rs
constants.rs bewaart gedeelde waarden op één plek. In dit sjabloon leidt
COUNTER_SEED de teller-PDA af, wordt HELLO_WORLD_LAMPORTS
overgedragen tijdens initialisatie, en wordt MAX_COUNT gecontroleerd vóór
het ophogen.
use anchor_lang::prelude::*;#[constant]pub const COUNTER_SEED: &[u8] = b"counter";#[constant]pub const HELLO_WORLD_LAMPORTS: u64 = 1;#[constant]pub const MAX_COUNT: u64 = 10;
#[derive(Accounts)]pub struct Initialize<'info> {// ...#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {// ...anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;Ok(())}
initialize.rs gebruikt twee constanten:
COUNTER_SEEDleidt het PDA-adres van de teller af.HELLO_WORLD_LAMPORTSstelt het bedrag in dat van de betaler naar het telleraccount wordt overgemaakt.
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;Ok(())}
increment.rs gebruikt MAX_COUNT als de bovengrens
van de teller. Als de huidige telling al op het maximum staat, geeft
require! de waarde CounterOverflow terug en worden de
accountgegevens niet gewijzigd.
state.rs
state.rs definieert aangepaste gegevenstypen voor accounts die het programma
aanmaakt en beheert. Het programma definieert instructies om die gegevens aan te
maken, te initialiseren en bij te werken, maar de tellergegevens worden niet in
het programma zelf opgeslagen. Ze worden opgeslagen in een aparte account met
een eigen adres.
use anchor_lang::prelude::*;#[account]#[derive(InitSpace)]pub struct Counter {pub count: u64,pub authority: Pubkey,}
#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();Ok(())}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...ctx.accounts.counter.count += 1;Ok(())}
state.rs definieert de Counter accountgegevens. De instructiebestanden
gebruiken dat type wanneer ze het account aanmaken en bijwerken:
Counter::INIT_SPACEbepaalt de grootte van het account voor de velden gedefinieerd instate.rs.countenauthorityzijn de veld- waarden die worden geschreven wanneer het account wordt geïnitialiseerd.count += 1werkt de opgeslagen tellerwaarde bij nadat de validatie is geslaagd.
error.rs
error.rs definieert de aangepaste fouten van het programma. In dit sjabloon
laten de fouten zien hoe instructie-handlers stoppen wanneer een aanroeper niet
bevoegd is om de teller bij te werken of de teller al MAX_COUNT heeft
bereikt.
use anchor_lang::prelude::*;#[error_code]pub enum ErrorCode {#[msg("Only the counter authority can update this counter")]Unauthorized,#[msg("Counter has reached the maximum value")]CounterOverflow,}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
error.rs benoemt de fouten die de instructie kan retourneren:
ErrorCode::Unauthorizedwordt geretourneerd wanneer de ondertekenaar niet de authority is die is opgeslagen in het telleraccount.ErrorCode::CounterOverflowwordt geretourneerd wanneer de teller alMAX_COUNTheeft bereikt.
instructions.rs
instructions.rs verbindt de instructiebestanden met de programma-crate zodat
lib.rs toegang heeft tot de initialize- en
increment-instructiecode. Elk instructiebestand definieert de accounts die
vereist zijn door die instructie en de handlerlogica die wordt uitgevoerd nadat
Anchor die accounts heeft gevalideerd.
pub mod initialize;pub mod increment;pub use initialize::*;pub use increment::*;
initialize.rs
initialize.rs definieert de accounts die nodig zijn om het tegenaccount aan te
maken, en schrijft vervolgens de eerste waarden van het account. De
#[derive(Accounts)]-struct gebruikt Anchor
account constraints
om aan te geven welke accounts vereist zijn en hoe het nieuwe tegenaccount wordt
aangemaakt.
use anchor_lang::prelude::*;use crate::{constants::*, state::Counter};#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Account Context
De Initialize-struct definieert de accounts die moeten worden meegestuurd
wanneer een gebruiker de initialize-instructie aanroept. Anchor
controleert deze accounts voordat de handler wordt uitgevoerd.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Payer Account
Het payer-account betaalt voor het aanmaken van het tegenaccount. Het
Signer<'info>-type betekent dat de betaler de transactie moet
ondertekenen, en #[account(mut)] betekent dat het betaleraccount kan
worden gewijzigd omdat er lamports zullen worden afgetrokken.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Counter Account
Het counter-account slaat de Counter-gegevens op uit state.rs.
init vertelt Anchor dit account aan te maken vóór de handler wordt
uitgevoerd, en payer = payer vertelt Anchor welk account de aanmaak
betaalt.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Accountgrootte
De space constraint vertelt Anchor hoeveel accountdata er gealloceerd moet
worden. Anchor slaat eerst een 8-byte discriminator op, gevolgd door de bytes
die nodig zijn voor de Counter velden. De discriminator stelt Anchor in
staat dit account te herkennen als een Counter account voordat de
accountdata gedeserialiseerd wordt.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Tegeradres
De seeds en bump constraints definiëren het verwachte PDA-adres voor
het tegeraccount. Anchor verifieert dat het opgegeven counter account
overeenkomt met dat adres. De sjabloon gebruikt een PDA zodat gebruikers het
tegeradres kunnen afleiden van de program-ID en seed, waardoor het tegeradres
deterministisch is.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
System Program
Het system_program account is vereist omdat het aanmaken van een nieuw
account gebruik maakt van het System Program.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Handleerfunctie
De handle_initialize functie wordt uitgevoerd nadat Anchor de accounts in
Initialize heeft gevalideerd. De ctx waarde geeft de handler toegang
tot die gecontroleerde accounts.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Begindata
De handler schrijft de eerste waarden naar het nieuwe tegeraccount. De teller
begint op 0, en de betaler wordt de autoriteit die gemachtigd is om de
teller later te verhogen.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Overdrachtsaccounts
Deze overdrachts-CPI is alleen opgenomen om te demonstreren hoe een CPI accounts
doorgeeft aan een ander programma. De Transfer struct vermeldt de accounts
die worden gebruikt door de System Program overdracht.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
CPI-context
CpiContext::new combineert het aangeroepen programma met de accounts die
aan dat programma worden meegegeven. Dit is de basisvorm van een CPI: kies het
programma om aan te roepen, verzamel de accounts die dat programma verwacht en
geef beide door aan de aanroep. Hier is het aangeroepen programma het System
Program.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Invoke Transfer
anchor_lang::system_program::transfer roept de System Program
transfer-instructie aan. In dit sjabloon is de overdracht een klein voorbeeld
van het aanroepen van een ander programma vanuit jouw programma. Als de transfer
CPI mislukt, mislukt ook de initialize-instructie.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Log Message
msg! schrijft een bericht naar de programmalogboeken. Ok(()) geeft
aan dat de instructie succesvol is teruggekeerd.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
increment.rs
increment.rs definieert de accounts die vereist zijn om een bestaand
telleraccount bij te werken. De handler controleert of de ondertekenaar de
opgeslagen autoriteit is, controleert of het aantal de opgegeven MAX_COUNT
niet heeft bereikt, en verhoogt vervolgens de teller.
use anchor_lang::prelude::*;use crate::{constants::*, error::ErrorCode, state::Counter};#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Accountcontext
De Increment struct definieert de accounts die moeten worden meegestuurd
wanneer een gebruiker de increment instructie aanroept. Anchor controleert
deze accounts voordat de handler wordt uitgevoerd.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Telleraccount
Het counter account slaat de Counter gegevens op. De mut
constraint staat de handler toe het opgeslagen aantal bij te werken, en de
seeds en bump constraints verifiëren het PDA-adres van de teller.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Autoriteitondertekenaar
Het authority account moet de transactie ondertekenen. De handler
controleert later of deze ondertekenaar overeenkomt met de autoriteit die is
opgeslagen in het telleraccount.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Handler Functie
De handle_increment functie wordt uitgevoerd nadat Anchor de accounts
valideert in Increment. De ctx waarde geeft de handler toegang tot
die gecontroleerde accounts.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Autoriteitscontrole
De eerste controle zorgt ervoor dat de ondertekenaar gemachtigd is om deze
teller bij te werken. Als het adres van de ondertekenaar niet overeenkomt met
counter.authority, stopt de instructie met ErrorCode::Unauthorized.
Dit demonstreert autorisatie op applicatieniveau: het programma bezit de
tellerdata, maar implementeert een regel voor welke ondertekenaar die data mag
wijzigen.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Maximale Tellercontrole
De tweede controle voorkomt dat de teller voorbij MAX_COUNT gaat. Als de
teller al op het maximum staat, stopt de instructie met
ErrorCode::CounterOverflow. Dit maximum is een kunstmatige regel in de
sjabloon zodat je kunt zien hoe aangepaste fouten een instructie stoppen voordat
de accountdata wordt gewijzigd.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Teller Bijwerken
Pas nadat beide controles zijn geslaagd, werkt de handler de accountdata bij. Deze regel telt één op bij de opgeslagen tellerwaarde.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Logbericht
msg! schrijft de bijgewerkte telling naar de programmalogboeken.
Ok(()) geeft aan dat de instructie succesvol is teruggekeerd.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Testbestand
programs/my-program/tests/test_initialize.rs is een Rust-integratietest. Er
wordt geen lokale validator gestart. In plaats daarvan laadt het het
gecompileerde .so-bestand in LiteSVM, bouwt het transacties die het programma
aanroepen en leest het het teller-account na elke transactie. De test bouwt
instructies voor een Solana-transactie door het programma-ID van het aan te
roepen programma op te geven, instruction data te verstrekken en de vereiste
accounts door te geven.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);
De Initialize accountcontext definieert de accounts die vereist zijn door
de initialize instructie. De test geeft diezelfde accounts door aan de
gegenereerde my_program::accounts::Initialize helper:
payerwordt doorgegeven alspayer: payer.pubkey().counterwordt doorgegeven alscounter.system_programwordt doorgegeven alssystem_program::ID.
my_program::instruction::Initialize {}.data()
maakt de instruction data aan. Dit is waar instructieargumenten gecodeerd zouden
worden, maar deze initialize instructie vereist geen argumenten.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);
De Increment accountcontext definieert de accounts die vereist zijn door
de increment instructie. De test geeft diezelfde accounts door aan de
gegenereerde my_program::accounts::Increment helper:
counterwordt doorgegeven alscounter.authoritywordt doorgegeven alsauthority: payer.pubkey().
my_program::instruction::Increment {}.data()
maakt de instruction data aan. Dit is waar instructieargumenten gecodeerd zouden
worden, maar deze increment instructie vereist geen argumenten.
use {anchor_lang::{prelude::Pubkey,solana_program::{instruction::Instruction, system_program},AccountDeserialize, InstructionData, ToAccountMetas,},litesvm::LiteSVM,solana_keypair::Keypair,solana_message::{Message, VersionedMessage},solana_signer::Signer,solana_transaction::versioned::VersionedTransaction,};#[test]fn test_initialize() {let program_id = my_program::id();let payer = Keypair::new();let counter = Pubkey::find_program_address(&[my_program::constants::COUNTER_SEED],&program_id,).0;let mut svm = LiteSVM::new();let bytes = include_bytes!(concat!(env!("CARGO_TARGET_TMPDIR"),"/../deploy/my_program.so"));svm.add_program(program_id, bytes).unwrap();svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 0);assert_eq!(counter_state.authority, payer.pubkey());let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 1);assert_eq!(counter_state.authority, payer.pubkey());}
Projectconfiguratie
De hoofdprojectbestanden vertellen Anchor en Cargo hoe het programma gebouwd, getest en gedeployed moet worden. Voor een volledige referentie, zie de Anchor-documentatie voor Anchor.toml-configuratie en de Anchor CLI.
skip_local_validator = true[toolchain][features]resolution = trueskip-lint = false[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"[scripts]test = "cargo test"[hooks]
Is this page helpful?