Anchor
Anchor Vault

Anchor Vault

166 Graduates

Anchor Vault

Anchor Vault Challenge

保險庫

保險庫讓用戶可以安全地存儲資產。保險庫是去中心化金融(DeFi)中的一個基本組件,其核心功能是讓用戶可以安全地存儲資產(在此情況下是 lamports),並且只有該用戶本人可以在之後提取。

在這個挑戰中,我們將構建一個簡單的 lamport 保險庫,展示如何使用基本賬戶、程序衍生地址(PDA)和跨程序調用(CPI)。如果你對 Anchor 不熟悉,建議先閱讀Anchor 入門,以熟悉我們在此程序中將使用的核心概念。

安裝

在開始之前,請確保已安裝 Rust 和 Anchor(如果需要回顧,請參閱官方文檔)。然後在終端中運行:

anchor init blueshift_anchor_vault

這次挑戰不需要額外的 crate,因此你現在可以打開新生成的文件夾,準備開始編碼!

模板

讓我們從基本的程序結構開始。由於這是一個簡單的程序,我們將在 lib.rs 中實現所有內容。以下是包含核心組件的初始模板:

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
}

注意:記得將程序 ID 更改為 22222222222222222222222222222222222222222222,因為我們在後台使用它來測試你的程序。

賬戶

由於兩個指令使用相同的賬戶,為了更簡單和更具可讀性,我們可以創建一個名為 VaultAction 的上下文,並將其用於 depositwithdraw

VaultAction 賬戶結構需要包含:

  • signer:這是保險庫的擁有者,也是創建保險庫後唯一可以提取 lamports 的人。

  • vault:一個由以下種子衍生出的 PDA:[b"vault", signer.key().as_ref()],用於為簽名者保存 lamports。

  • system_program:系統程序賬戶,需要包含它,因為我們將使用系統程序的轉賬指令 CPI。

以下是我們如何定義帳戶結構的:

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

讓我們逐一解析每個帳戶約束:

  1. signer:需要 mut 約束,因為我們將在轉賬過程中修改其 lamports。

  2. vault

    • mut,因為我們將修改其 lamports。

    • seedsbumps 定義了如何從種子派生出有效的 PDA。

  3. system_program:檢查帳戶是否設置為可執行,並且地址是否為系統程序的地址。

Errors

對於這些小型程序,我們不需要太多錯誤,因此我們只會創建兩個枚舉:

  • VaultAlreadyExists:用於告知帳戶中是否已經有一些 lamports,因為這意味著保險庫已經存在。

  • InvalidAmount:我們不能存入少於基本帳戶最低租金的金額,因此我們檢查金額是否大於該值。

它看起來會像這樣:

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

Deposit

存款指令執行以下步驟:

  1. 驗證保險庫為空(lamports 為零),以防止重複存款

  2. 確保存款金額超過 SystemAccount 的免租金最低限額

  3. 使用 CPI 將 lamports 從簽署者轉移到保險庫,調用系統程序

首先實現這些檢查:

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);

這兩個 require 宏就像自定義的守衛條款:

  • require_eq! 確認保險庫為空(防止重複存款)。

  • require_gt! 檢查金額是否超過免租金門檻。

一旦檢查通過,Anchor 的系統程序助手會像這樣調用 Transfer CPI:

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

提取指令執行以下步驟:

  1. 驗證保管庫內是否有 lamports(非空)

  2. 使用保管庫的 PDA 以其自身名義簽署轉賬

  3. 將保管庫內的所有 lamports 轉回到簽署者

首先,讓我們檢查保管庫內是否有可提取的 lamports:

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

然後,我們需要創建 PDA 簽署者種子並執行轉賬:

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()
)?;

此提取的安全性由以下兩個因素保證:

  1. 保管庫的 PDA 是使用簽署者的公鑰派生的,確保只有原始存款人可以提取

  2. PDA 簽署轉賬的能力通過我們提供給 CpiContext::new_with_signer 的種子進行驗證

Conclusion

現在,您可以使用我們的單元測試來測試您的程序並領取您的 NFT!

首先,在終端中使用以下命令構建您的程序

anchor build

這會在您的 target/deploy 資料夾中直接生成一個 .so 文件。

現在點擊 take challenge 按鈕,然後將文件拖放到那裡!

準備好參加挑戰了嗎?
Blueshift © 2025Commit: e573eab