Skip to content

Commit

Permalink
Lockup realization trait
Browse files Browse the repository at this point in the history
  • Loading branch information
armaniferrante committed Feb 6, 2021
1 parent 48b27e6 commit bffba39
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 7 deletions.
39 changes: 39 additions & 0 deletions examples/lockup/programs/lockup/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub mod lockup {
Ok(())
}

#[access_control(is_realized(&ctx))]
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
// Has the given amount vested?
if amount
Expand Down Expand Up @@ -242,6 +243,8 @@ impl<'info> CreateVesting<'info> {
}
}

// All accounts not included here, i.e., the "remaining accounts" should be
// ordered according to the realization interface.
#[derive(Accounts)]
pub struct Withdraw<'info> {
// Vesting.
Expand Down Expand Up @@ -327,6 +330,13 @@ pub struct Vesting {
pub whitelist_owned: u64,
/// Signer nonce.
pub nonce: u8,
/// The program that determines when the locked account is **realized**.
/// In addition to the lockup schedule, the program provides the ability
/// for applications to determine when locked tokens are considered earned.
/// For example, when earning locked tokens via the staking program, one
/// cannot receive the tokens until they unstake. As a result, if one never
/// unstakes, then one would never actually receives the locked tokens.
pub lock_realizor: Pubkey,
}

#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Default, Copy, Clone)]
Expand Down Expand Up @@ -366,6 +376,10 @@ pub enum ErrorCode {
WhitelistEntryNotFound,
#[msg("You do not have sufficient permissions to perform this action.")]
Unauthorized,
#[msg("You are unable to claim projected rewards until ")]
UnableToWithdrawWhileStaked,
#[msg("The given lock realizor doesn't match the vesting account.")]
InvalidLockRealizor,
}

impl<'a, 'b, 'c, 'info> From<&mut CreateVesting<'info>>
Expand Down Expand Up @@ -456,3 +470,28 @@ fn whitelist_auth(lockup: &Lockup, ctx: &Context<Auth>) -> Result<()> {
}
Ok(())
}

// Returns Ok if the locked vesting account has been fully realized. Realization
// is application dependent. For example, in the case of staking, one must first
// unstake before being able to earn locked tokens.
fn is_realized<'info>(ctx: &Context<Withdraw>) -> Result<()> {
let cpi_program = {
let p = ctx.remaining_accounts[0].clone();
if p.key != &ctx.accounts.vesting.lock_realizor {
return Err(ErrorCode::InvalidLockRealizor.into());
}
p
};
let cpi_accounts = ctx.remaining_accounts.to_vec()[1..].to_vec();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

// realize_lock::cpi::is_realized(cpi_ctx).map_err(Into::into)
Ok(())
}

/*
#[cpi]
pub trait RealizeLock {
fn is_realized<'info, T: Accounts<'info>>(ctx: Context<T>) -> ProgramResult;
}
*/
4 changes: 2 additions & 2 deletions lang/attribute/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anchor_syn::codegen::program as program_codegen;
use anchor_syn::parser::program as program_parser;
use syn::parse_macro_input;

/// The module containing all instruction handlers defining all entries to the
/// Solana program.
/// The `#[program]` attribute defines the module containing all instruction
/// handlers defining all entries into a Solana program.
#[proc_macro_attribute]
pub fn program(
_args: proc_macro::TokenStream,
Expand Down
12 changes: 9 additions & 3 deletions lang/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Accounts;
use crate::{Accounts, ToAccountInfos, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::pubkey::Pubkey;

Expand Down Expand Up @@ -27,13 +27,19 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> {
}

/// Context speciying non-argument inputs for cross-program-invocations.
pub struct CpiContext<'a, 'b, 'c, 'info, T: Accounts<'info>> {
pub struct CpiContext<'a, 'b, 'c, 'info, T>
where
T: ToAccountMetas + ToAccountInfos<'info>,
{
pub accounts: T,
pub program: AccountInfo<'info>,
pub signer_seeds: &'a [&'b [&'c [u8]]],
}

impl<'a, 'b, 'c, 'info, T: Accounts<'info>> CpiContext<'a, 'b, 'c, 'info, T> {
impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T>
where
T: ToAccountMetas + ToAccountInfos<'info>,
{
pub fn new(program: AccountInfo<'info>, accounts: T) -> Self {
Self {
accounts,
Expand Down
5 changes: 3 additions & 2 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod idl;
mod program_account;
mod state;
mod sysvar;
mod vec;

pub use crate::context::{Context, CpiContext};
pub use crate::cpi_account::CpiAccount;
Expand Down Expand Up @@ -68,8 +69,8 @@ pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
/// program dependent. However, users of these types should never have to
/// worry about account substitution attacks. For example, if a program
/// expects a `Mint` account from the SPL token program in a particular
/// field, then it should be impossible for this method to return `Ok` if any
/// other account type is given--from the SPL token program or elsewhere.
/// field, then it should be impossible for this method to return `Ok` if
/// any other account type is given--from the SPL token program or elsewhere.
///
/// `program_id` is the currently executing program. `accounts` is the
/// set of accounts to construct the type from. For every account used,
Expand Down
19 changes: 19 additions & 0 deletions lang/src/vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::{ToAccountInfos, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;

impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec<T> {
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
self.iter()
.flat_map(|item| item.to_account_infos())
.collect()
}
}

impl<T: ToAccountMetas> ToAccountMetas for Vec<T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
self.iter()
.flat_map(|item| (*item).to_account_metas(is_signer))
.collect()
}
}

0 comments on commit bffba39

Please sign in to comment.