在Anchor框架中,有多种不同类型的账户和相关模块,每种类型都有其特定的用途和场景。Anchor对其进行了封装,不同的庄户类型有不同的作用,那我们在写合约的时候可能会疑惑,这么多种账户系统,我怎么知道我要写的账户应该用哪种方式表示呢?下面我们就来详细进行研究一下,Anchor提供的工具怎么开
<!--StartFragment-->
在 Anchor 框架中,有多种不同类型的账户和相关模块,每种类型都有其特定的用途和场景。Anchor对其进行了封装,不同的庄户类型有不同的作用,那我们在写合约的时候可能会疑惑,这么多种账户系统,我怎么知道我要写的账户应该用哪种方式表示呢?下面我们就来详细进行研究一下,Anchor提供的工具怎么开用的顺手
Anchor 一共定义了9种不同的账户类型,用来适应不同的情况,看看你了解几种?
| 账户类型 | 用途 | 使用场景 | 
|---|---|---|
account | 
用于定义由智能合约管理的数据结构 | 创建存储数据的账户,如用户信息、游戏状态等 | 
account_info | 
提供账户的低级访问,包括密钥、余额、所有者等信息 | 在合约中手动处理账户数据或需要访问账户的详细信息时使用 | 
account_loader | 
用于延迟加载账户数据,通常用于大型数据账户 | 处理大型数据结构时使用,优化性能和成本 | 
interface_account | 
用于定义可以实现特定接口的账户 | 当合约逻辑需要与实现了某个接口的其他合约互动时使用 | 
program | 
用于定义与其他合约程序的交互 | 当合约需要调用其他合约的功能或方法时使用 | 
signer | 
代表需要签名的账户,通常是用户的钱包账户 | 处理需要用户授权的交易时使用 | 
system_account | 
代表系统账户,如 Solana 的系统程序 | 进行如创建账户、转账等系统级操作时使用 | 
sysvar | 
用于访问系统变量,如当前区块时间、租金配置等 | 合约需要读取链上的环境或配置信息时使用 | 
unchecked_account | 
用于账户,其数据不会在运行时被自动解码或验证 | 需要手动处理账户数据或执行非标准操作时使用 | 
这些账户类型在 Anchor 框架中的设计使得智能合约开发更加模块化和安全,同时提供了灵活的方式来处理各种区块链上的情况。
但是看完还是有点懵,来看看是怎么在代码中使用的吧:Anchor 中每种账户类型的 Rust 代码示例
accountaccount 装饰器用于定义一个由智能合约管理的数据结构,这些结构映射到链上的账户,并由 Anchor 框架自动处理序列化和反序列化。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
#[account]
pub struct UserProfile {
    pub user_id: u64,
    pub username: String,
    pub email: String,
    pub score: u32,
}
#[derive(Accounts)]
pub struct CreateUserProfile<'info> {
    #[account(init, payer = user, space = 8 + 32 + 40 + 40 + 4)] // 分配足够的空间
    pub user_profile: Account<'info, UserProfile>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}
pub fn create_user_profile(ctx: Context<CreateUserProfile>, username: String, email: String) -> Result<()> {
    let profile = &mut ctx.accounts.user_profile;
    profile.user_id = ctx.accounts.user.key().to_bytes().iter().fold(0, |acc, &b| acc * 256 + b as u64); // 生成一个简单的用户ID
    profile.username = username;
    profile.email = email;
    profile.score = 0;
    Ok(())
}
account_infoaccount_info 提供对账户的低级访问,包括账户的公钥、余额、所有者和其他元数据。它不自动处理数据的反序列化,让开发者可以手动处理账户数据。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct TransferFunds<'info> {
    #[account(mut)]
    pub from_account: AccountInfo<'info>,
    #[account(mut)]
    pub to_account: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}
