use {
crate::{
account::AccountMeta,
decode_error::DecodeError,
instruction::{Instruction, InstructionError},
pubkey::Pubkey,
stake::{
program::STAKE_PROGRAM_ID,
state::{Authorized, StakeAuthorize, StakeState},
},
system_instruction,
},
num_derive::{FromPrimitive, ToPrimitive},
serde_derive::{Deserialize, Serialize},
thiserror::Error,
};
#[derive(Error, Debug, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
pub enum StakeError {
#[error("not enough credits to redeem")]
NoCreditsToRedeem,
#[error("lockup has not yet expired")]
LockupInForce,
#[error("stake already deactivated")]
AlreadyDeactivated,
#[error("one re-delegation permitted per epoch")]
TooSoonToRedelegate,
#[error("split amount is more than is staked")]
InsufficientStake,
#[error("stake account with transient stake cannot be merged")]
MergeTransientStake,
#[error("stake account merge failed due to different authority, lockups or state")]
MergeMismatch,
#[error("custodian address not present")]
CustodianMissing,
#[error("custodian signature not present")]
CustodianSignatureMissing,
#[error("insufficient voting activity in the reference vote account")]
InsufficientReferenceVotes,
#[error("stake account is not delegated to the provided vote account")]
VoteAddressMismatch,
#[error(
"stake account has not been delinquent for the minimum epochs required for deactivation"
)]
MinimumDelinquentEpochsForDeactivationNotMet,
#[error("delegation amount is less than the minimum")]
InsufficientDelegation,
#[error("stake account with transient or inactive stake cannot be redelegated")]
RedelegateTransientOrInactiveStake,
#[error("stake redelegation to the same vote account is not permitted")]
RedelegateToSameVoteAccount,
#[error("redelegated stake must be fully activated before deactivation")]
RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted,
}
impl From<StakeError> for InstructionError {
fn from(error: StakeError) -> Self {
InstructionError::StakeError(error)
}
}
impl<E> DecodeError<E> for StakeError {
fn type_of() -> &'static str {
"StakeError"
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StakeInstruction {
Initialize(Authorized),
Authorize(Pubkey, StakeAuthorize),
DelegateStake,
Withdraw(u64),
Deactivate,
}
pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized) -> Instruction {
Instruction::new_with_bincode(
STAKE_PROGRAM_ID,
StakeInstruction::Initialize(*authorized),
vec![
AccountMeta::new(*stake_pubkey, false),
],
)
}
pub fn create_account(
from_pubkey: &Pubkey,
stake_pubkey: &Pubkey,
authorized: &Authorized,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_pubkey,
stake_pubkey,
lamports,
StakeState::size_of() as u64,
&STAKE_PROGRAM_ID,
),
initialize(stake_pubkey, authorized),
]
}
pub fn create_account_and_delegate_stake(
from_pubkey: &Pubkey,
stake_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
authorized: &Authorized,
lamports: u64,
) -> Vec<Instruction> {
let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lamports);
instructions.push(delegate_stake(
stake_pubkey,
&authorized.staker,
vote_pubkey,
));
instructions
}
pub fn authorize(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey,
stake_authorize: StakeAuthorize,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(*authorized_pubkey, true),
];
Instruction::new_with_bincode(
STAKE_PROGRAM_ID,
StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
account_metas,
)
}
pub fn delegate_stake(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(*vote_pubkey, false),
AccountMeta::new_readonly(*authorized_pubkey, true),
];
Instruction::new_with_bincode(
STAKE_PROGRAM_ID,
&StakeInstruction::DelegateStake,
account_metas,
)
}
pub fn withdraw(
stake_pubkey: &Pubkey,
withdrawer_pubkey: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*to_pubkey, false),
AccountMeta::new_readonly(*withdrawer_pubkey, true),
];
Instruction::new_with_bincode(
STAKE_PROGRAM_ID,
StakeInstruction::Withdraw(lamports),
account_metas,
)
}
pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(*authorized_pubkey, true),
];
Instruction::new_with_bincode(
STAKE_PROGRAM_ID,
&StakeInstruction::Deactivate,
account_metas,
)
}