Stake pool improvements and fixes (#665)
* Added address type in programm address generation for the stake pool, renamed nonce to bump seed * Formatting fixed * Bump seed calculation moved to the smart contract, test for fee > 1 added, state length public constant added * Added claim method to stake pool, fixed program address generation in token mint and burn calls * Refactored signers management when calling other contracts * Signers formation put back into calling functions, deposit/withdraw/claim method reworked, state serialization bug fixed
This commit is contained in:
parent
5ceeefca5a
commit
df63d6a0f0
|
@ -40,6 +40,9 @@ pub enum Error {
|
||||||
/// The calculation failed.
|
/// The calculation failed.
|
||||||
#[error("CalculationFailure")]
|
#[error("CalculationFailure")]
|
||||||
CalculationFailure,
|
CalculationFailure,
|
||||||
|
/// Stake pool fee > 1.
|
||||||
|
#[error("FeeTooHigh")]
|
||||||
|
FeeTooHigh,
|
||||||
}
|
}
|
||||||
impl From<Error> for ProgramError {
|
impl From<Error> for ProgramError {
|
||||||
fn from(e: Error) -> Self {
|
fn from(e: Error) -> Self {
|
||||||
|
|
|
@ -25,11 +25,6 @@ pub struct Fee {
|
||||||
pub struct InitArgs {
|
pub struct InitArgs {
|
||||||
/// Fee paid to the owner in pool tokens
|
/// Fee paid to the owner in pool tokens
|
||||||
pub fee: Fee,
|
pub fee: Fee,
|
||||||
/// Nonce used for the deposit program address
|
|
||||||
pub deposit_bump_seed: u8,
|
|
||||||
/// Nonce used for the withdraw program address
|
|
||||||
/// This program address is used as the stake withdraw key as well
|
|
||||||
pub withdraw_bump_seed: u8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instructions supported by the StakePool program.
|
/// Instructions supported by the StakePool program.
|
||||||
|
@ -48,29 +43,42 @@ pub enum StakePoolInstruction {
|
||||||
/// Deposit some stake into the pool. The output is a "pool" token representing ownership
|
/// Deposit some stake into the pool. The output is a "pool" token representing ownership
|
||||||
/// into the pool. Inputs are converted to the current ratio.
|
/// into the pool. Inputs are converted to the current ratio.
|
||||||
///
|
///
|
||||||
/// 0. `[]` StakePool
|
/// 0. `[w]` Stake pool
|
||||||
/// 1. `[]` deposit authority
|
/// 1. `[]` Stake pool deposit authority
|
||||||
/// 2. `[]` withdraw authority
|
/// 2. `[]` Stake pool withdraw authority
|
||||||
/// 3. `[w]` Stake, deposit authority is set as the withdrawal key
|
/// 3. `[w]` Stake account to join the pool (withdraw should be set to stake pool deposit)
|
||||||
/// 4. `[w]` Pool MINT account, authority is the owner.
|
/// 4. `[w]` User account to receive pool tokens
|
||||||
/// 5. `[w]` Pool Account to deposit the generated tokens.
|
/// 5. `[w]` Account to receive pool fee tokens
|
||||||
/// 6. `[w]` Pool Account to deposit the generated fee for owner.
|
/// 6. `[w]` Pool token mint account
|
||||||
/// 7. `[]` Token program id
|
/// 7. `[]` Pool token program id
|
||||||
Deposit,
|
Deposit,
|
||||||
|
|
||||||
/// Withdraw the token from the pool at the current ratio.
|
/// Withdraw the token from the pool at the current ratio.
|
||||||
/// The amount withdrawn is the MIN(u64, stake size)
|
/// The amount withdrawn is the MIN(u64, stake size)
|
||||||
///
|
///
|
||||||
/// 0. `[]` StakePool
|
/// 0. `[w]` Stake pool
|
||||||
/// 1. `[]` withdraw authority
|
/// 1. `[]` Stake pool withdraw authority
|
||||||
/// 2. `[w]` SOURCE Pool account, amount is transferable by authority
|
/// 2. `[w]` Stake account to split
|
||||||
/// 3. `[w]` Pool MINT account, authority is the owner
|
/// 3. `[w]` Unitialized stake account to receive withdrawal
|
||||||
/// 4. `[w]` Stake SOURCE owned by the withdraw authority
|
/// 4. `[]` User account to set as a new withdraw authority
|
||||||
/// 6. `[w]` Stake destination, uninitialized, for the user stake
|
/// 5. `[w]` User account with pool tokens to burn from
|
||||||
/// 7. `[]` Token program id
|
/// 6. `[w]` Pool token mint account
|
||||||
|
/// 7. `[]` Pool token program id
|
||||||
/// userdata: amount to withdraw
|
/// userdata: amount to withdraw
|
||||||
Withdraw(u64),
|
Withdraw(u64),
|
||||||
|
|
||||||
|
/// Claim ownership of a whole stake account.
|
||||||
|
/// Also burns enough tokens to make up for the stake account balance
|
||||||
|
///
|
||||||
|
/// 0. `[w]` Stake pool
|
||||||
|
/// 1. `[]` Stake pool withdraw authority
|
||||||
|
/// 2. `[w]` Stake account to claim
|
||||||
|
/// 3. `[]` User account to set as a new withdraw authority
|
||||||
|
/// 4. `[w]` User account with pool tokens to burn from
|
||||||
|
/// 5. `[w]` Pool token mint account
|
||||||
|
/// 6. `[]` Pool token program id
|
||||||
|
Claim,
|
||||||
|
|
||||||
/// Update the staking pubkey for a stake
|
/// Update the staking pubkey for a stake
|
||||||
///
|
///
|
||||||
/// 0. `[w]` StakePool
|
/// 0. `[w]` StakePool
|
||||||
|
@ -106,8 +114,9 @@ impl StakePoolInstruction {
|
||||||
let val: &u64 = unpack(input)?;
|
let val: &u64 = unpack(input)?;
|
||||||
Self::Withdraw(*val)
|
Self::Withdraw(*val)
|
||||||
}
|
}
|
||||||
3 => Self::SetStakingAuthority,
|
3 => Self::Claim,
|
||||||
4 => Self::SetOwner,
|
4 => Self::SetStakingAuthority,
|
||||||
|
5 => Self::SetOwner,
|
||||||
_ => return Err(ProgramError::InvalidAccountData),
|
_ => return Err(ProgramError::InvalidAccountData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -132,12 +141,15 @@ impl StakePoolInstruction {
|
||||||
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) };
|
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) };
|
||||||
*value = *val;
|
*value = *val;
|
||||||
}
|
}
|
||||||
Self::SetStakingAuthority => {
|
Self::Claim => {
|
||||||
output[0] = 3;
|
output[0] = 3;
|
||||||
}
|
}
|
||||||
Self::SetOwner => {
|
Self::SetStakingAuthority => {
|
||||||
output[0] = 4;
|
output[0] = 4;
|
||||||
}
|
}
|
||||||
|
Self::SetOwner => {
|
||||||
|
output[0] = 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,18 +40,29 @@ impl Processor {
|
||||||
)
|
)
|
||||||
.or(Err(Error::InvalidProgramAddress))
|
.or(Err(Error::InvalidProgramAddress))
|
||||||
}
|
}
|
||||||
|
/// Generates seed bump for stake pool authorities
|
||||||
|
pub fn find_authority_bump_seed(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
my_info: &Pubkey,
|
||||||
|
authority_type: &[u8],
|
||||||
|
) -> u8 {
|
||||||
|
let (_pubkey, bump_seed) =
|
||||||
|
Pubkey::find_program_address(&[&my_info.to_bytes()[..32], authority_type], program_id);
|
||||||
|
bump_seed
|
||||||
|
}
|
||||||
|
|
||||||
/// Issue a stake_split instruction.
|
/// Issue a stake_split instruction.
|
||||||
pub fn stake_split<'a>(
|
pub fn stake_split<'a>(
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
stake_account: AccountInfo<'a>,
|
stake_account: AccountInfo<'a>,
|
||||||
authority: AccountInfo<'a>,
|
authority: AccountInfo<'a>,
|
||||||
nonce: u8,
|
authority_type: &[u8],
|
||||||
|
bump_seed: u8,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
split_stake: AccountInfo<'a>,
|
split_stake: AccountInfo<'a>,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let me_bytes = stake_pool.to_bytes();
|
let me_bytes = stake_pool.to_bytes();
|
||||||
let authority_signature_seeds = [&me_bytes[..32], &[nonce]];
|
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
|
||||||
let signers = &[&authority_signature_seeds[..]];
|
let signers = &[&authority_signature_seeds[..]];
|
||||||
|
|
||||||
let ix = stake::split_only(stake_account.key, authority.key, amount, split_stake.key);
|
let ix = stake::split_only(stake_account.key, authority.key, amount, split_stake.key);
|
||||||
|
@ -64,12 +75,13 @@ impl Processor {
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
stake_account: AccountInfo<'a>,
|
stake_account: AccountInfo<'a>,
|
||||||
authority: AccountInfo<'a>,
|
authority: AccountInfo<'a>,
|
||||||
nonce: u8,
|
authority_type: &[u8],
|
||||||
|
bump_seed: u8,
|
||||||
new_staker: &Pubkey,
|
new_staker: &Pubkey,
|
||||||
staker_auth: stake::StakeAuthorize,
|
staker_auth: stake::StakeAuthorize,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let me_bytes = stake_pool.to_bytes();
|
let me_bytes = stake_pool.to_bytes();
|
||||||
let authority_signature_seeds = [&me_bytes[..32], &[nonce]];
|
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
|
||||||
let signers = &[&authority_signature_seeds[..]];
|
let signers = &[&authority_signature_seeds[..]];
|
||||||
|
|
||||||
let ix = stake::authorize(stake_account.key, authority.key, new_staker, staker_auth);
|
let ix = stake::authorize(stake_account.key, authority.key, new_staker, staker_auth);
|
||||||
|
@ -78,17 +90,19 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issue a spl_token `Burn` instruction.
|
/// Issue a spl_token `Burn` instruction.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn token_burn<'a>(
|
pub fn token_burn<'a>(
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
token_program: AccountInfo<'a>,
|
token_program: AccountInfo<'a>,
|
||||||
burn_account: AccountInfo<'a>,
|
burn_account: AccountInfo<'a>,
|
||||||
mint: AccountInfo<'a>,
|
mint: AccountInfo<'a>,
|
||||||
authority: AccountInfo<'a>,
|
authority: AccountInfo<'a>,
|
||||||
nonce: u8,
|
authority_type: &[u8],
|
||||||
|
bump_seed: u8,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let me_bytes = stake_pool.to_bytes();
|
let me_bytes = stake_pool.to_bytes();
|
||||||
let authority_signature_seeds = [&me_bytes[..32], &[nonce]];
|
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
|
||||||
let signers = &[&authority_signature_seeds[..]];
|
let signers = &[&authority_signature_seeds[..]];
|
||||||
|
|
||||||
let ix = spl_token::instruction::burn(
|
let ix = spl_token::instruction::burn(
|
||||||
|
@ -108,18 +122,21 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issue a spl_token `MintTo` instruction.
|
/// Issue a spl_token `MintTo` instruction.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn token_mint_to<'a>(
|
pub fn token_mint_to<'a>(
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
token_program: AccountInfo<'a>,
|
token_program: AccountInfo<'a>,
|
||||||
mint: AccountInfo<'a>,
|
mint: AccountInfo<'a>,
|
||||||
destination: AccountInfo<'a>,
|
destination: AccountInfo<'a>,
|
||||||
authority: AccountInfo<'a>,
|
authority: AccountInfo<'a>,
|
||||||
nonce: u8,
|
authority_type: &[u8],
|
||||||
|
bump_seed: u8,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let me_bytes = stake_pool.to_bytes();
|
let me_bytes = stake_pool.to_bytes();
|
||||||
let authority_signature_seeds = [&me_bytes[..32], &[nonce]];
|
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
|
||||||
let signers = &[&authority_signature_seeds[..]];
|
let signers = &[&authority_signature_seeds[..]];
|
||||||
|
|
||||||
let ix = spl_token::instruction::mint_to(
|
let ix = spl_token::instruction::mint_to(
|
||||||
token_program.key,
|
token_program.key,
|
||||||
mint.key,
|
mint.key,
|
||||||
|
@ -134,7 +151,7 @@ impl Processor {
|
||||||
|
|
||||||
/// Processes an [Initialize](enum.Instruction.html).
|
/// Processes an [Initialize](enum.Instruction.html).
|
||||||
pub fn process_initialize(
|
pub fn process_initialize(
|
||||||
_program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
init: InitArgs,
|
init: InitArgs,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
|
@ -145,14 +162,28 @@ impl Processor {
|
||||||
let owner_fee_info = next_account_info(account_info_iter)?;
|
let owner_fee_info = next_account_info(account_info_iter)?;
|
||||||
let token_program_info = next_account_info(account_info_iter)?;
|
let token_program_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
|
// Stake pool account should not be already initialized
|
||||||
if State::Unallocated != State::deserialize(&stake_pool_info.data.borrow())? {
|
if State::Unallocated != State::deserialize(&stake_pool_info.data.borrow())? {
|
||||||
return Err(Error::AlreadyInUse.into());
|
return Err(Error::AlreadyInUse.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
||||||
|
if init.fee.numerator > init.fee.denominator {
|
||||||
|
return Err(Error::FeeTooHigh.into());
|
||||||
|
}
|
||||||
|
|
||||||
let stake_pool = State::Init(StakePool {
|
let stake_pool = State::Init(StakePool {
|
||||||
owner: *owner_info.key,
|
owner: *owner_info.key,
|
||||||
deposit_bump_seed: init.deposit_bump_seed,
|
deposit_bump_seed: Self::find_authority_bump_seed(
|
||||||
withdraw_bump_seed: init.withdraw_bump_seed,
|
program_id,
|
||||||
|
stake_pool_info.key,
|
||||||
|
Self::AUTHORITY_DEPOSIT,
|
||||||
|
),
|
||||||
|
withdraw_bump_seed: Self::find_authority_bump_seed(
|
||||||
|
program_id,
|
||||||
|
stake_pool_info.key,
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
|
),
|
||||||
pool_mint: *pool_mint_info.key,
|
pool_mint: *pool_mint_info.key,
|
||||||
owner_fee_account: *owner_fee_info.key,
|
owner_fee_account: *owner_fee_info.key,
|
||||||
token_program_id: *token_program_info.key,
|
token_program_id: *token_program_info.key,
|
||||||
|
@ -163,16 +194,24 @@ impl Processor {
|
||||||
stake_pool.serialize(&mut stake_pool_info.data.borrow_mut())
|
stake_pool.serialize(&mut stake_pool_info.data.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an [Withdraw](enum.Instruction.html).
|
/// Processes [Deposit](enum.Instruction.html).
|
||||||
pub fn process_deposit(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
pub fn process_deposit(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
// Stake pool
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake pool deposit authority
|
||||||
let deposit_info = next_account_info(account_info_iter)?;
|
let deposit_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake pool withdraw authority
|
||||||
let withdraw_info = next_account_info(account_info_iter)?;
|
let withdraw_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake account to join the pool (withdraw should be set to stake pool deposit)
|
||||||
let stake_info = next_account_info(account_info_iter)?;
|
let stake_info = next_account_info(account_info_iter)?;
|
||||||
let pool_mint_info = next_account_info(account_info_iter)?;
|
// User account to receive pool tokens
|
||||||
let dest_user_info = next_account_info(account_info_iter)?;
|
let dest_user_info = next_account_info(account_info_iter)?;
|
||||||
|
// Account to receive pool fee tokens
|
||||||
let owner_fee_info = next_account_info(account_info_iter)?;
|
let owner_fee_info = next_account_info(account_info_iter)?;
|
||||||
|
// Pool token mint account
|
||||||
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
|
// Pool token program id
|
||||||
let token_program_info = next_account_info(account_info_iter)?;
|
let token_program_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
let mut stake_pool = State::deserialize(&stake_pool_info.data.borrow())?.stake_pool()?;
|
let mut stake_pool = State::deserialize(&stake_pool_info.data.borrow())?.stake_pool()?;
|
||||||
|
@ -223,6 +262,7 @@ impl Processor {
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
stake_info.clone(),
|
stake_info.clone(),
|
||||||
deposit_info.clone(),
|
deposit_info.clone(),
|
||||||
|
Self::AUTHORITY_DEPOSIT,
|
||||||
stake_pool.deposit_bump_seed,
|
stake_pool.deposit_bump_seed,
|
||||||
withdraw_info.key,
|
withdraw_info.key,
|
||||||
stake::StakeAuthorize::Withdrawer,
|
stake::StakeAuthorize::Withdrawer,
|
||||||
|
@ -235,7 +275,8 @@ impl Processor {
|
||||||
pool_mint_info.clone(),
|
pool_mint_info.clone(),
|
||||||
dest_user_info.clone(),
|
dest_user_info.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
stake_pool.withdraw_bump_seed,
|
Self::AUTHORITY_DEPOSIT,
|
||||||
|
stake_pool.deposit_bump_seed,
|
||||||
user_amount,
|
user_amount,
|
||||||
)?;
|
)?;
|
||||||
let fee_amount = <u64>::try_from(fee_amount).or(Err(Error::CalculationFailure))?;
|
let fee_amount = <u64>::try_from(fee_amount).or(Err(Error::CalculationFailure))?;
|
||||||
|
@ -245,6 +286,7 @@ impl Processor {
|
||||||
pool_mint_info.clone(),
|
pool_mint_info.clone(),
|
||||||
owner_fee_info.clone(),
|
owner_fee_info.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
fee_amount as u64,
|
fee_amount as u64,
|
||||||
)?;
|
)?;
|
||||||
|
@ -255,19 +297,28 @@ impl Processor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an [Withdraw](enum.Instruction.html).
|
/// Processes [Withdraw](enum.Instruction.html).
|
||||||
pub fn process_withdraw(
|
pub fn process_withdraw(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_amount: u64,
|
stake_amount: u64,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
// Stake pool
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake pool withdraw authority
|
||||||
let withdraw_info = next_account_info(account_info_iter)?;
|
let withdraw_info = next_account_info(account_info_iter)?;
|
||||||
let source_info = next_account_info(account_info_iter)?;
|
// Stake account to split
|
||||||
|
let stake_split_from = next_account_info(account_info_iter)?;
|
||||||
|
// Unitialized stake account to receive withdrawal
|
||||||
|
let stake_split_to = next_account_info(account_info_iter)?;
|
||||||
|
// User account to set as a new withdraw authority
|
||||||
|
let user_stake_authority = next_account_info(account_info_iter)?;
|
||||||
|
// User account with pool tokens to burn from
|
||||||
|
let burn_from_info = next_account_info(account_info_iter)?;
|
||||||
|
// Pool token mint account
|
||||||
let pool_mint_info = next_account_info(account_info_iter)?;
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
let stake_info = next_account_info(account_info_iter)?;
|
// Pool token program id
|
||||||
let dest_user_info = next_account_info(account_info_iter)?;
|
|
||||||
let token_program_info = next_account_info(account_info_iter)?;
|
let token_program_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
let mut stake_pool = State::deserialize(&stake_pool_info.data.borrow())?.stake_pool()?;
|
let mut stake_pool = State::deserialize(&stake_pool_info.data.borrow())?.stake_pool()?;
|
||||||
|
@ -293,28 +344,31 @@ impl Processor {
|
||||||
|
|
||||||
Self::stake_split(
|
Self::stake_split(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
source_info.clone(),
|
stake_split_from.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
stake_amount,
|
stake_amount,
|
||||||
stake_info.clone(),
|
stake_split_to.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Self::stake_authorize(
|
Self::stake_authorize(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
stake_info.clone(),
|
stake_split_to.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
dest_user_info.key,
|
user_stake_authority.key,
|
||||||
stake::StakeAuthorize::Withdrawer,
|
stake::StakeAuthorize::Withdrawer,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Self::token_burn(
|
Self::token_burn(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
token_program_info.clone(),
|
token_program_info.clone(),
|
||||||
source_info.clone(),
|
burn_from_info.clone(),
|
||||||
pool_mint_info.clone(),
|
pool_mint_info.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
pool_amount,
|
pool_amount,
|
||||||
)?;
|
)?;
|
||||||
|
@ -324,7 +378,73 @@ impl Processor {
|
||||||
State::Init(stake_pool).serialize(&mut stake_pool_info.data.borrow_mut())?;
|
State::Init(stake_pool).serialize(&mut stake_pool_info.data.borrow_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Processes an [SetStakeAuthority](enum.Instruction.html).
|
/// Processes [Claim](enum.Instruction.html).
|
||||||
|
pub fn process_claim(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
// Stake pool
|
||||||
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake pool withdraw authority
|
||||||
|
let withdraw_info = next_account_info(account_info_iter)?;
|
||||||
|
// Stake account to claim
|
||||||
|
let stake_to_claim = next_account_info(account_info_iter)?;
|
||||||
|
// User account to set as a new withdraw authority
|
||||||
|
let user_stake_authority = next_account_info(account_info_iter)?;
|
||||||
|
// User account with pool tokens to burn from
|
||||||
|
let burn_from_info = next_account_info(account_info_iter)?;
|
||||||
|
// Pool token account
|
||||||
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
|
// Pool token program id
|
||||||
|
let token_program_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
|
let mut stake_pool = State::deserialize(&stake_pool_info.data.borrow())?.stake_pool()?;
|
||||||
|
|
||||||
|
if *withdraw_info.key
|
||||||
|
!= Self::authority_id(
|
||||||
|
program_id,
|
||||||
|
stake_pool_info.key,
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.withdraw_bump_seed,
|
||||||
|
)?
|
||||||
|
{
|
||||||
|
return Err(Error::InvalidProgramAddress.into());
|
||||||
|
}
|
||||||
|
if stake_pool.token_program_id != *token_program_info.key {
|
||||||
|
return Err(Error::InvalidInput.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let stake_amount = **stake_to_claim.lamports.borrow();
|
||||||
|
let pool_amount = stake_pool
|
||||||
|
.calc_pool_withdraw_amount(stake_amount)
|
||||||
|
.ok_or(Error::CalculationFailure)?;
|
||||||
|
let pool_amount = <u64>::try_from(pool_amount).or(Err(Error::CalculationFailure))?;
|
||||||
|
|
||||||
|
Self::stake_authorize(
|
||||||
|
stake_pool_info.key,
|
||||||
|
stake_to_claim.clone(),
|
||||||
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.withdraw_bump_seed,
|
||||||
|
user_stake_authority.key,
|
||||||
|
stake::StakeAuthorize::Withdrawer,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Self::token_burn(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
burn_from_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.withdraw_bump_seed,
|
||||||
|
pool_amount,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
stake_pool.pool_total -= pool_amount;
|
||||||
|
stake_pool.stake_total -= stake_amount;
|
||||||
|
State::Init(stake_pool).serialize(&mut stake_pool_info.data.borrow_mut())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Processes [SetStakeAuthority](enum.Instruction.html).
|
||||||
pub fn process_set_staking_auth(
|
pub fn process_set_staking_auth(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
|
@ -355,10 +475,12 @@ impl Processor {
|
||||||
{
|
{
|
||||||
return Err(Error::InvalidProgramAddress.into());
|
return Err(Error::InvalidProgramAddress.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::stake_authorize(
|
Self::stake_authorize(
|
||||||
stake_info.key,
|
stake_pool_info.key,
|
||||||
stake_info.clone(),
|
stake_info.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
|
Self::AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.withdraw_bump_seed,
|
||||||
staker_info.key,
|
staker_info.key,
|
||||||
stake::StakeAuthorize::Staker,
|
stake::StakeAuthorize::Staker,
|
||||||
|
@ -366,7 +488,7 @@ impl Processor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an [SetOwner](enum.Instruction.html).
|
/// Processes [SetOwner](enum.Instruction.html).
|
||||||
pub fn process_set_owner(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
pub fn process_set_owner(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -387,7 +509,7 @@ impl Processor {
|
||||||
State::Init(stake_pool).serialize(&mut stake_pool_info.data.borrow_mut())?;
|
State::Init(stake_pool).serialize(&mut stake_pool_info.data.borrow_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Processes an [Instruction](enum.Instruction.html).
|
/// Processes [Instruction](enum.Instruction.html).
|
||||||
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
|
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
|
||||||
let instruction = StakePoolInstruction::deserialize(input)?;
|
let instruction = StakePoolInstruction::deserialize(input)?;
|
||||||
match instruction {
|
match instruction {
|
||||||
|
@ -403,6 +525,10 @@ impl Processor {
|
||||||
info!("Instruction: Withdraw");
|
info!("Instruction: Withdraw");
|
||||||
Self::process_withdraw(program_id, amount, accounts)
|
Self::process_withdraw(program_id, amount, accounts)
|
||||||
}
|
}
|
||||||
|
StakePoolInstruction::Claim => {
|
||||||
|
info!("Instruction: Claim");
|
||||||
|
Self::process_claim(program_id, accounts)
|
||||||
|
}
|
||||||
StakePoolInstruction::SetStakingAuthority => {
|
StakePoolInstruction::SetStakingAuthority => {
|
||||||
info!("Instruction: SetStakingAuthority");
|
info!("Instruction: SetStakingAuthority");
|
||||||
Self::process_set_staking_auth(program_id, accounts)
|
Self::process_set_staking_auth(program_id, accounts)
|
||||||
|
@ -476,6 +602,7 @@ impl PrintProgramError for Error {
|
||||||
Error::InvalidInput => info!("Error: InvalidInput"),
|
Error::InvalidInput => info!("Error: InvalidInput"),
|
||||||
Error::InvalidOutput => info!("Error: InvalidOutput"),
|
Error::InvalidOutput => info!("Error: InvalidOutput"),
|
||||||
Error::CalculationFailure => info!("Error: CalculationFailure"),
|
Error::CalculationFailure => info!("Error: CalculationFailure"),
|
||||||
|
Error::FeeTooHigh => info!("Error: FeeTooHigh"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -627,10 +754,8 @@ mod tests {
|
||||||
&pool_token_key,
|
&pool_token_key,
|
||||||
&TOKEN_PROGRAM_ID,
|
&TOKEN_PROGRAM_ID,
|
||||||
InitArgs {
|
InitArgs {
|
||||||
deposit_bump_seed: 0,
|
|
||||||
withdraw_bump_seed: 0,
|
|
||||||
fee: Fee {
|
fee: Fee {
|
||||||
denominator: 1,
|
denominator: 10,
|
||||||
numerator: 2,
|
numerator: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,10 +12,10 @@ pub struct StakePool {
|
||||||
/// Owner authority
|
/// Owner authority
|
||||||
/// allows for updating the staking authority
|
/// allows for updating the staking authority
|
||||||
pub owner: Pubkey,
|
pub owner: Pubkey,
|
||||||
/// Deposit authority nonce
|
/// Deposit authority bump seed
|
||||||
/// for `create_program_address(&[state::StakePool account, "deposit"])`
|
/// for `create_program_address(&[state::StakePool account, "deposit"])`
|
||||||
pub deposit_bump_seed: u8,
|
pub deposit_bump_seed: u8,
|
||||||
/// Withdrawal authority nonce
|
/// Withdrawal authority bump seed
|
||||||
/// for `create_program_address(&[state::StakePool account, "withdrawal"])`
|
/// for `create_program_address(&[state::StakePool account, "withdrawal"])`
|
||||||
pub withdraw_bump_seed: u8,
|
pub withdraw_bump_seed: u8,
|
||||||
/// Pool Mint
|
/// Pool Mint
|
||||||
|
@ -67,6 +67,8 @@ pub enum State {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
/// Length of state data when serialized
|
||||||
|
pub const LEN: usize = size_of::<u8>() + size_of::<StakePool>();
|
||||||
/// Deserializes a byte buffer into a [State](struct.State.html).
|
/// Deserializes a byte buffer into a [State](struct.State.html).
|
||||||
/// TODO efficient unpacking here
|
/// TODO efficient unpacking here
|
||||||
pub fn deserialize(input: &[u8]) -> Result<State, ProgramError> {
|
pub fn deserialize(input: &[u8]) -> Result<State, ProgramError> {
|
||||||
|
@ -76,7 +78,7 @@ impl State {
|
||||||
Ok(match input[0] {
|
Ok(match input[0] {
|
||||||
0 => State::Unallocated,
|
0 => State::Unallocated,
|
||||||
1 => {
|
1 => {
|
||||||
let swap: &StakePool = unpack(input)?;
|
let swap: &StakePool = unpack(&input[1..])?;
|
||||||
State::Init(*swap)
|
State::Init(*swap)
|
||||||
}
|
}
|
||||||
_ => return Err(ProgramError::InvalidAccountData),
|
_ => return Err(ProgramError::InvalidAccountData),
|
||||||
|
|
Loading…
Reference in New Issue