pub fn transfer_funds(ctx: Context<TransferFunds>, amount: u64) -> Result<()> {
    // 检查余额是否充足
    if **ctx.accounts.from_account.lamports.borrow() < amount {
        return Err(ProgramError::InsufficientFunds.into());
    }
    // 从一个账户转账到另一个账户
    **ctx.accounts.from_account.lamports.borrow_mut() -= amount;
    **ctx.accounts.to_account.lamports.borrow_mut() += amount;
    Ok(())
}
account_loaderaccount_loader 用于延迟加载账户数据,这对于处理大型数据结构特别有用,因为它允许合约在需要时才加载数据,从而可以节省资源和提高执行效率。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
use anchor_lang::solana_program::entrypoint::ProgramResult;
#[account(zero_copy)]
pub struct LargeData {
    pub data: [u64; 1024],  // 示例: 大型数据数组
}
#[derive(Accounts)]
pub struct ProcessLargeData<'info> {
    #[account(zero_copy)]
    pub large_data_account: AccountLoader<'info, LargeData>,
}
pub fn process_data(ctx: Context<ProcessLargeData>, index: usize, value: u64) -> ProgramResult {
    let mut data_account = ctx.accounts.large_data_account.load_mut()?;
    data_account.data[index] = value;  // 更新特定索引处的数据
    Ok(())
}
interface_accountinterface_account 用于定义一个可以实现特定接口的账户。这允许合约通过一个统一的接口与多个不同的实现互动,增加了合约的灵活性和可扩展性。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_pack::Pack;
#[interface]
pub trait Token {
    fn transfer(&self, to: Pubkey, amount: u64) -> ProgramResult;
}
#[derive(Accounts)]
pub struct TransferTokens<'info> {
    #[account(executable)]
    pub token_program: InterfaceAccount<'info, dyn Token>,
    pub from: Signer<'info>,
    pub to: AccountInfo<'info>,
}
pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> ProgramResult {
    ctx.accounts.token_program.transfer(ctx.accounts.to.key(), amount)
}
programprogram 用于定义合约代码与其他 Solana 程序的交互。这使得合约可以调用其他程序提供的功能,如系统功能、代币操作等。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct CallAnotherProgram<'info> {
    pub system_program: Program<'info, System>,
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(init, payer = payer, space = 8 + 8)]
    pub new_account: AccountInfo<'info>,
}
pub fn create_account(ctx: Context<CallAnotherProgram>, lamports: u64) -> Result<()> {
    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(ctx.accounts.new_account.data_len());
    let seeds = &[b"new_account_seed"[..], &[bump_seed]];
    let bump_seed = Pubkey::find_program_address(seeds, ctx.program_id).1;
    anchor_lang::system_program::create_account(
        CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
            anchor_lang::system_program::CreateAccount {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.new_account.to_account_info(),
                lamports: lamports,
                space: ctx.accounts.new_account.data_len() as u64,
                owner: ctx.program_id,
            },
        ),
    )?;
    Ok(())
}
signersigner 代表需要参与交易签名的账户,通常是指用户的钱包账户或其他需要用户明确授权的账户。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct ExecuteTransaction<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    #[account(mut)]
    pub storage_account: Account<'info, Storage>,
}
#[account]
pub struct Storage {
    pub data: u64,
}
pub fn update_storage(ctx: Context<ExecuteTransaction>, new_data: u64) -> Result<()> {
    let account = &mut ctx.accounts.storage_account;
    account.data = new_data;
    Ok(())
}
sysvar 用于访问 Solana 网络提供的系统变量,这些系统变量提供了链上的环境信息,如当前区块时间、最近区块哈希、租金配置等。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::clock::Clock;
#[derive(Accounts)]
pub struct CheckTime<'info> {
    // 直接访问 Clock sysvar
    pub clock: Sysvar<'info, Clock>,
}
pub fn check_current_time(ctx: Context<CheckTime>) -> Result<()> {
    let clock = &ctx.accounts.clock;
    msg!("Current slot: {}", clock.slot);
    msg!("Current timestamp: {}", clock.unix_timestamp);
    Ok(())
}
system_account 代表 Solana 系统程序的账户,这个程序负责基础的网络操作,如账户创建、资金转账等。代码示例:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_program;
#[derive(Accounts)]
pub struct CreateNewAccount<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(init, payer = payer, space = 8 + 64)]
    pub new_account: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}
pub fn create_account(ctx: Context<CreateNewAccount>, lamports: u64) -> Result<()> {
    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(ctx.accounts.new_account.data_len());
    system_program::create_account(
        CpiContext::new_with_signer(
            ctx.accounts.system_program.to_account_info(),
            system_program::CreateAccount {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.new_account.to_account_info(),
                lamports: required_lamports,
                space: ctx.accounts.new_account.data_len() as u64,
                owner: ctx.program_id,
            },
            &[&[b"new_account_seed", &[bump_seed]]],
        ),
    )?;
    Ok(())
}
unchecked_account用途: unchecked_account 用于表示一个账户,其数据不会在运行时被 Anchor 自动解码或验证。这意味着 Anchor 不会自动处理这个账户的数据结构和类型安全检查。
场景: 此类型通常用于以下情况:
这里是一个简单的例子,展示如何在 Anchor 中使用 unchecked_account:
rust
 代码解读
复制代码
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct UseUncheckedAccount<'info> {
    pub user: Signer<'info>,
    // 声明一个 unchecked_account
    pub data_account: UncheckedAccount<'info>,
}
pub fn access_unchecked_account(ctx: Context<UseUncheckedAccount>) -> Result<()> {
    // 直接访问未经检查的账户的数据
    let data = ctx.accounts.data_account.try_borrow_data()?;
    // 开发者需要自己处理数据解码
    let custom_data = CustomData::try_from_slice(&data)?;
    msg!("Custom data: {:?}", custom_data);
    Ok(())
}
// 假设有一个自定义数据结构
#[derive(Debug)]
struct CustomData {
    pub field1: u32,
    pub field2: u64,
}
impl CustomData {
    fn try_from_slice(data: &[u8]) -> Result<Self, ProgramError> {
        let field1 = u32::from_le_bytes(data[0..4].try_into().unwrap());
        let field2 = u64::from_le_bytes(data[4..12].try_into().unwrap());
        Ok(Self { field1, field2 })
    }
}
在这个示例中,我们看到 data_account 被声明为 UncheckedAccount,这允许开发者直接访问和操作原始数据。此时,数据的正确性和解码逻辑完全由开发者控制,这提供了灵活性但同时增加了错误的可能性。
Telegram: https://t.me/gtokentool
<!--EndFragment-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!