From 3970847e75bbade82641b83d3e2fbad6faeb7375 Mon Sep 17 00:00:00 2001 From: Han Yang Date: Sun, 25 Sep 2022 02:11:12 +0800 Subject: [PATCH] stake (#2195) --- Cargo.lock | 1 + spl/Cargo.toml | 2 + spl/src/lib.rs | 3 + spl/src/stake.rs | 166 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 spl/src/stake.rs diff --git a/Cargo.lock b/Cargo.lock index 1d963050..c620a52e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -277,6 +277,7 @@ name = "anchor-spl" version = "0.25.0" dependencies = [ "anchor-lang", + "borsh", "mpl-token-metadata", "serum_dex", "solana-program", diff --git a/spl/Cargo.toml b/spl/Cargo.toml index 32fc6fbf..85f7fa76 100644 --- a/spl/Cargo.toml +++ b/spl/Cargo.toml @@ -14,12 +14,14 @@ token = ["spl-token"] associated_token = ["spl-associated-token-account"] governance = [] shmem = [] +stake = ["borsh"] devnet = [] metadata = ["mpl-token-metadata"] dex = ["serum_dex"] [dependencies] anchor-lang = { path = "../lang", version = "0.25.0", features = ["derive"] } +borsh = { version = "^0.9", optional = true } serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "1be91f2", version = "0.4.0", features = ["no-entrypoint"], optional = true } solana-program = "~1.10.29" spl-token = { version = "~3.3.0", features = ["no-entrypoint"], optional = true } diff --git a/spl/src/lib.rs b/spl/src/lib.rs index 5ba8cc41..252b43ed 100644 --- a/spl/src/lib.rs +++ b/spl/src/lib.rs @@ -16,5 +16,8 @@ pub mod governance; #[cfg(feature = "shmem")] pub mod shmem; +#[cfg(feature = "stake")] +pub mod stake; + #[cfg(feature = "metadata")] pub mod metadata; diff --git a/spl/src/stake.rs b/spl/src/stake.rs new file mode 100644 index 00000000..ce0659c4 --- /dev/null +++ b/spl/src/stake.rs @@ -0,0 +1,166 @@ +use anchor_lang::{ + context::CpiContext, + solana_program::{ + account_info::AccountInfo, + pubkey::Pubkey, + stake::{ + self, + program::ID, + state::{StakeAuthorize, StakeState}, + }, + }, + Accounts, Result, +}; +use borsh::BorshDeserialize; +use std::ops::Deref; + +// CPI functions + +pub fn authorize<'info>( + ctx: CpiContext<'_, '_, '_, 'info, Authorize<'info>>, + stake_authorize: StakeAuthorize, + custodian: Option>, +) -> Result<()> { + let ix = stake::instruction::authorize( + ctx.accounts.stake.key, + ctx.accounts.authorized.key, + ctx.accounts.new_authorized.key, + stake_authorize, + custodian.as_ref().map(|c| c.key), + ); + let mut account_infos = vec![ + ctx.accounts.stake, + ctx.accounts.clock, + ctx.accounts.authorized, + ]; + if let Some(c) = custodian { + account_infos.push(c); + } + solana_program::program::invoke_signed(&ix, &account_infos, ctx.signer_seeds) + .map_err(Into::into) +} + +pub fn withdraw<'info>( + ctx: CpiContext<'_, '_, '_, 'info, Withdraw<'info>>, + amount: u64, + custodian: Option>, +) -> Result<()> { + let ix = stake::instruction::withdraw( + ctx.accounts.stake.key, + ctx.accounts.withdrawer.key, + ctx.accounts.to.key, + amount, + custodian.as_ref().map(|c| c.key), + ); + let mut account_infos = vec![ + ctx.accounts.stake, + ctx.accounts.to, + ctx.accounts.clock, + ctx.accounts.stake_history, + ctx.accounts.withdrawer, + ]; + if let Some(c) = custodian { + account_infos.push(c); + } + solana_program::program::invoke_signed(&ix, &account_infos, ctx.signer_seeds) + .map_err(Into::into) +} + +pub fn deactivate_stake<'info>( + ctx: CpiContext<'_, '_, '_, 'info, DeactivateStake<'info>>, +) -> Result<()> { + let ix = stake::instruction::deactivate_stake(ctx.accounts.stake.key, ctx.accounts.staker.key); + solana_program::program::invoke_signed( + &ix, + &[ctx.accounts.stake, ctx.accounts.clock, ctx.accounts.staker], + ctx.signer_seeds, + ) + .map_err(Into::into) +} + +// CPI accounts + +#[derive(Accounts)] +pub struct Authorize<'info> { + /// The stake account to be updated + pub stake: AccountInfo<'info>, + + /// The existing authority + pub authorized: AccountInfo<'info>, + + /// The new authority to replace the existing authority + pub new_authorized: AccountInfo<'info>, + + /// Clock sysvar + pub clock: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct Withdraw<'info> { + /// The stake account to be updated + pub stake: AccountInfo<'info>, + + /// The stake account's withdraw authority + pub withdrawer: AccountInfo<'info>, + + /// Account to send withdrawn lamports to + pub to: AccountInfo<'info>, + + /// Clock sysvar + pub clock: AccountInfo<'info>, + + /// StakeHistory sysvar + pub stake_history: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct DeactivateStake<'info> { + /// The stake account to be deactivated + pub stake: AccountInfo<'info>, + + /// The stake account's stake authority + pub staker: AccountInfo<'info>, + + /// Clock sysvar + pub clock: AccountInfo<'info>, +} + +// State + +#[derive(Clone)] +pub struct StakeAccount(StakeState); + +impl anchor_lang::AccountDeserialize for StakeAccount { + fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result { + Self::try_deserialize_unchecked(buf) + } + + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + StakeState::deserialize(buf).map(Self).map_err(Into::into) + } +} + +impl anchor_lang::AccountSerialize for StakeAccount {} + +impl anchor_lang::Owner for StakeAccount { + fn owner() -> Pubkey { + ID + } +} + +impl Deref for StakeAccount { + type Target = StakeState; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone)] +pub struct Stake; + +impl anchor_lang::Id for Stake { + fn id() -> Pubkey { + ID + } +}