use std::mem::size_of; use solana_program::instruction::{AccountMeta, Instruction}; use crate::error::StakingError; use crate::instruction::StakingInstruction::*; use crate::solana_program::{msg, program_error::ProgramError, pubkey::Pubkey, sysvar}; /// Instructions supported by the lending program. #[derive(Clone, Debug, PartialEq)] pub enum StakingInstruction { /// Accounts expected by this instruction: /// /// 0. `[writable]` Stake account - uninitialized. /// 1. `[]` Staking Pool. /// 2. `[]` Stake account owner. /// 3. `[]` Rent sysvar. CreateStakeAccount, /// Claim all unclaimed Reward from a stake account /// /// Accounts expected by this instruction: /// 0. `[signer]` Stake account owner. /// 1. `[writable]` Stake account. /// 2. `[writable]` Staking pool. /// 3. `[writable]` Reward token pool. /// 4. `[writable]` Reward destination. /// 5. `[]` Staking Pool owner derived from staking pool pubkey /// 6. `[]` Clock sysvar. /// 7. `[]` Token program. ClaimReward, } impl StakingInstruction { pub fn unpack(input: &[u8]) -> Result { input .split_first() .ok_or_else(|| StakingError::InstructionUnpackError.into()) .and_then(|(&tag, rest)| match tag { 1 => Ok((CreateStakeAccount, rest)), 4 => Ok((ClaimReward, rest)), _ => { msg!("Instruction cannot be unpacked"); Err(StakingError::InstructionUnpackError.into()) } }) .and_then(|(ins, rest)| { if rest.is_empty() { Ok(ins) } else { Err(StakingError::InstructionUnpackError.into()) } }) } pub fn pack(&self) -> Vec { let mut buf = Vec::with_capacity(size_of::()); match *self { Self::CreateStakeAccount => { buf.push(1); } Self::ClaimReward => { buf.push(4); } }; buf } } //helpers fn create_write_accounts(accounts: Vec) -> impl Iterator { accounts.into_iter().map(|acc| AccountMeta::new(acc, false)) } fn create_read_accounts(accounts: Vec) -> impl Iterator { accounts .into_iter() .map(|acc| AccountMeta::new_readonly(acc, false)) } pub fn create_stake_account( program_id: Pubkey, stake_account: Pubkey, staking_pool: Pubkey, stake_account_owner: Pubkey, ) -> Instruction { let read_accounts = create_read_accounts(vec![staking_pool, stake_account_owner, sysvar::rent::id()]); let accounts = vec![AccountMeta::new(stake_account, false)] .into_iter() .chain(read_accounts) .collect(); Instruction { program_id, accounts, data: StakingInstruction::CreateStakeAccount.pack(), } } pub fn claim_reward( program_id: Pubkey, stake_account_owner: Pubkey, stake_account: Pubkey, staking_pool: Pubkey, reward_token_pool: Pubkey, reward_destination: Pubkey, ) -> Instruction { let (staking_program_derived, _bump_seed) = Pubkey::find_program_address(&[staking_pool.as_ref()], &program_id); let write_accounts = create_write_accounts(vec![ stake_account, staking_pool, reward_token_pool, reward_destination, ]); let read_accounts = create_read_accounts(vec![ staking_program_derived, sysvar::clock::id(), spl_token::id(), ]); let accounts = vec![AccountMeta::new_readonly(stake_account_owner, true)] .into_iter() .chain(write_accounts) .chain(read_accounts) .collect(); Instruction { program_id, accounts, data: ClaimReward.pack(), } }