前言:
前面我们介绍了 solana 关于 anchor 智能合约的环境搭建 配置,简单编写 发布 部署 调用 的一些落地。等等。接下来我们借着案例。详细剖析下智能合约编写。
案例 journal 介绍:
这是一个关于 日志的一个区块链应用。作者可以 将自己的日志 记录于 区块链:
智能合约的分析:
很多类似的操作 和 固定写法我们 之前的章节 有更详细的讲解。我们这里 着重 讲解下 这这个章节中学到的 新的姿势:
更新 日记,因为
solana 对于链上 pda 使用空间费用 是按照 实际占用的空间大小进行计算的,所以如果改变日记 内容。会造成 pda 账户 租金的变化。这里需要注意
rust// 更新日记 pub fn update_journal_entry(ctx:Context<UpdateEntry>,title:String,message:String) -> Result<()> { let journal_entry = &mut ctx.accounts.journal_entry; journal_entry.message = message; Ok(()) } // 对应结构体 #[derive(Accounts)] #[instruction(title:String)] pub struct UpdateEntry<'info> { #[account(mut)] pub owner: Signer<'info>, // 这里不是创建 而是查找,并且要修改 // solana 对于链上 pda 使用空间费用 是按照实际占用的空间大小进行计算的, // 这里的两个更新 会涉及两个 String 类型占用空间大小的改变。因此需要重新计算 费用,可能是 增加,也有可能是减小 // 所以 这里的 account 宏 使用 realloc 约束,超出的需要付费,减少的 会 退回 // realloc::zero = true,//原始空间设置为0 然后重新计算 大小 #[account( mut, seeds = [title.as_bytes(), owner.key().as_ref()], bump, realloc = 8 + JournalEntryState::INIT_SPACE, realloc::payer = owner, realloc::zero = true, )] pub journal_entry:Account<'info, JournalEntryState>, pub system_program: Program<'info, System>, }
删除日志,删除 pda 回收账户资金
rust// 删除日记 pub fn delete_journal_entry(ctx:Context<DeleteEntry>,title:String) -> Result<()> { Ok(()) } //对应结构体 #[derive(Accounts)] #[instruction(title:String)] pub struct DeleteEntry<'info> { #[account(mut)] pub owner: Signer<'info>, // close 指定删除的 pda 必须是自己 也就是 payer,回收账户租金 #[account( mut, seeds = [title.as_bytes(), owner.key().as_ref()], bump, close = owner, )] pub journal_entry:Account<'info, JournalEntryState>, pub system_program: Program<'info, System>, }
完整脚本:
rust
use anchor_lang::prelude::*;
declare_id!("5U8DEe5jUTsTAWZBRmD6aRRnFwoDQSNHBqE9H93R2aXA");
#[program]
pub mod anchor_crud {
use super::*;
// 创建日记
pub fn create_journal_entry(ctx: Context<CreateEntry>,title:String,message:String) -> Result<()> {
//通过 CreateEntry 结构体指令 定位到 pad 上下文 进行赋值
let journal_entry = &mut ctx.accounts.journal_entry;
journal_entry.title = title;
journal_entry.message = message;
journal_entry.owner = ctx.accounts.owner.key();
Ok(())
}
// 更新日记
pub fn update_journal_entry(ctx:Context<UpdateEntry>,title:String,message:String) -> Result<()> {
let journal_entry = &mut ctx.accounts.journal_entry;
journal_entry.message = message;
Ok(())
}
// 删除日记
pub fn delete_journal_entry(ctx:Context<DeleteEntry>,title:String) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
#[instruction(title:String)]
pub struct CreateEntry<'info> {
#[account(mut)]
pub owner: Signer<'info>,
#[account(
init,
payer = owner,
space = 8 + JournalEntryState::INIT_SPACE,
seeds = [title.as_bytes(), owner.key().as_ref()],
bump,
)]
pub journal_entry:Account<'info, JournalEntryState>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
#[instruction(title:String)]
pub struct UpdateEntry<'info> {
#[account(mut)]
pub owner: Signer<'info>,
// 这里不是创建 而是查找,并且要修改
// solana 对于链上 pda 使用空间费用 是按照实际占用的空间大小进行计算的,
// 这里的两个更新 会涉及两个 String 类型占用空间大小的改变。因此需要重新计算 费用,可能是 增加,也有可能是减小
// 所以 这里的 account 宏 使用 realloc 约束,超出的需要付费,减少的 会 退回
// realloc::zero = true,//原始空间设置为0 然后重新计算 大小
#[account(
mut,
seeds = [title.as_bytes(), owner.key().as_ref()],
bump,
realloc = 8 + JournalEntryState::INIT_SPACE,
realloc::payer = owner,
realloc::zero = true,
)]
pub journal_entry:Account<'info, JournalEntryState>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
#[instruction(title:String)]
pub struct DeleteEntry<'info> {
#[account(mut)]
pub owner: Signer<'info>,
// close 指定删除的 pda 必须是自己 也就是 payer,回收账户租金
#[account(
mut,
seeds = [title.as_bytes(), owner.key().as_ref()],
bump,
close = owner,
)]
pub journal_entry:Account<'info, JournalEntryState>,
pub system_program: Program<'info, System>,
}
#[account]
#[derive(InitSpace)]
pub struct JournalEntryState {
pub owner: Pubkey,
#[max_len(50)]
pub title: String,
#[max_len(1000)]
pub message: String,
}