基于Solana Anchor框架实现双人竞猜合约的技术咨询
双人SOL竞猜合约实现方案(Anchor框架)
核心逻辑拆解
要实现你需要的功能,核心是用**PDA(程序派生账户)**作为SOL的托管容器,分两个阶段处理:
- 存入阶段:两名玩家分别向PDA转入等额SOL,合约记录存入状态
- 结算阶段:触发条件满足后,将PDA内的总SOL转给指定获胜者
具体实现步骤
1. 定义游戏状态结构体
在Anchor程序里定义GameAccount,用来存储游戏关键信息:
- 约定的单份投注金额
- 两名玩家的公钥
- 双方的存入状态
- 获胜者公钥
- 游戏是否已结算
2. 初始化游戏指令
创建PDA作为托管账户,初始化游戏状态,这里用玩家A的公钥作为PDA种子的一部分,确保每个游戏实例唯一。
3. 玩家存入指令
- 校验游戏未结算、当前玩家未存入
- 若玩家是未登记的第二名玩家,自动登记其公钥
- 通过CPI调用系统程序,将玩家的SOL转入PDA
- 更新对应玩家的存入状态
4. 结算指令
- 校验游戏未结算且双方已完成存入
- 校验获胜者是两名玩家之一
- 将PDA内的所有SOL(两份投注金额之和)转给获胜者
- 标记游戏已结算
关键代码片段
use anchor_lang::prelude::*; declare_id!("YourProgramIDHere"); #[program] pub mod two_player_bet { use super::*; // 初始化游戏,玩家A发起,指定投注金额 pub fn initialize(ctx: Context<Initialize>, bet_amount: u64) -> Result<()> { let game = &mut ctx.accounts.game; game.bet_amount = bet_amount; game.player_a = ctx.accounts.player.key(); game.player_b = Pubkey::default(); game.a_deposited = false; game.b_deposited = false; game.winner = Pubkey::default(); game.settled = false; Ok(()) } // 玩家存入SOL pub fn deposit(ctx: Context<Deposit>) -> Result<()> { let game = &mut ctx.accounts.game; let player = ctx.accounts.player.key(); // 基础状态校验 require!(!game.settled, GameError::GameSettled); // 处理玩家身份 if player == game.player_a { require!(!game.a_deposited, GameError::AlreadyDeposited); game.a_deposited = true; } else if game.player_b == Pubkey::default() { game.player_b = player; game.b_deposited = true; } else if player == game.player_b { require!(!game.b_deposited, GameError::AlreadyDeposited); game.b_deposited = true; } else { return err!(GameError::InvalidPlayer); } // 转账SOL到PDA anchor_lang::system_program::transfer( CpiContext::new( ctx.accounts.system_program.to_account_info(), anchor_lang::solana_program::system_instruction::transfer( &player, &game.key(), game.bet_amount, ), ), )?; Ok(()) } // 结算游戏,转总金额给获胜者 pub fn settle(ctx: Context<Settle>, winner: Pubkey) -> Result<()> { let game = &mut ctx.accounts.game; // 校验条件 require!(!game.settled, GameError::GameSettled); require!(game.a_deposited && game.b_deposited, GameError::NotAllDeposited); require!(winner == game.player_a || winner == game.player_b, GameError::InvalidWinner); // 转移总金额给获胜者 let total = game.bet_amount * 2; let game_lamports = &mut ctx.accounts.game.to_account_info().lamports.borrow_mut(); let winner_lamports = &mut ctx.accounts.winner.lamports.borrow_mut(); *game_lamports -= total; *winner_lamports += total; // 更新状态 game.winner = winner; game.settled = true; Ok(()) } } // 账户结构体定义 #[derive(Accounts)] pub struct Initialize<'info> { #[account( init, payer = player, space = 8 + 8 + 32 + 32 + 1 + 1 + 32 + 1, seeds = [b"bet", player.key().as_ref()], bump )] pub game: Account<'info, GameAccount>, #[account(mut)] pub player: Signer<'info>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Deposit<'info> { #[account(mut, seeds = [b"bet", game.player_a.as_ref()], bump)] pub game: Account<'info, GameAccount>, #[account(mut)] pub player: Signer<'info>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Settle<'info> { #[account(mut, seeds = [b"bet", game.player_a.as_ref()], bump)] pub game: Account<'info, GameAccount>, #[account(mut)] pub winner: AccountInfo<'info>, } #[account] pub struct GameAccount { pub bet_amount: u64, // 单份投注金额(Lamports) pub player_a: Pubkey, // 玩家A公钥 pub player_b: Pubkey, // 玩家B公钥 pub a_deposited: bool, // 玩家A是否已存入 pub b_deposited: bool, // 玩家B是否已存入 pub winner: Pubkey, // 获胜者公钥 pub settled: bool, // 游戏是否已结算 } // 错误码定义 #[error_code] pub enum GameError { #[msg("游戏已结算")] GameSettled, #[msg("该玩家已完成存入")] AlreadyDeposited, #[msg("无效玩家")] InvalidPlayer, #[msg("未完成全部存入")] NotAllDeposited, #[msg("无效获胜者")] InvalidWinner, }
重点注意事项
- PDA安全性:必须用固定的seeds生成PDA,确保只有合约能控制该账户的资金
- 状态校验:每个指令都要严格校验游戏状态,防止重复存入、提前结算等非法操作
- 转账处理:直接操作账户的lamports时要确保金额计算正确,或者用Anchor提供的
transferCPI宏简化操作 - 权限控制:如果结算需要特定权限(比如裁判、或者双方签名确认),可以在
Settle账户结构体中添加对应的签名者校验
学习资源推荐
- Anchor官方文档:重点研读PDA派生账户和CPI调用章节,理解账户初始化、签名验证的底层逻辑
- Solana官方编程模型:掌握Solana账户模型、Lamport转移的核心规则
- Anchor官方示例项目:从
basic-0到basic-4的基础示例,学习状态管理和指令编写的规范
内容的提问来源于stack exchange,提问作者dev1800
相关产品推荐
相关产品推荐

