我想要将solana的任何spl token能够通过inbox转移到当前程序账户下,然后任何token都能通过outbox从当前程序账户下转移到用户,我的代码好像是有误的,希望老师们能帮忙解决下,solana小白。
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_instruction;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};
#[account]
pub struct MappingMakerToBool {
pub mappings: Vec<PubkeyBoolPair>,
}
impl MappingMakerToBool {
pub fn set(&mut self, key: Pubkey, value: bool) {
if let Some(pair) = self.mappings.iter_mut().find(|pair| pair.key == key) {
pair.value = value;
} else {
self.mappings.push(PubkeyBoolPair { key, value });
}
}
pub fn get(&self, key: Pubkey) -> Option<bool> {
self.mappings
.iter()
.find(|pair| pair.key == key)
.map(|pair| pair.value)
}
}
#[program]
mod test {
//初始化
pub fn initialize(
ctx: Context<Initialize>,
owner: Pubkey,
fee: u64,
fee_receiver: Pubkey,
paused: bool,
) -> Result<()> {
let init_state = &mut ctx.accounts.state;
// require!(
// ctx.accounts.user.key().to_string() == INIT_OPERATOR,
// ErrorCode::NoPermission
// );
// 确保只能调用一次
require!(!init_state.initialized, ErrorCode::AlreadyInitialized);
let (pda, bump) = Pubkey::find_program_address(
&[b"authority", init_state.key().as_ref()],
ctx.program_id,
);
// PDA seeds and bump to "sign" for CPI
// let seeds = b"authority";
// let bump = ctx.bumps.reward_token_mint;
// let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
require!(fee <= 10000, ErrorCode::FeeOver10000);
init_state.bump = bump;
init_state.program_pda = pda;
init_state.owner = owner;
init_state.fee = fee;
init_state.fee_receiver = fee_receiver;
init_state.paused = paused;
init_state.initialized = true;
Ok(())
}
pub fn set_maker_list(
ctx: Context<SetMakerList>,
makers: Vec<Pubkey>,
status: Vec<bool>,
) -> Result<()> {
let state = &mut ctx.accounts.state;
require!(
state.owner == ctx.accounts.user.key(),
ErrorCode::NoPermission
);
require!(makers.len() == status.len(), ErrorCode::InvalidLength);
let maker_valid = &mut ctx.accounts.maker_valid;
for (i, &maker) in makers.iter().enumerate() {
maker_valid.set(maker, status[i]);
}
emit!(ChangeMakers {
state_key: state.key(),
new_makers: makers,
});
Ok(())
}
pub fn inbox(ctx: Context<Inbox>, amount: u64, data: Vec<u8>) -> Result<()> {
let state = &ctx.accounts.state;
require!(!state.paused, ErrorCode::ContractPaused);
let maker_valid = &mut ctx.accounts.maker_valid;
let if_maker_valid = maker_valid.get(ctx.accounts.user.key()).unwrap_or(false);
require!(if_maker_valid, ErrorCode::NotMaker);
let required_fee = state.fee;
let fee_amount = amount
.checked_mul(required_fee)
.and_then(|v| v.checked_div(10000))
.ok_or(ErrorCode::InsufficientAmount)?;
let real_in_amount = amount.checked_sub(fee_amount).ok_or(ErrorCode::Overflow)?;
require!(fee_amount <= real_in_amount, ErrorCode::InvalidFeeAmount);
//Transfer fee to feeReceiver
if fee_amount > 0 {
// Transfer fee to feeReceiver
let cpi_accounts = Transfer {
from: ctx.accounts.source.to_account_info(),
to: ctx.accounts.fee_destination.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, fee_amount)?;
emit!(InboxFeeEvent {
token: ctx.accounts.contract_token_account.key(),
sender: ctx.accounts.user.key(),
fee_receiver: state.fee_receiver.key(),
fee_amount: fee_amount,
});
}
// Transfer tokens inbox(state)
let cpi_accounts = Transfer {
from: ctx.accounts.contract_token_account.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, real_in_amount)?;
emit!(InboxEvent {
token: ctx.accounts.contract_token_account.key(),
sender: ctx.accounts.user.key(),
receiver: ctx.accounts.destination.key(),
amount: real_in_amount,
data: data,
});
Ok(())
}
pub fn outbox(ctx: Context<Outbox>, amount: u64, data: Vec<u8>) -> Result<()> {
let state = &ctx.accounts.state;
require!(!state.paused, ErrorCode::ContractPaused);
let maker_valid = &mut ctx.accounts.maker_valid;
let if_maker_valid = maker_valid.get(ctx.accounts.user.key()).unwrap_or(false);
require!(if_maker_valid, ErrorCode::NotMaker);
//Token Transfer
// let seeds = &[&[state.bump]];
// let signer_seeds = &[&seeds[..]];
// let seeds = b"authority";
// let bump = state.bump;
// let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
let state_key = state.key();
let seeds = &[b"authority", state_key.as_ref(), &[state.bump]];
let signer_seeds = &[&seeds[..]];
let cpi_accounts = Transfer {
from: ctx.accounts.source.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.contract_authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);
token::transfer(cpi_ctx, amount)?;
emit!(OutboxEvent {
token: ctx.accounts.contract_token_account.key(),
sender: ctx.accounts.source.key(),
receiver: ctx.accounts.destination.key(),
amount: amount,
data: data,
});
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 256)]
pub state: Account<'info, InitializeState>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SetMakerList<'info> {
pub state: Account<'info, InitializeState>,
#[account(init, payer = user, space = 8 + 1024)]
pub maker_valid: Account<'info, MappingMakerToBool>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Inbox<'info> {
pub state: Account<'info, InitializeState>,
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub source: Account<'info, TokenAccount>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
#[account(mut)]
pub fee_destination: Account<'info, TokenAccount>,
#[account(mut)]
pub contract_token_account: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
pub maker_valid: Account<'info, MappingMakerToBool>,
}
#[derive(Accounts)]
pub struct Outbox<'info> {
pub state: Account<'info, InitializeState>,
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub contract_token_account: Account<'info, TokenAccount>,
#[account(mut)]
pub source: Account<'info, TokenAccount>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
#[account(seeds = [b"authority", state.key().as_ref()],bump = state.bump)]
pub contract_authority: AccountInfo<'info>,
pub token_program: Program<'info, Token>,
pub maker_valid: Account<'info, MappingMakerToBool>,
}
#[account]
pub struct InitializeState {
pub bump: u8,
pub program_pda: Pubkey,
pub owner: Pubkey,
pub paused: bool,
pub fee: u64,
pub fee_receiver: Pubkey,
pub initialized: bool,
}
#[error_code]
pub enum ErrorCode {
#[msg("No permission to perform this action.")]
NoPermission,
#[msg("Initialization has been performed.")]
AlreadyInitialized,
#[msg("Insufficient amount.")]
InsufficientAmount,
#[msg("Contract is paused.")]
ContractPaused,
#[msg("Invalid length.")]
InvalidLength,
#[msg("Invalid fee amount")]
InvalidFeeAmount,
#[msg("Overflow")]
Overflow,
#[msg("Not a maker.")]
NotMaker,
#[msg("Fee over 10000.")]
FeeOver10000,
#[msg("Not a manager")]
NotManager,
}
#[event]
pub struct OwnerChangeEvent {
pub old_owner: Pubkey,
pub new_owner: Pubkey,
}
#[event]
pub struct ChangeMakers {
pub state_key: Pubkey,
pub new_makers: Vec<Pubkey>,
}
#[event]
pub struct InboxEvent {
pub token: Pubkey,
pub sender: Pubkey,
pub receiver: Pubkey,
pub amount: u64,
pub data: Vec<u8>,
}
#[event]
pub struct InboxFeeEvent {
pub token: Pubkey,
pub sender: Pubkey,
pub fee_receiver: Pubkey,
pub fee_amount: u64,
}
#[event]
pub struct OutboxEvent {
pub token: Pubkey,
pub sender: Pubkey,
pub receiver: Pubkey,
pub amount: u64,
pub data: Vec<u8>,
}