🥇 为用户铸造代币 - 让评论变成财富!
🎮 欢迎来到代币经济的世界!
嘿,Web3建设者们!👋 我们的电影评论程序已经很酷了,但现在要让它真正Web3化!想象一下:
- 📝 写影评 = 💰 获得代币
- 💬 发评论 = 🪙 赚取奖励
- 🎬 活跃用户 = 🏆 代币富翁
这就像StackOverflow遇见了加密货币!每个贡献都有价值,每个互动都能赚钱!🚀
🎯 今日任务: 集成SPL Token程序,为活跃用户铸造奖励代币!
🏗️ 项目准备 - 搭建你的代币工厂
📦 选择你的起点
你有两个选择:
# 🎯 选项1:继续使用本地环境
cd your-existing-project
# 🎯 选项2:克隆完整的起始代码
git clone https://github.com/buildspace/solana-movie-program/
cd solana-movie-program
git checkout solution-add-comments
💡 Pro Tip: 如果你想要一个干净的开始,也可以从这个Playground环境复制!
🎨 升级你的工具箱
打开 Cargo.toml
,添加魔法依赖:
[dependencies]
solana-program = "~1.10.29"
borsh = "0.9.3"
thiserror = "1.0.31"
# 🪙 新增的代币超能力!
spl-token = { version="3.2.0", features = [ "no-entrypoint" ] }
spl-associated-token-account = { version="=1.0.5", features = [ "no-entrypoint" ] }
🔍 这些是什么?
spl-token
= Solana的代币程序库(创建和管理代币)spl-associated-token-account
= 用户代币账户助手(自动管理用户钱包)
✅ 快速健康检查
# 🔨 测试构建
cargo build-sbf
# 看到绿色的 "Finished"了吗?太棒了!🎉
🪙 理解代币铸造 - 你的印钞机!
📚 什么是Token Mint?
┌─────────────────────────────────────┐
│ 🏭 Token Mint(代币铸造) │
├─────────────────────────────────────┤
│ │
│ 就像是你的代币工厂: │
│ │
│ • 📊 记录代币总供应量 │
│ • 🔑 控制谁能铸造新币 │
│ • 📈 追踪代币小数位数 │
│ • 🎯 定义代币的基本属性 │
│ │
│ 类比:中央银行的印钞机 🏦 │
└─────────────────────────────────────┘
📝 Step 1: 更新指令系统
🎯 更新instruction.rs
// 📋 添加新的指令类型
pub enum MovieInstruction {
// 🎬 添加电影评论
AddMovieReview {
title: String,
rating: u8,
description: String,
},
// ✏️ 更新电影评论
UpdateMovieReview {
title: String,
rating: u8,
description: String,
},
// 💬 添加评论
AddComment {
comment: String,
},
// 🆕 初始化代币铸造!
InitializeMint, // 不需要任何参数 - 简单明了!
}
🔄 更新解包函数
impl MovieInstruction {
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
// 🎯 获取指令类型(第一个字节)
let (&variant, rest) = input
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
// 🎭 根据类型解析不同的指令
Ok(match variant {
0 => {
// 🎬 处理添加评论
let payload = MovieReviewPayload::try_from_slice(rest).unwrap();
Self::AddMovieReview {
title: payload.title,
rating: payload.rating,
description: payload.description,
}
}
1 => {
// ✏️ 处理更新评论
let payload = MovieReviewPayload::try_from_slice(rest).unwrap();
Self::UpdateMovieReview {
title: payload.title,
rating: payload.rating,
description: payload.description,
}
}
2 => {
// 💬 处理添加评论
let payload = CommentPayload::try_from_slice(rest).unwrap();
Self::AddComment {
comment: payload.comment,
}
}
// 🪙 处理初始化铸造 - 超级简单!
3 => Self::InitializeMint,
_ => return Err(ProgramError::InvalidInstructionData),
})
}
}
🎮 Step 2: 更新处理器
📦 添加必要的导入
// processor.rs 顶部
use solana_program::{
// ... 现有的导入 ...
// 🆕 新增的系统级导入
sysvar::{rent::Rent, Sysvar, rent::ID as RENT_PROGRAM_ID},
native_token::LAMPORTS_PER_SOL, // SOL到lamports的转换
system_program::ID as SYSTEM_PROGRAM_ID // 系统程序ID
};
// 🪙 SPL Token相关导入
use spl_associated_token_account::get_associated_token_address;
use spl_token::{instruction::initialize_mint, ID as TOKEN_PROGRAM_ID};
🚦 更新路由函数
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
// 📦 解包指令
let instruction = MovieInstruction::unpack(instruction_data)?;
// 🎯 路由到对应的处理函数
match instruction {
MovieInstruction::AddMovieReview { title, rating, description } => {
msg!("🎬 处理添加电影评论...");
add_movie_review(program_id, accounts, title, rating, description)
},
MovieInstruction::UpdateMovieReview { title, rating, description } => {
msg!("✏️ 处理更新电影评论...");
update_movie_review(program_id, accounts, title, rating, description)
},
MovieInstruction::AddComment { comment } => {
msg!("💬 处理添加评论...");
add_comment(program_id, accounts, comment)
},
// 🆕 处理初始化铸造!
MovieInstruction::InitializeMint => {
msg!("🪙 初始化代币铸造...");
initialize_token_mint(program_id, accounts)
},
}
}
🏭 Step 3: 实现代币铸造功能
🎯 完整的初始化函数
pub fn initialize_token_mint(
program_id: &Pubkey,
accounts: &[AccountInfo]
) -> ProgramResult {
msg!("🏭 开始创建代币铸造工厂...");
// 📋 Step 1: 解析账户列表
// 账户顺序很重要!客户端会按这个顺序发送
let account_info_iter = &mut accounts.iter();
// 👤 谁发起了这个交易
let initializer = next_account_info(account_info_iter)?;
msg!("👤 初始化者: {}", initializer.key);
// 🪙 代币铸造PDA - 在客户端派生
let token_mint = next_account_info(account_info_iter)?;
// 🔑 代币铸造权限账户
let mint_auth = next_account_info(account_info_iter)?;
// ⚙️ 系统程序 - 用于创建新账户
let system_program = next_account_info(account_info_iter)?;
// 🎯 Solana Token程序地址
let token_program = next_account_info(account_info_iter)?;
// 📊 系统租金账户 - 用于计算租金
let sysvar_rent = next_account_info(account_info_iter)?;
// 🔐 Step 2: 派生并验证PDA
// 种子是 "token_mint" - 简单明了!
let (mint_pda, mint_bump) = Pubkey::find_program_address(
&[b"token_mint"], // 🌱 种子
program_id
);
// 派生铸造权限PDA
// 种子是 "token_auth"
let (mint_auth_pda, _mint_auth_bump) = Pubkey::find_program_address(
&[b"token_auth"], // 🌱 种子
program_id
);
msg!("🪙 Token mint地址: {:?}", mint_pda);
msg!("🔑 Mint权限地址: {:?}", mint_auth_pda);
// ✅ Step 3: 验证所有重要账户
if mint_pda != *token_mint.key {
msg!("❌ Token mint账户不匹配!");
return Err(ReviewError::IncorrectAccountError.into());
}
if *token_program.key != TOKEN_PROGRAM_ID {
msg!("❌ Token程序ID不正确!");
return Err(ReviewError::IncorrectAccountError.into());
}
if *mint_auth.key != mint_auth_pda {
msg!("❌ Mint权限账户不匹配!");
return Err(ReviewError::IncorrectAccountError.into());
}
if *system_program.key != SYSTEM_PROGRAM_ID {
msg!("❌ 系统程序ID不正确!");
return Err(ReviewError::IncorrectAccountError.into());
}
if *sysvar_rent.key != RENT_PROGRAM_ID {
msg!("❌ 租金程序ID不正确!");
return Err(ReviewError::IncorrectAccountError.into());
}
// 💰 Step 4: 计算租金
let rent = Rent::get()?;
// Mint账户的大小是82字节(记住这个数字!)
let rent_lamports = rent.minimum_balance(82);
msg!("💵 需要租金: {} lamports", rent_lamports);
// 🏗️ Step 5: 创建Token Mint PDA账户
msg!("🔨 创建token mint账户...");
invoke_signed(
// 创建账户的系统指令
&system_instruction::create_account(
initializer.key, // 💰 付款人
token_mint.key, // 🆕 新账户地址
rent_lamports, // 💵 租金
82, // 📏 账户大小(字节)
token_program.key, // 👮 账户所有者
),
// 涉及的账户
&[
initializer.clone(),
token_mint.clone(),
system_program.clone(),
],
// 🔑 PDA签名种子
&[&[b"token_mint", &[mint_bump]]],
)?;
msg!("✅ Token mint账户创建成功!");
// 🎉 Step 6: 初始化mint账户
msg!("🎯 初始化mint账户...");
invoke_signed(
// SPL Token的初始化mint指令
&initialize_mint(
token_program.key, // Token程序
token_mint.key, // Mint账户
mint_auth.key, // Mint权限
Option::None, // 冻结权限 - 我们不需要冻结功能!
9, // 小数位数(9 = 像SOL一样)
)?,
// 涉及的账户
&[
token_mint.clone(),
sysvar_rent.clone(),
mint_auth.clone()
],
// 🔑 PDA签名种子
&[&[b"token_mint", &[mint_bump]]],
)?;
msg!("🎊 Token mint初始化完成!");
msg!("🏭 代币工厂已经开始运作!");
Ok(())
}
💡 关键概念解析:
- PDA(程序派生地址) = 只有程序能控制的特殊地址
- Bump Seed = 确保地址不在Ed25519曲线上的魔法数字
- 租金 = 在Solana上存储数据需要支付的费用
🚨 Step 4: 添加错误处理
⚠️ 更新error.rs
#[derive(Debug, Error)]
pub enum ReviewError {
// 😴 账户未初始化
#[error("Account not initialized yet")]
UninitializedAccount,
// 🔍 PDA不匹配
#[error("PDA derived does not equal PDA passed in")]
InvalidPDA,
// 📏 数据太长
#[error("Input data exceeds max length")]
InvalidDataLength,
// ⭐ 评分无效
#[error("Rating greater than 5 or less than 1")]
InvalidRating,
// 🆕 账户不匹配错误
#[error("Accounts do not match")]
IncorrectAccountError, // 用于验证账户时
}
🚀 部署和测试
🔨 构建和部署
# 🧹 Step 1: 清理旧的部署文件
# 在文件浏览器中删除 target/deploy 文件夹中的密钥对
# 🔨 Step 2: 构建程序
cargo build-sbf
# 🚀 Step 3: 部署到本地网络
# 复制控制台输出的部署命令并运行
# 💰 如果余额不足
solana airdrop 2
🧪 设置测试客户端
# 📥 Step 1: 克隆测试客户端
git clone https://github.com/buildspace/solana-movie-token-client
cd solana-movie-token-client
# 📦 Step 2: 安装依赖
npm install
⚙️ 配置客户端
-
更新程序ID 📝
// index.ts
const PROGRAM_ID = "你的程序ID"; -
切换到本地网络 🌐
// 第67行
const connection = new web3.Connection("http://localhost:8899"); -
开启日志监控 👀
# 在新的终端窗口
solana logs 你的程序ID
🎮 运行测试
# 🚀 启动测试脚本
npm start
# 🎉 你应该看到类似这样的输出:
# "Created token mint account"
# "Initialized token mint"
# "Token mint initialization complete!"
💡 实用技巧与最佳实践
🎯 技巧1:调试技巧
// 🔍 添加详细的调试信息
msg!("🔍 调试信息:");
msg!(" 程序ID: {}", program_id);
msg!(" Mint PDA: {}", mint_pda);
msg!(" Bump: {}", mint_bump);
🎯 技巧2:错误处理模式
// 🛡️ 优雅的错误处理
fn validate_accounts(
expected: &Pubkey,
actual: &Pubkey,
error_msg: &str
) -> ProgramResult {
if expected != actual {
msg!("❌ {}", error_msg);
return Err(ReviewError::IncorrectAccountError.into());
}
Ok(())
}
🎯 技巧3:租金计算助手
// 💰 通用租金计算函数
fn calculate_rent(size: usize) -> Result<u64, ProgramError> {
let rent = Rent::get()?;
Ok(rent.minimum_balance(size))
}
🎓 知识总结
📚 你学到了什么?
┌─────────────────────────────────────┐
│ 🏆 成就解锁 │
├─────────────────────────────────────┤
│ ✅ 理解Token Mint概念 │
│ ✅ 集成SPL Token程序 │
│ ✅ 创建和初始化Mint账户 │
│ ✅ 使用PDA控制代币 │
│ ✅ 处理跨程序调用(CPI) │
└─────────────────────────────────────┘
🔮 接下来是什么?
下一步我们将:
- 🪙 为用户创建代币账户
- 💰 实现代币铸造逻辑
- 🎁 为评论和留言发放奖励
- 📊 创建代币经济系统
🚀 总结
恭喜!🎉 你已经成功创建了你的第一个代币铸造工厂!现在你的程序有了自己的货币系统,可以奖励活跃用户了!
💭 思考: 这只是开始!想象一下你可以用代币做什么:
- 🏪 创建市场
- 🗳️ 投票权重
- 🎮 游戏化体验
- 💎 VIP功能
准备好铸造你的第一批代币了吗?让我们继续前进! 🚀💰✨