在 Solana 程序中创建一个钱包来保存 SOL,程序可以用它来接受 SOL 并支付它

Job*_*odd 6 javascript rust solana

我在 Solana 区块链上有一个程序,运行良好,但是我现在想为该程序集成一个功能,以接收 SOL 将其保存在帐户中,然后根据需要将其发送给用户。从文档来看,我似乎需要使用带有种子短语和凹凸的 PDA。这就是我到目前为止所创建的。

use anchor_lang::prelude::*;
use anchor_lang::AccountsClose;

declare_id!("Cq1TSA1obVQZzw2YYxvFN6Q5ia5TYxSbwyZQ9JwQCbBL");


#[program]
pub mod myepicproject {
    use anchor_lang::solana_program::{program::invoke, system_instruction::transfer};

    use super::*;
    pub fn start_stuff_off(ctx: Context<StartStuffOff>) -> ProgramResult {
        // get a referance to the account 
        let base_account = &mut ctx.accounts.base_account;
        // initliase total_count. 
        base_account.total_pools = 0;
        Ok(())
    }
    // old name which I don't want to change as used in other places function is add_pool 
    pub fn add_gif(ctx: Context<AddGif>, image_link: String, pool_name: String, pool_desc: String, win_opt:String, close: u32, verify:String, fee:u8) -> ProgramResult {
        let base_account = &mut ctx.accounts.base_account;
        let wins: Vec<String> = win_opt.split(';').map(|s| s.trim().to_string()).collect(); //chars().filter(|c| !c.is_whitespace()).collect()
        // make a program address which will hold the SOL for this pool 
        let pool_wallet = &ctx.accounts.pool_wallet;
        let pool = PoolStruct{
            pool_wallet: pool_wallet.to_account_info().key.to_string(),
            pool_id: base_account.total_pools,
            image_link: image_link.to_string(),
            user_address: *base_account.to_account_info().key,
            pool_name: pool_name.to_string(), 
            pool_balance: 0,
            pool_description: pool_desc.to_string(),
            win_options: wins, 
            close_date_time: close, 
            verify_url:verify.to_string(),
            owner_fee: fee,
            result: "".to_string(), 
            closed: false, 
            entries: Vec::new()
        };
        base_account.pool_list.push(pool);
        base_account.total_pools += 1;
        Ok(())
    }

    pub fn add_result(ctx: Context<AddGif>,result:String, pool_id:u32 ) -> ProgramResult{
        //TODO: Make this only callable by the pool owner. 
        let base_account = &mut ctx.accounts.base_account;
        let mut i = 0; 
        let mut found = false;
        for p in &base_account.pool_list {
            if p.pool_id == pool_id {
                found = true;
                break;
            } 
            i += 1;
        }
        if found {
            base_account.pool_list[i].closed = true;
            base_account.pool_list[i].result = result.to_string();
        };
        Ok(())
    }

    pub fn place_bet(ctx: Context<AddGif>, pred: String, pool_id:u32, stake_bal:u32, user:String) -> ProgramResult {
        let base_account = &mut ctx.accounts.base_account;
        let pool_wallet = &mut ctx.accounts.pool_wallet;
        // TODO: check prediction is one of possible options 
        // TODO: Add payment to this function 
        // TODO: make sure today is before the close date. 
        let bet = EntryStruct{
            user: user,
            prediction: pred,
            stake_bal: stake_bal
        };
        let mut i = 0;
        let mut found = false;
        for p in &base_account.pool_list{
            if p.pool_id == pool_id {
                found = true;
                break;
            }
            i += 1;
        };
        if found{
            let sb = stake_bal as u64;
            let account_lamports = **pool_wallet.to_account_info().lamports.borrow();
            let transfer_amount = sb.checked_sub(account_lamports).ok_or(0)?;

            if transfer_amount > 0 {
                invoke( 
                    &transfer(
                        ctx.accounts.user.to_account_info().key,
                        pool_wallet.to_account_info().key,
                        transfer_amount,
                ),
                &[
                    ctx.accounts.user.to_account_info(),
                    pool_wallet.to_account_info(),
                    ctx.accounts.system_program.to_account_info()
                ],    
            )?;
            }
            base_account.pool_list[i].pool_balance += stake_bal as u64;
            base_account.pool_list[i].entries.push(bet);
        }
        Ok(())
    }
}

