Day 02 - Solana基础知识
日期: 第2天
目标: 理解 Solana 账户模型、PDA 和 Instruction 架构
📋 任务清单
- 理解 Solana 账户模型
- 学习 Program Derived Address (PDA)
- 掌握 Instruction 设计模式
- 理解 Signer 和 Owner 概念
- 认识账户生命周期
💻 核心代码
1. 账户模型基础
// Solana 账户包含:
// 1. pubkey - 账户地址
// 2. lamports - SOL 余额(1 SOL = 10^9 lamports)
// 3. data - 账户存储的数据
// 4. owner - 账户的所有者(通常是程序)
// 5. executable - 是否是可执行程序
// 6. rent_epoch - 下一个需要支付租金的时期
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct ExampleAccounts<'info> {
// 需要签署的账户
#[account(mut)]
pub authority: Signer<'info>,
// 程序账户
#[account(mut, owner = system_program::ID)]
pub program_account: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}
2. Program Derived Address (PDA)
use anchor_lang::prelude::*;
// PDA 是由程序生成的确定性地址
// 格式:find_program_address(&[seeds], program_id)
#[derive(Accounts)]
#[instruction(name: String)]
pub struct CreateAccount<'info> {
#[account(mut)]
pub payer: Signer<'info>,
// 使用 PDA 创建账户
#[account(
init,
payer = payer,
space = 8 + 32 + 8,
seeds = [b"user", name.as_ref()],
bump
)]
pub user_account: Account<'info, UserAccount>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct UserAccount {
pub authority: Pubkey,
pub balance: u64,
}
impl<'info> CreateAccount<'info> {
pub fn process(&mut self, name: String) -> Result<()> {
let user = &mut self.user_account;
user.authority = self.payer.key();
user.balance = 0;
Ok(())
}
}
3. Instruction 架构
// Instruction = Context + 指令参数
use anchor_lang::prelude::*;
#[program]
pub mod pump_contract {
use super::*;
// 指令 1: 初始化
pub fn initialize(
ctx: Context<Initialize>,
fee: u64,
) -> Result<()> {
ctx.accounts.process(fee)
}
// 指令 2: 更新状态
pub fn update(
ctx: Context<Update>,
new_value: u64,
) -> Result<()> {
ctx.accounts.process(new_value)
}
}
// Context 定义账户需求
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = payer, space = 8 + 32 + 8)]
pub state: Account<'info, GlobalState>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut, has_one = authority)]
pub state: Account<'info, GlobalState>,
pub authority: Signer<'info>,
}
#[account]
pub struct GlobalState {
pub authority: Pubkey,
pub fee: u64,
}
impl<'info> Initialize<'info> {
pub fn process(&mut self, fee: u64) -> Result<()> {
let state = &mut self.state;
state.authority = self.payer.key();
state.fee = fee;
Ok(())
}
}
impl<'info> Update<'info> {
pub fn process(&mut self, new_value: u64) -> Result<()> {
let state = &mut self.state;
state.fee = new_value;
Ok(())
}
}
4. 权限检查模式
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct AdminOnly<'info> {
#[account(
mut,
constraint = config.authority == admin.key() @ CustomError::Unauthorized
)]
pub config: Account<'info, Config>,
pub admin: Signer<'info>,
}
#[derive(Accounts)]
pub struct TransferOwnership<'info> {
#[account(mut, has_one = owner)]
pub vault: Account<'info, Vault>,
pub owner: Signer<'info>,
}
#[account]
pub struct Config {
pub authority: Pubkey,
}
#[account]
pub struct Vault {
pub owner: Pubkey,
pub amount: u64,
}
#[error_code]
pub enum CustomError {
#[msg("Unauthorized access")]
Unauthorized,
}
5. 账户生命周期
use anchor_lang::prelude::*;
#[program]
pub mod account_lifecycle {
use super::*;
// 创建账户
pub fn create(ctx: Context<Create>) -> Result<()> {
let account = &mut ctx.accounts.state;
account.initialized = true;
msg!("Account created: {}", account.key());
Ok(())
}
// 修改账户
pub fn modify(ctx: Context<Modify>, new_value: u64) -> Result<()> {
let account = &mut ctx.accounts.state;
account.value = new_value;
msg!("Account modified");
Ok(())
}
// 关闭账户(收回租金)
pub fn close(ctx: Context<Close>) -> Result<()> {
msg!("Closing account, returning {} lamports to {}",
ctx.accounts.state.to_account_info().lamports(),
ctx.accounts.receiver.key()
);
Ok(())
}
}
#[derive(Accounts)]
pub struct Create<'info> {
#[account(init, payer = payer, space = 8 + 1 + 8)]
pub state: Account<'info, State>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Modify<'info> {
#[account(mut)]
pub state: Account<'info, State>,
}
#[derive(Accounts)]
pub struct Close<'info> {
#[account(mut, close = receiver)]
pub state: Account<'info, State>,
#[account(mut)]
pub receiver: SystemAccount<'info>,
}
#[account]
pub struct State {
pub initialized: bool,
pub value: u64,
}
⚠️ 常见问题
问题1:账户不属于程序
现象: error: Account owner must be the program in order to deserialize as this type
解决方案: 确保账户由程序所有
#[derive(Accounts)]
pub struct MyInstruction<'info> {
// 正确:指定 owner
#[account(owner = crate::ID)]
pub my_account: Account<'info, MyState>,
// 或使用 Anchor 的默认检查(已自动指定)
#[account]
pub state: Account<'info, GlobalState>,
}
问题2:PDA 碰撞
现象: 多个不同用户得到相同的 PDA
解决方案: 在 seed 中包含唯一标识符
// 不好:没有唯一标识
#[account(
seeds = [b"user"], // 所有用户相同!
bump
)]
// 好:包含唯一标识
#[account(
seeds = [b"user", user_pubkey.as_ref()], // 每个用户不同
bump
)]
📝 总结
✅ 理解了 Solana 的核心概念:
- 账户模型和账户结构
- Program Derived Address (PDA) 的作用
- Instruction 和 Context 的关系
- 权限检查和约束模式
- 账户生命周期管理
下一步: Day 03 将设计合约状态和数据结构
时间估计: 2-3 小时 | 难度: ⭐⭐ 中等 | 关键词: 账户模型、PDA、Instruction、权限管理