Anchor
Anchor für Einsteiger

Anchor für Einsteiger

Anchor 101

Einführung in Anchor

What is Anchor

Anchor ist das führende Framework für die Entwicklung von Solana Smart Contracts und bietet einen vollständigen Workflow zum Schreiben, Testen, Bereitstellen und Interagieren mit Onchain-Programmen.

Hauptvorteile

  • Reduzierter Boilerplate-Code: Anchor abstrahiert die repetitive Arbeit der Kontoverwaltung, Instruktionsserialisierung und Fehlerbehandlung, sodass Sie sich auf die eigentliche Geschäftslogik konzentrieren können.

  • Integrierte Sicherheit: Strenge Prüfungen wie die Verifizierung der Kontoinhaberschaft und Datenvalidierung laufen automatisch ab und mindern viele gängige Sicherheitslücken, bevor sie auftreten.

Anchor Macros

  • declare_id!(): Deklariert, unter welcher Onchain-Adresse das Programm existiert.

  • #[program]: Markiert das Modul, das jeden Instruction-Einstiegspunkt und jede Geschäftslogikfunktion enthält.

  • #[derive(Accounts)]: Listet die Konten auf, die eine Instruction benötigt, und erzwingt automatisch deren Einschränkungen.

  • #[error_code]: Definiert benutzerdefinierte, menschenlesbare Fehlertypen, die das Debugging klarer und schneller machen.

Zusammen abstrahieren diese prozeduralen Macros die Low-Level-Byte-Verwaltung und ermöglichen es dir, sichere, produktionsreife Solana-Programme mit deutlich weniger Aufwand zu erstellen.

Programmstruktur

Beginnen wir mit einer minimalen Version eines Programms, um im Detail zu erklären, was jedes Macro tatsächlich bewirkt:

rust
declare_id!("22222222222222222222222222222222222222222222");

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

    pub fn deposit(ctx: Context<VaultAction>, amount: u64) -> Result<()> {
        // ...
        Ok(())
    }

    pub fn withdraw(ctx: Context<VaultAction>) -> Result<()> {
        // ...
        Ok(())
    }
}

#[derive(Accounts)]
pub struct VaultAction<'info> {
    // ...
}

#[error_code]
pub enum VaultError {
    // ...
}

Dies wird sich in fokussierte Module verwandeln, anstatt alles in das lib.rs zu packen, für strukturiertere Programme. Der Programmordnerbaum wird ungefähr so aussehen:

text
src
├── instructions
│       ├── instruction1.rs
│       ├── mod.rs
│       ├── instruction2.rs
│       └── instruction3.rs
├── errors.rs
├── lib.rs
└── state.rs

declare_id!()

Das declare_id!()Macro weist deinem Programm seine Onchain-Adresse zu; einen eindeutigen öffentlichen Schlüssel, der aus dem Keypair im targetOrdner des Projekts abgeleitet wird. Dieses Keypair signiert und stellt die kompilierte .soBinärdatei bereit, die die gesamte Programmlogik und -daten enthält.

Hinweis: Wir verwenden den Platzhalter 222222... in Blueshift-Beispielen aufgrund unserer internen Testsuite. In der Produktionsumgebung wird Anchor eine neue Programm-ID für Sie generieren, wenn Sie die standardmäßigen Build- und Deploy-Befehle ausführen.

#[program] & #[derive(Accounts)]

Jede Anweisung hat ihre eigene Context-Struktur, die alle Konten und optional alle Daten auflistet, die die Anweisung benötigt.

In diesem Beispiel teilen sich sowohl deposit als auch withdraw dieselben Konten; aus diesem Grund erstellen wir eine einzelne Kontostruktur namens VaultAction, um die Sache effizienter und einfacher zu gestalten.

Ein genauerer Blick auf das #[derive(Accounts)] Makro

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>,
}

Wie wir aus dem Code-Snippet ersehen können, erfüllt das #[derive(Accounts)] Makro drei wichtige Aufgaben:

  • Es deklariert alle Konten, die eine bestimmte Anweisung benötigt.

  • Es erzwingt automatische Constraint-Prüfungen und blockiert viele Fehler und potenzielle Exploits zur Laufzeit.

  • Es generiert Hilfsmethoden, mit denen Sie sicher auf Konten zugreifen und diese ändern können.

Dies wird durch eine Kombination aus Kontotypen und Inline-Attributen erreicht.

Kontotypen in unserem Beispiel

  • Signer<'info>: Überprüft, ob das Konto die Transaktion signiert hat; wichtig für die Sicherheit und für CPIs, die eine Signatur erfordern.

  • SystemAccount<'info>: Bestätigt, dass das Konto dem System Program gehört.

  • Program<'info, System>: Stellt sicher, dass das Konto ausführbar ist und mit der System Program ID übereinstimmt, was CPIs wie Kontoerstellung oder Lamport-Transfers ermöglicht.

Inline-Attribute, denen Sie begegnen werden

  • mut: Kennzeichnet das Konto als veränderbar; obligatorisch, wenn sich der Lamport-Saldo oder die Daten ändern können.

  • seeds & bump: Überprüft, ob das Konto eine Program-Derived Address (PDA) ist, die aus den angegebenen Seeds plus einem Bump-Byte generiert wurde.

Hinweis PDAs sind wichtig, weil:

  • Wenn sie vom Programm verwendet werden, das sie besitzt, können PDAs CPIs im Namen des Programms signieren.

  • Sie bieten deterministische, überprüfbare Adressen zur Speicherung des Programmzustands.

#[error_code]

Das Makro #[error_code] ermöglicht es dir, klare, benutzerdefinierte Fehler innerhalb des Programms zu definieren.

rust
#[error_code]
pub enum VaultError {
    #[msg("Vault already exists")]
    VaultAlreadyExists,
    #[msg("Invalid amount")]
    InvalidAmount,
}

Jede Enum-Variante kann ein #[msg(...)]Attribut tragen, das einen beschreibenden String protokolliert, wann immer der Fehler auftritt; wesentlich hilfreicher als ein roher numerischer Code beim Debugging.

Blueshift © 2025Commit: e573eab