#[derive(Accounts)]
pub struct StartStuffOff<'info> {
    #[account(init, payer = user, space= 10240)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(pool_name: String, item_name: String, bounty: u64)]
pub struct AddGif<'info> {
    #[account(mut)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(init, seeds=[pool_name.as_bytes(),b"pool_wallet"], space=9000, bump = 5, payer=user)]
    pub pool_wallet: Account<'info, PoolWallet>,
    // #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>
}



#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct PoolStruct{
    pub pool_wallet: String,
    pub pool_id: u32,
    pub image_link: String,
    pub user_address: Pubkey,
    pub pool_name: String, 
    pub pool_balance: u64, 
    pub pool_description: String,
    pub win_options: Vec<String>,
    pub close_date_time: u32, 
    pub verify_url: String, 
    pub owner_fee: u8,
    // TODO: to allow for pools with more then 1 winning result perhaps result should be an array?
    pub result: String,
    pub closed: bool,
    pub entries: Vec<EntryStruct>
}

#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct EntryStruct{
    pub user: String,
    pub prediction: String,
    pub stake_bal: u32,
}

//Tell solana we want to store on this account 
#[account]
pub struct BaseAccount {
    pub total_pools: u32,
    pub pool_list: Vec<PoolStruct>,
}

#[account]
pub struct PoolWallet{
    pub balance: u64 
} 
Run Code Online (Sandbox Code Playgroud)

我还创建了以下 JS 测试。

const anchor = require('@project-serum/anchor');

const { SystemProgram, LAMPORTS_PER_SOL } = anchor.web3;

const main = async() => {
  console.log(" Starting test...");
  
  const provider = anchor.Provider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.Myepicproject;

  const baseAccount = anchor.web3.Keypair.generate();
  

  const tx = await program.rpc.startStuffOff({
    accounts: {
      baseAccount: baseAccount.publicKey,
      user: provider.wallet.publicKey,
      systemProgram: SystemProgram.programId
    },
    signers: [baseAccount],
  });

  console.log("Your transaction signiture", tx);

  let account = await program.account.baseAccount.fetch(baseAccount.publicKey);
  console.log(' Total Pools', account.totalPools.toString())

  //call add_gif
  const poolWallet = anchor.web3.Keypair.generate();
  await program.rpc.addGif("insert image linke here", "Test Pool","This is a test Pool", "option 1; option 2", 9897, "Verify here", 5,{
    
    accounts: {
      baseAccount: baseAccount.publicKey,
      poolWallet: poolWallet.publicKey,
      user:provider.wallet.publicKey,
      systemProgram: SystemProgram.programId
    },
    signers: [baseAccount],
  });
  await program.rpc.addGif("insert image linke here 3", "Test Pool 2","This is a test Pool 2", "2option 1; 2option 2", 789897, "2Verify here", 2,{
    accounts: {
      baseAccount: baseAccount.publicKey,
    }
  });
  await program.rpc.addResult("winner 1",0,{
    accounts: {
      baseAccount: baseAccount.publicKey,
    }
  });
  await program.rpc.placeBet("winner 1",1,125,"user",  {
    accounts: {
      baseAccount: baseAccount.publicKey,
    }
  })

  account = await program.account.baseAccount.fetch(baseAccount.publicKey);
  console.log("Total Pools count ", account.totalPools.toString())

  console.log("Pool list: ", account.poolList)

};

const runMain = async () => {
  try{
    await main();
    process.exit(0);
  }catch (error){
    console.error(error);
  }
};

runMain();
Run Code Online (Sandbox Code Playgroud)

测试因错误而失败。

Translating error Error: unknown signer:

这个问题是我不知道我是否在 Rust 程序代码中犯了错误,或者这是否正常但我的测试代码有问题。有人能确认程序代码是否有意义吗?

小智 0

您需要signer标签,例如:#[account(signer)]上面base_account