Anchor
Vault Anchor

Vault Anchor

166 Graduates

Vault Anchor

Anchor Vault Challenge

The Vault

Vault memungkinkan pengguna untuk menyimpan aset mereka dengan aman. Vault adalah blok bangunan fundamental dalam DeFi yang, pada intinya, memungkinkan pengguna untuk menyimpan aset mereka (lamport dalam kasus ini) dengan aman yang hanya dapat ditarik kembali oleh pengguna yang sama di kemudian hari.

Dalam tantangan ini, kita akan membangun vault lamport sederhana yang menunjukkan cara bekerja dengan akun dasar, Program Derived Addresses (PDA), dan Cross-Program Invocation (CPI). Jika Anda belum familiar dengan Anchor, Anda sebaiknya mulai dengan membaca Pengantar Anchor untuk membiasakan diri dengan konsep inti yang akan kita gunakan dalam program ini.

Installation

Sebelum Anda memulai, pastikan Rust dan Anchor sudah terpasang (lihat dokumentasi resmi jika Anda perlu penyegaran). Kemudian di terminal Anda jalankan:

anchor init blueshift_anchor_vault

Kita tidak memerlukan crate tambahan untuk tantangan ini, jadi Anda sekarang dapat membuka folder yang baru dibuat, dan Anda siap untuk mulai coding!

Template

Mari mulai dengan struktur program dasar. Kita akan mengimplementasikan semuanya di lib.rs karena ini adalah program yang sederhana. Berikut adalah template awal dengan komponen inti yang akan kita butuhkan:

rust
declare_id!("22222222222222222222222222222222222222222222");

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

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

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

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

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

Catatan: ingat untuk mengubah ID program menjadi 22222222222222222222222222222222222222222222 karena kami menggunakan ini di balik layar untuk menguji program Anda.

Accounts

Karena kedua instruksi menggunakan akun yang sama, untuk membuatnya lebih mudah dan lebih mudah dibaca, kita dapat membuat satu konteks bernama VaultAction dan menggunakannya untuk kedua deposit dan withdraw.

Struktur akun VaultAction akan memerlukan:

  • signer: ini adalah pemilik vault, dan satu-satunya orang yang dapat menarik lamport setelah membuat vault.

  • vault: PDA yang diturunkan dari seeds berikut: [b"vault", signer.key().as_ref()] yang menyimpan lamport untuk penandatangan.

  • system_program: akun program sistem yang perlu disertakan karena kita akan menggunakan instruksi transfer CPI dari program sistem

Berikut cara kami mendefinisikan struct akun:

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

Mari kita uraikan setiap batasan akun:

  1. signer: Batasan mut diperlukan karena kita akan memodifikasi lamports-nya selama transfer.

  2. vault:

    • mut karena kita akan memodifikasi lamports-nya.

    • seeds & bumps mendefinisikan cara menurunkan PDA yang valid dari seeds.

  3. system_program: memeriksa apakah akun diatur sebagai executable dan alamatnya adalah alamat System Program.

Errors

Kita tidak memerlukan banyak error untuk program kecil ini, jadi kita hanya akan membuat 2 enum:

  • VaultAlreadyExists: yang memberi tahu kita jika sudah ada beberapa lamports di akun karena itu berarti vault sudah ada.

  • InvalidAmount: kita tidak dapat menyetor jumlah yang kurang dari rent minimum untuk akun dasar, jadi kita memeriksa bahwa jumlahnya lebih besar dari itu.

Ini akan terlihat seperti ini:

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

Deposit

Instruksi deposit melakukan langkah-langkah berikut:

  1. Memverifikasi vault kosong (tidak memiliki lamports) untuk mencegah deposit ganda

  2. Memastikan jumlah deposit melebihi minimum rent-exempt untuk SystemAccount

  3. Mentransfer lamports dari penandatangan ke vault menggunakan CPI ke System Program

Mari kita implementasikan pemeriksaan ini terlebih dahulu:

rust
// Check if vault is empty
require_eq!(ctx.accounts.vault.lamports(), 0, VaultError::VaultAlreadyExists);

// Ensure amount exceeds rent-exempt minimum
require_gt!(amount, Rent::get()?.minimum_balance(0), VaultError::InvalidAmount);

Dua makro require bertindak seperti klausa penjaga kustom:

  • require_eq! mengonfirmasi vault kosong (mencegah deposit ganda).

  • require_gt! memeriksa jumlah melewati ambang batas rent-exempt.

Setelah pemeriksaan berhasil, helper System Program Anchor memanggil CPI Transfer seperti ini:

rust
use anchor_lang::system_program::{transfer, Transfer};

transfer(
    CpiContext::new(
        ctx.accounts.system_program.to_account_info(),
        Transfer {
            from: ctx.accounts.signer.to_account_info(),
            to: ctx.accounts.vault.to_account_info(),
        },
    ),
    amount,
)?;

Withdraw

Instruksi withdraw melakukan langkah-langkah berikut:

  1. Memverifikasi vault berisi lamport (tidak kosong)

  2. Menggunakan PDA vault untuk menandatangani transfer atas namanya sendiri

  3. Mentransfer semua lamport dari vault kembali ke penandatangan

Pertama, mari kita periksa apakah vault memiliki lamport untuk ditarik:

rust
// Check if vault has any lamports
require_neq!(ctx.accounts.vault.lamports(), 0, VaultError::InvalidAmount);

Kemudian, kita perlu membuat seeds penandatangan PDA dan melakukan transfer:

rust
// Create PDA signer seeds
let signer_key = ctx.accounts.signer.key();
let signer_seeds = &[b"vault", signer_key.as_ref(), &[ctx.bumps.vault]];

// Transfer all lamports from vault to signer
transfer(
    CpiContext::new_with_signer(
        ctx.accounts.system_program.to_account_info(),
        Transfer {
            from: ctx.accounts.vault.to_account_info(),
            to: ctx.accounts.signer.to_account_info(),
        },
        &[&signer_seeds[..]]
    ),
    ctx.accounts.vault.lamports()
)?;

Keamanan penarikan ini dijamin oleh dua faktor:

  1. PDA vault diturunkan menggunakan kunci publik penandatangan, memastikan hanya penyetor asli yang dapat melakukan penarikan

  2. Kemampuan PDA untuk menandatangani transfer diverifikasi melalui seeds yang kita berikan ke CpiContext::new_with_signer

Conclusion

Sekarang Anda dapat menguji program Anda terhadap unit test kami dan mengklaim NFT Anda!

Mulailah dengan membangun program Anda menggunakan perintah berikut di terminal Anda

anchor build

Ini menghasilkan file .so langsung di folder target/deploy Anda.

Sekarang klik tombol take challenge dan letakkan file tersebut di sana!

Siap mengambil tantangan?
Daftar Isi
Lihat Sumber
Blueshift © 2025Commit: e573eab