Explorar el Código

Complete staking registry

Armani Ferrante hace 4 años
padre
commit
e4b8267697

+ 2 - 3
examples/lockup/programs/lockup/Cargo.toml

@@ -1,12 +1,12 @@
 [package]
-name = "serum-lockup"
+name = "lockup"
 version = "0.1.0"
 description = "Created with Anchor"
 edition = "2018"
 
 [lib]
 crate-type = ["cdylib", "lib"]
-name = "serum_lockup"
+name = "lockup"
 
 [features]
 no-entrypoint = []
@@ -15,4 +15,3 @@ cpi = ["no-entrypoint"]
 [dependencies]
 anchor-lang = { git = "https://github.com/project-serum/anchor", features = ["derive"] }
 anchor-spl = { git = "https://github.com/project-serum/anchor" }
-bytemuck = "1.4.0"

+ 82 - 80
examples/lockup/programs/lockup/src/lib.rs

@@ -41,7 +41,7 @@ mod lockup {
         Ok(())
     }
 
-    #[access_control(create_vesting_accounts(&ctx, nonce))]
+    #[access_control(CreateVesting::accounts(&ctx, nonce))]
     pub fn create_vesting(
         ctx: Context<CreateVesting>,
         beneficiary: Pubkey,
@@ -191,51 +191,6 @@ mod lockup {
     }
 }
 
-#[access_control(is_whitelisted(transfer))]
-pub fn whitelist_relay_cpi<'info>(
-    transfer: &WhitelistTransfer,
-    remaining_accounts: &[AccountInfo<'info>],
-    instruction_data: Vec<u8>,
-) -> Result<(), Error> {
-    let mut meta_accounts = vec![
-        AccountMeta::new_readonly(*transfer.vesting.to_account_info().key, false),
-        AccountMeta::new(*transfer.vault.to_account_info().key, false),
-        AccountMeta::new_readonly(*transfer.vesting_signer.to_account_info().key, true),
-        AccountMeta::new_readonly(*transfer.token_program.to_account_info().key, false),
-        AccountMeta::new(
-            *transfer.whitelisted_program_vault.to_account_info().key,
-            false,
-        ),
-        AccountMeta::new_readonly(
-            *transfer
-                .whitelisted_program_vault_authority
-                .to_account_info()
-                .key,
-            false,
-        ),
-    ];
-    meta_accounts.extend(remaining_accounts.iter().map(|a| {
-        if a.is_writable {
-            AccountMeta::new(*a.key, a.is_signer)
-        } else {
-            AccountMeta::new_readonly(*a.key, a.is_signer)
-        }
-    }));
-    let relay_instruction = Instruction {
-        program_id: *transfer.whitelisted_program.to_account_info().key,
-        accounts: meta_accounts,
-        data: instruction_data.to_vec(),
-    };
-
-    let seeds = &[
-        transfer.vesting.to_account_info().key.as_ref(),
-        &[transfer.vesting.nonce],
-    ];
-    let signer = &[&seeds[..]];
-    solana_program::program::invoke_signed(&relay_instruction, &transfer.to_account_infos(), signer)
-        .map_err(Into::into)
-}
-
 #[derive(Accounts)]
 pub struct SetAuthority<'info> {
     #[account(mut, has_one = authority)]
@@ -263,20 +218,22 @@ pub struct CreateVesting<'info> {
     clock: Sysvar<'info, Clock>,
 }
 
-fn create_vesting_accounts(ctx: &Context<CreateVesting>, nonce: u8) -> Result<(), Error> {
-    let vault_authority = Pubkey::create_program_address(
-        &[
-            ctx.accounts.vesting.to_account_info().key.as_ref(),
-            &[nonce],
-        ],
-        ctx.program_id,
-    )
-    .map_err(|_| ErrorCode::InvalidProgramAddress)?;
-    if ctx.accounts.vault.owner != vault_authority {
-        return Err(ErrorCode::InvalidVaultOwner)?;
-    }
+impl<'info> CreateVesting<'info> {
+    fn accounts(ctx: &Context<CreateVesting>, nonce: u8) -> Result<(), Error> {
+        let vault_authority = Pubkey::create_program_address(
+            &[
+                ctx.accounts.vesting.to_account_info().key.as_ref(),
+                &[nonce],
+            ],
+            ctx.program_id,
+        )
+        .map_err(|_| ErrorCode::InvalidProgramAddress)?;
+        if ctx.accounts.vault.owner != vault_authority {
+            return Err(ErrorCode::InvalidVaultOwner)?;
+        }
 
-    Ok(())
+        Ok(())
+    }
 }
 
 #[derive(Accounts)]
@@ -307,13 +264,6 @@ pub struct WhitelistAdd<'info> {
     authority: AccountInfo<'info>,
 }
 
-fn whitelist_has_capacity(ctx: &Context<WhitelistAdd>) -> Result<(), Error> {
-    if ctx.accounts.lockup.whitelist.len() == lockup::Lockup::WHITELIST_SIZE {
-        return Err(ErrorCode::WhitelistFull.into());
-    }
-    Ok(())
-}
-
 #[derive(Accounts)]
 pub struct WhitelistDelete<'info> {
     #[account(mut, has_one = authority)]
@@ -353,26 +303,12 @@ pub struct WhitelistTransfer<'info> {
     whitelisted_program_vault_authority: AccountInfo<'info>,
 }
 
-pub fn is_whitelisted<'info>(transfer: &WhitelistTransfer<'info>) -> Result<(), Error> {
-    if !transfer.lockup.whitelist.contains(&WhitelistEntry {
-        program_id: *transfer.whitelisted_program.key,
-    }) {
-        return Err(ErrorCode::WhitelistEntryNotFound.into());
-    }
-    Ok(())
-}
-
 #[derive(Accounts)]
 pub struct AvailableForWithdrawal<'info> {
     vesting: ProgramAccount<'info, Vesting>,
     clock: Sysvar<'info, Clock>,
 }
 
-#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Default, Copy, Clone)]
-pub struct WhitelistEntry {
-    pub program_id: Pubkey,
-}
-
 #[account]
 pub struct Vesting {
     /// The owner of this Vesting account.
@@ -403,6 +339,11 @@ pub struct Vesting {
     pub nonce: u8,
 }
 
+#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Default, Copy, Clone)]
+pub struct WhitelistEntry {
+    pub program_id: Pubkey,
+}
+
 #[error]
 pub enum ErrorCode {
     #[msg("Vesting end must be greater than the current unix timestamp.")]
@@ -460,3 +401,64 @@ impl<'a, 'b, 'c, 'info> From<&Withdraw<'info>> for CpiContext<'a, 'b, 'c, 'info,
         CpiContext::new(cpi_program, cpi_accounts)
     }
 }
+
+fn whitelist_has_capacity(ctx: &Context<WhitelistAdd>) -> Result<(), Error> {
+    if ctx.accounts.lockup.whitelist.len() == lockup::Lockup::WHITELIST_SIZE {
+        return Err(ErrorCode::WhitelistFull.into());
+    }
+    Ok(())
+}
+
+#[access_control(is_whitelisted(transfer))]
+pub fn whitelist_relay_cpi<'info>(
+    transfer: &WhitelistTransfer,
+    remaining_accounts: &[AccountInfo<'info>],
+    instruction_data: Vec<u8>,
+) -> Result<(), Error> {
+    let mut meta_accounts = vec![
+        AccountMeta::new_readonly(*transfer.vesting.to_account_info().key, false),
+        AccountMeta::new(*transfer.vault.to_account_info().key, false),
+        AccountMeta::new_readonly(*transfer.vesting_signer.to_account_info().key, true),
+        AccountMeta::new_readonly(*transfer.token_program.to_account_info().key, false),
+        AccountMeta::new(
+            *transfer.whitelisted_program_vault.to_account_info().key,
+            false,
+        ),
+        AccountMeta::new_readonly(
+            *transfer
+                .whitelisted_program_vault_authority
+                .to_account_info()
+                .key,
+            false,
+        ),
+    ];
+    meta_accounts.extend(remaining_accounts.iter().map(|a| {
+        if a.is_writable {
+            AccountMeta::new(*a.key, a.is_signer)
+        } else {
+            AccountMeta::new_readonly(*a.key, a.is_signer)
+        }
+    }));
+    let relay_instruction = Instruction {
+        program_id: *transfer.whitelisted_program.to_account_info().key,
+        accounts: meta_accounts,
+        data: instruction_data.to_vec(),
+    };
+
+    let seeds = &[
+        transfer.vesting.to_account_info().key.as_ref(),
+        &[transfer.vesting.nonce],
+    ];
+    let signer = &[&seeds[..]];
+    solana_program::program::invoke_signed(&relay_instruction, &transfer.to_account_infos(), signer)
+        .map_err(Into::into)
+}
+
+pub fn is_whitelisted<'info>(transfer: &WhitelistTransfer<'info>) -> Result<(), Error> {
+    if !transfer.lockup.whitelist.contains(&WhitelistEntry {
+        program_id: *transfer.whitelisted_program.key,
+    }) {
+        return Err(ErrorCode::WhitelistEntryNotFound.into());
+    }
+    Ok(())
+}

+ 1 - 1
examples/lockup/programs/registry/Cargo.toml

@@ -15,4 +15,4 @@ cpi = ["no-entrypoint"]
 [dependencies]
 anchor-lang = { git = "https://github.com/project-serum/anchor", features = ["derive"] }
 anchor-spl = { git = "https://github.com/project-serum/anchor" }
-serum-lockup = { path = "../lockup", features = ["cpi"] }
+lockup = { path = "../lockup", features = ["cpi"] }

+ 305 - 261
examples/lockup/programs/registry/src/lib.rs

@@ -6,7 +6,7 @@
 use anchor_lang::prelude::*;
 use anchor_lang::solana_program::program_option::COption;
 use anchor_spl::token::{self, Mint, TokenAccount, Transfer};
-use serum_lockup::CreateVesting;
+use lockup::{CreateVesting, Vesting};
 use std::convert::Into;
 
 #[program]
@@ -15,8 +15,7 @@ mod registry {
 
     #[state]
     pub struct Registry {
-        /// Address of the lockup program.
-        lockup_program: Pubkey,
+        pub lockup_program: Pubkey,
     }
 
     impl Registry {
@@ -25,7 +24,7 @@ mod registry {
         }
     }
 
-    #[access_control(initialize_accounts(&ctx, nonce))]
+    #[access_control(Initialize::accounts(&ctx, nonce))]
     pub fn initialize(
         ctx: Context<Initialize>,
         mint: Pubkey,
@@ -71,7 +70,7 @@ mod registry {
         Ok(())
     }
 
-    #[access_control(create_member_accounts(&ctx, nonce))]
+    #[access_control(CreateMember::accounts(&ctx, nonce))]
     pub fn create_member(ctx: Context<CreateMember>, nonce: u8) -> Result<(), Error> {
         let member = &mut ctx.accounts.member;
         member.registrar = *ctx.accounts.registrar.to_account_info().key;
@@ -93,30 +92,13 @@ mod registry {
         Ok(())
     }
 
+    // Deposits that can only come directly from the member beneficiary.
     pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<(), Error> {
-        // Deposit authority *must*  match one of the balance ids.
-        // Similarly, the vault must match the balance id.
-        let vault = ctx.accounts.vault.to_account_info().key;
-
-        // Unlocked vault.
-        if ctx.accounts.depositor_authority.key == &ctx.accounts.member.balances.balance_id {
-            if vault != &ctx.accounts.member.balances.vault {
-                return Err(ErrorCode::InvalidVaultDeposit.into());
-            }
-        }
-        // Locked vault.
-        else if ctx.accounts.depositor_authority.key
-            == &ctx.accounts.member.balances_locked.balance_id
-        {
-            if vault != &ctx.accounts.member.balances_locked.vault {
-                return Err(ErrorCode::InvalidVaultDeposit.into());
-            }
-        }
-        // Unknown.
-        else {
-            return Err(ErrorCode::InvalidDepositor.into());
-        }
+        token::transfer(ctx.accounts.into(), amount).map_err(Into::into)
+    }
 
+    // Deposits that can only come from the beneficiary's vesting accounts.
+    pub fn deposit_locked(ctx: Context<DepositLocked>, amount: u64) -> Result<(), Error> {
         token::transfer(ctx.accounts.into(), amount).map_err(Into::into)
     }
 
@@ -126,23 +108,17 @@ mod registry {
         &ctx.accounts.balances,
         &ctx.accounts.balances_locked,
     ))]
-    pub fn stake(ctx: Context<Stake>, spt_amount: u64, balance_id: Pubkey) -> Result<(), Error> {
-        // Choose balances (locked or unlocked) based on balance_id.
+    pub fn stake(ctx: Context<Stake>, spt_amount: u64, locked: bool) -> Result<(), Error> {
         let balances = {
-            if balance_id == ctx.accounts.member.beneficiary {
-                &ctx.accounts.balances
-            } else {
+            if locked {
                 &ctx.accounts.balances_locked
+            } else {
+                &ctx.accounts.balances
             }
         };
 
         // Transfer tokens into the stake vault.
         {
-            // Convert from stake-token units to mint-token units.
-            let token_amount = spt_amount
-                .checked_mul(ctx.accounts.registrar.stake_rate)
-                .unwrap();
-
             let seeds = &[
                 ctx.accounts.registrar.to_account_info().key.as_ref(),
                 ctx.accounts.member.to_account_info().key.as_ref(),
@@ -158,6 +134,10 @@ mod registry {
                 },
                 member_signer,
             );
+            // Convert from stake-token units to mint-token units.
+            let token_amount = spt_amount
+                .checked_mul(ctx.accounts.registrar.stake_rate)
+                .unwrap();
             token::transfer(cpi_ctx, token_amount)?;
         }
 
@@ -193,14 +173,13 @@ mod registry {
     pub fn start_unstake(
         ctx: Context<StartUnstake>,
         spt_amount: u64,
-        balance_id: Pubkey,
+        locked: bool,
     ) -> Result<(), Error> {
-        // Choose balances (locked or unlocked) based on balance_id.
         let balances = {
-            if balance_id == ctx.accounts.member.beneficiary {
-                &ctx.accounts.balances
-            } else {
+            if locked {
                 &ctx.accounts.balances_locked
+            } else {
+                &ctx.accounts.balances
             }
         };
 
@@ -254,8 +233,8 @@ mod registry {
             ctx.accounts.clock.unix_timestamp + ctx.accounts.registrar.withdrawal_timelock;
         pending_withdrawal.amount = token_amount;
         pending_withdrawal.pool = ctx.accounts.registrar.pool_mint;
-        pending_withdrawal.balance_id = balance_id;
         pending_withdrawal.registrar = *ctx.accounts.registrar.to_account_info().key;
+        pending_withdrawal.locked = locked;
 
         Ok(())
     }
@@ -267,15 +246,10 @@ mod registry {
 
         // Select which balance set this affects.
         let balances = {
-            if ctx.accounts.pending_withdrawal.balance_id == ctx.accounts.member.balances.balance_id
-            {
-                &ctx.accounts.member.balances
-            } else if ctx.accounts.pending_withdrawal.balance_id
-                == ctx.accounts.member.balances_locked.balance_id
-            {
+            if ctx.accounts.pending_withdrawal.locked {
                 &ctx.accounts.member.balances_locked
             } else {
-                return Err(ErrorCode::Unknown.into());
+                &ctx.accounts.member.balances
             }
         };
         // Check the vaults given are corrrect.
@@ -314,29 +288,6 @@ mod registry {
     }
 
     pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<(), Error> {
-        // Deposit authority *must*  match one of the balance ids.
-        // Similarly, the vault must match the balance id.
-        let vault = ctx.accounts.vault.to_account_info().key;
-
-        // Unlocked vault.
-        if ctx.accounts.depositor_authority.key == &ctx.accounts.member.balances.balance_id {
-            if vault != &ctx.accounts.member.balances.vault {
-                return Err(ErrorCode::InvalidVaultDeposit.into());
-            }
-        }
-        // Locked vault.
-        else if ctx.accounts.depositor_authority.key
-            == &ctx.accounts.member.balances_locked.balance_id
-        {
-            if vault != &ctx.accounts.member.balances_locked.vault {
-                return Err(ErrorCode::InvalidVaultDeposit.into());
-            }
-        }
-        // Unknown.
-        else {
-            return Err(ErrorCode::InvalidDepositor.into());
-        }
-
         let seeds = &[
             ctx.accounts.registrar.to_account_info().key.as_ref(),
             ctx.accounts.member.to_account_info().key.as_ref(),
@@ -354,6 +305,25 @@ mod registry {
         token::transfer(cpi_ctx, amount).map_err(Into::into)
     }
 
+    pub fn withdraw_locked(ctx: Context<WithdrawLocked>, amount: u64) -> Result<(), Error> {
+        let seeds = &[
+            ctx.accounts.registrar.to_account_info().key.as_ref(),
+            ctx.accounts.member.to_account_info().key.as_ref(),
+            &[ctx.accounts.member.nonce],
+        ];
+        let signer = &[&seeds[..]];
+        let cpi_accounts = Transfer {
+            from: ctx.accounts.member_vault.to_account_info(),
+            to: ctx.accounts.vesting_vault.to_account_info(),
+            authority: ctx.accounts.member_signer.clone(),
+        };
+        let cpi_program = ctx.accounts.token_program.clone();
+        let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
+
+        token::transfer(cpi_ctx, amount).map_err(Into::into)
+    }
+
+    #[access_control(DropReward::accounts(&ctx, nonce))]
     pub fn drop_reward(
         ctx: Context<DropReward>,
         kind: RewardVendorKind,
@@ -362,19 +332,6 @@ mod registry {
         expiry_receiver: Pubkey,
         nonce: u8,
     ) -> Result<(), Error> {
-        // Validate args.
-        let vendor_signer = Pubkey::create_program_address(
-            &[
-                ctx.accounts.registrar.to_account_info().key.as_ref(),
-                ctx.accounts.vendor.to_account_info().key.as_ref(),
-                &[nonce],
-            ],
-            ctx.program_id,
-        )
-        .map_err(|_| ErrorCode::InvalidNonce)?;
-        if vendor_signer != ctx.accounts.vendor_vault.owner {
-            return Err(ErrorCode::InvalidVaultOwner.into());
-        }
         if total < ctx.accounts.pool_mint.supply {
             return Err(ErrorCode::InsufficientReward.into());
         }
@@ -411,11 +368,11 @@ mod registry {
     }
 
     #[access_control(reward_eligible(&ctx.accounts.cmn))]
-    pub fn claim_reward_unlocked(ctx: Context<ClaimRewardUnlocked>) -> Result<(), Error> {
+    pub fn claim_reward(ctx: Context<ClaimReward>) -> Result<(), Error> {
         if RewardVendorKind::Unlocked != ctx.accounts.cmn.vendor.kind {
             return Err(ErrorCode::ExpectedUnlockedVendor.into());
         }
-        // Reward to distribute.
+        // Reward distribution.
         let spt_total =
             ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount;
         let reward_amount = spt_total
@@ -425,7 +382,7 @@ mod registry {
             .unwrap();
         assert!(reward_amount > 0);
 
-        // Vend reward to the member.
+        // Send reward to the given token account.
         let seeds = &[
             ctx.accounts.cmn.registrar.to_account_info().key.as_ref(),
             ctx.accounts.cmn.vendor.to_account_info().key.as_ref(),
@@ -435,8 +392,8 @@ mod registry {
         let cpi_ctx = CpiContext::new_with_signer(
             ctx.accounts.cmn.token_program.clone(),
             token::Transfer {
-                to: ctx.accounts.token.to_account_info(),
                 from: ctx.accounts.cmn.vault.to_account_info(),
+                to: ctx.accounts.to.to_account_info(),
                 authority: ctx.accounts.cmn.vendor_signer.to_account_info(),
             },
             signer,
@@ -462,14 +419,8 @@ mod registry {
                 period_count,
             } => (end_ts, period_count),
         };
-        // Lockup program requires the timestamp to be >= clock's timestamp.
-        // So update if the time has already passed. 60 seconds is arbitrary.
-        let end_ts = match end_ts > ctx.accounts.cmn.clock.unix_timestamp + 60 {
-            true => end_ts,
-            false => ctx.accounts.cmn.clock.unix_timestamp + 60,
-        };
 
-        // Calculate reward distribution.
+        // Reward distribution.
         let spt_total =
             ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount;
         let reward_amount = spt_total
@@ -479,7 +430,14 @@ mod registry {
             .unwrap();
         assert!(reward_amount > 0);
 
-        // Vend reward to the member by creating a lockup account.
+        // Lockup program requires the timestamp to be >= clock's timestamp.
+        // So update if the time has already passed. 60 seconds is arbitrary.
+        let end_ts = match end_ts > ctx.accounts.cmn.clock.unix_timestamp + 60 {
+            true => end_ts,
+            false => ctx.accounts.cmn.clock.unix_timestamp + 60,
+        };
+
+        // Create lockup account for the member's beneficiary.
         let seeds = &[
             ctx.accounts.cmn.registrar.to_account_info().key.as_ref(),
             ctx.accounts.cmn.vendor.to_account_info().key.as_ref(),
@@ -487,12 +445,11 @@ mod registry {
         ];
         let signer = &[&seeds[..]];
         let mut remaining_accounts: &[AccountInfo] = ctx.remaining_accounts;
-
         let cpi_program = ctx.accounts.lockup_program.clone();
         let cpi_accounts =
             CreateVesting::try_accounts(ctx.accounts.lockup_program.key, &mut remaining_accounts)?;
         let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
-        serum_lockup::cpi::create_vesting(
+        lockup::cpi::create_vesting(
             cpi_ctx,
             ctx.accounts.cmn.member.beneficiary,
             end_ts,
@@ -513,7 +470,7 @@ mod registry {
             return Err(ErrorCode::VendorNotYetExpired.into());
         }
 
-        // Send all remaining funds to the expiry receiver.
+        // Send all remaining funds to the expiry receiver's token.
         let seeds = &[
             ctx.accounts.registrar.to_account_info().key.as_ref(),
             ctx.accounts.vendor.to_account_info().key.as_ref(),
@@ -523,7 +480,7 @@ mod registry {
         let cpi_ctx = CpiContext::new_with_signer(
             ctx.accounts.token_program.clone(),
             token::Transfer {
-                to: ctx.accounts.token.to_account_info(),
+                to: ctx.accounts.expiry_receiver_token.to_account_info(),
                 from: ctx.accounts.vault.to_account_info(),
                 authority: ctx.accounts.vendor_signer.to_account_info(),
             },
@@ -531,6 +488,7 @@ mod registry {
         );
         token::transfer(cpi_ctx, ctx.accounts.vault.amount)?;
 
+        // Burn the vendor.
         let vendor = &mut ctx.accounts.vendor;
         vendor.expired = true;
 
@@ -538,51 +496,6 @@ mod registry {
     }
 }
 
-fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<(), Error> {
-    let vendor = &cmn.vendor;
-    let member = &cmn.member;
-    if vendor.expired {
-        return Err(ErrorCode::VendorExpired.into());
-    }
-    if member.rewards_cursor > vendor.reward_event_q_cursor {
-        return Err(ErrorCode::CursorAlreadyProcessed.into());
-    }
-    if member.last_stake_ts > vendor.start_ts {
-        return Err(ErrorCode::NotStakedDuringDrop.into());
-    }
-    Ok(())
-}
-
-// Asserts the user calling the `Stake` instruction has no rewards available
-// in the reward queue.
-pub fn no_available_rewards<'info>(
-    reward_q: &ProgramAccount<'info, RewardQueue>,
-    member: &ProgramAccount<'info, Member>,
-    balances: &BalanceSandboxAccounts<'info>,
-    balances_locked: &BalanceSandboxAccounts<'info>,
-) -> Result<(), Error> {
-    let mut cursor = member.rewards_cursor;
-
-    // If the member's cursor is less then the tail, then the ring buffer has
-    // overwritten those entries, so jump to the tail.
-    let tail = reward_q.tail();
-    if cursor < tail {
-        cursor = tail;
-    }
-
-    while cursor < reward_q.head() {
-        let r_event = reward_q.get(cursor);
-        if member.last_stake_ts < r_event.ts {
-            if balances.spt.amount > 0 || balances_locked.spt.amount > 0 {
-                return Err(ErrorCode::RewardsNeedsProcessing.into());
-            }
-        }
-        cursor += 1;
-    }
-
-    Ok(())
-}
-
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(init)]
@@ -593,20 +506,22 @@ pub struct Initialize<'info> {
     rent: Sysvar<'info, Rent>,
 }
 
-fn initialize_accounts<'info>(ctx: &Context<Initialize<'info>>, nonce: u8) -> Result<(), Error> {
-    let registrar_signer = Pubkey::create_program_address(
-        &[
-            ctx.accounts.registrar.to_account_info().key.as_ref(),
-            &[nonce],
-        ],
-        ctx.program_id,
-    )
-    .map_err(|_| ErrorCode::InvalidNonce)?;
-    if ctx.accounts.pool_mint.mint_authority != COption::Some(registrar_signer) {
-        return Err(ErrorCode::InvalidPoolMintAuthority.into());
+impl<'info> Initialize<'info> {
+    fn accounts(ctx: &Context<Initialize<'info>>, nonce: u8) -> Result<(), Error> {
+        let registrar_signer = Pubkey::create_program_address(
+            &[
+                ctx.accounts.registrar.to_account_info().key.as_ref(),
+                &[nonce],
+            ],
+            ctx.program_id,
+        )
+        .map_err(|_| ErrorCode::InvalidNonce)?;
+        if ctx.accounts.pool_mint.mint_authority != COption::Some(registrar_signer) {
+            return Err(ErrorCode::InvalidPoolMintAuthority.into());
+        }
+        assert!(ctx.accounts.pool_mint.supply == 0);
+        Ok(())
     }
-    assert!(ctx.accounts.pool_mint.supply == 0);
-    Ok(())
 }
 
 #[derive(Accounts)]
@@ -619,12 +534,13 @@ pub struct UpdateRegistrar<'info> {
 
 #[derive(Accounts)]
 pub struct CreateMember<'info> {
+    // Stake instance.
     registrar: ProgramAccount<'info, Registrar>,
+    // Member.
     #[account(init)]
     member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
-    member_signer: AccountInfo<'info>,
     #[account(
         "&balances.spt.owner == member_signer.key",
         "balances.spt.mint == registrar.pool_mint",
@@ -637,30 +553,37 @@ pub struct CreateMember<'info> {
         "balances_locked.vault.mint == registrar.mint"
     )]
     balances_locked: BalanceSandboxAccounts<'info>,
+    member_signer: AccountInfo<'info>,
+    // Misc.
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
     rent: Sysvar<'info, Rent>,
 }
 
-fn create_member_accounts(ctx: &Context<CreateMember>, nonce: u8) -> Result<(), Error> {
-    // Check the nonce + signer is correct.
-    let seeds = &[
-        ctx.accounts.registrar.to_account_info().key.as_ref(),
-        ctx.accounts.member.to_account_info().key.as_ref(),
-        &[nonce],
-    ];
-    let member_signer = Pubkey::create_program_address(seeds, ctx.program_id)
-        .map_err(|_| ErrorCode::InvalidNonce)?;
-    if &member_signer != ctx.accounts.member_signer.to_account_info().key {
-        return Err(ErrorCode::InvalidMemberSigner.into());
-    }
+impl<'info> CreateMember<'info> {
+    fn accounts(ctx: &Context<CreateMember>, nonce: u8) -> Result<(), Error> {
+        let seeds = &[
+            ctx.accounts.registrar.to_account_info().key.as_ref(),
+            ctx.accounts.member.to_account_info().key.as_ref(),
+            &[nonce],
+        ];
+        let member_signer = Pubkey::create_program_address(seeds, ctx.program_id)
+            .map_err(|_| ErrorCode::InvalidNonce)?;
+        if &member_signer != ctx.accounts.member_signer.to_account_info().key {
+            return Err(ErrorCode::InvalidMemberSigner.into());
+        }
 
-    Ok(())
+        Ok(())
+    }
 }
 
+// When creating a member, the mints and owners of these accounts are correct.
+// Upon creation, we assign the accounts. A onetime operation.
+// When using a member, we check these accounts addresess are equal to the
+// addresses stored on the member. If so, the correct accounts were given are
+// correct.
 #[derive(Accounts, Clone)]
 pub struct BalanceSandboxAccounts<'info> {
-    balance_id: AccountInfo<'info>,
     #[account(mut)]
     spt: CpiAccount<'info, TokenAccount>,
     #[account(mut, "vault.owner == spt.owner")]
@@ -675,18 +598,6 @@ pub struct BalanceSandboxAccounts<'info> {
     vault_pw: CpiAccount<'info, TokenAccount>,
 }
 
-impl<'info> From<&BalanceSandboxAccounts<'info>> for BalanceSandbox {
-    fn from(accs: &BalanceSandboxAccounts<'info>) -> Self {
-        Self {
-            balance_id: *accs.balance_id.key,
-            spt: *accs.spt.to_account_info().key,
-            vault: *accs.vault.to_account_info().key,
-            vault_stake: *accs.vault_stake.to_account_info().key,
-            vault_pw: *accs.vault_pw.to_account_info().key,
-        }
-    }
-}
-
 #[derive(Accounts)]
 pub struct UpdateMember<'info> {
     #[account(mut, has_one = beneficiary)]
@@ -697,16 +608,44 @@ pub struct UpdateMember<'info> {
 
 #[derive(Accounts)]
 pub struct Deposit<'info> {
-    // Lockup whitelist relay interface.
-    vesting: AccountInfo<'info>,
+    // Member.
+    #[account(has_one = beneficiary)]
+    member: ProgramAccount<'info, Member>,
+    #[account(signer)]
+    beneficiary: AccountInfo<'info>,
+    #[account(mut, "vault.to_account_info().key == &member.balances.vault")]
+    vault: CpiAccount<'info, TokenAccount>,
+    // Depositor.
     #[account(mut)]
     depositor: AccountInfo<'info>,
+    #[account(signer, "depositor_authority.key == &member.beneficiary")]
+    depositor_authority: AccountInfo<'info>,
+    // Misc.
+    #[account("token_program.key == &token::ID")]
+    token_program: AccountInfo<'info>,
+}
+
+#[derive(Accounts)]
+pub struct DepositLocked<'info> {
+    // Lockup whitelist relay interface.
+    #[account(
+        "vesting.to_account_info().owner == &registry.lockup_program",
+        "vesting.beneficiary == member.beneficiary"
+    )]
+    vesting: CpiAccount<'info, Vesting>,
+    #[account(mut, "vesting_vault.key == &vesting.vault")]
+    vesting_vault: AccountInfo<'info>,
+    // Note: no need to verify the depositor_authority since the SPL program
+    //       will fail the transaction if it's not correct.
     #[account(signer)]
     depositor_authority: AccountInfo<'info>,
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
-    #[account(mut, "&vault.owner == member_signer.key")]
-    vault: CpiAccount<'info, TokenAccount>,
+    #[account(
+        mut,
+        "member_vault.to_account_info().key == &member.balances_locked.vault"
+    )]
+    member_vault: CpiAccount<'info, TokenAccount>,
     #[account(
         seeds = [
             registrar.to_account_info().key.as_ref(),
@@ -717,25 +656,12 @@ pub struct Deposit<'info> {
     member_signer: AccountInfo<'info>,
 
     // Program specific.
+    registry: ProgramState<'info, Registry>,
     registrar: ProgramAccount<'info, Registrar>,
+    #[account(belongs_to = registrar, has_one = beneficiary)]
+    member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
-    #[account(belongs_to = registrar, belongs_to = beneficiary)]
-    member: ProgramAccount<'info, Member>,
-}
-
-impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>>
-    for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
-{
-    fn from(accounts: &mut Deposit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
-        let cpi_accounts = Transfer {
-            from: accounts.depositor.clone(),
-            to: accounts.vault.to_account_info(),
-            authority: accounts.depositor_authority.clone(),
-        };
-        let cpi_program = accounts.token_program.clone();
-        CpiContext::new(cpi_program, cpi_accounts)
-    }
 }
 
 #[derive(Accounts)]
@@ -747,7 +673,7 @@ pub struct Stake<'info> {
     #[account(mut)]
     pool_mint: CpiAccount<'info, Mint>,
 
-    // Member specific.
+    // Member.
     #[account(mut, has_one = beneficiary, belongs_to = registrar)]
     member: ProgramAccount<'info, Member>,
     #[account(signer)]
@@ -757,7 +683,7 @@ pub struct Stake<'info> {
     #[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
     balances_locked: BalanceSandboxAccounts<'info>,
 
-    // Programmatic signers.
+    // Program signers.
     #[account(
         seeds = [
             registrar.to_account_info().key.as_ref(),
@@ -766,12 +692,7 @@ pub struct Stake<'info> {
         ]
     )]
     member_signer: AccountInfo<'info>,
-    #[account(
-        seeds = [
-            registrar.to_account_info().key.as_ref(),
-            &[registrar.nonce],
-        ]
-    )]
+    #[account(seeds = [registrar.to_account_info().key.as_ref(), &[registrar.nonce]])]
     registrar_signer: AccountInfo<'info>,
 
     // Misc.
@@ -795,17 +716,9 @@ pub struct StartUnstake<'info> {
     member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
-    #[account(
-        "&balances.spt.owner == member_signer.key",
-        "balances.spt.mint == registrar.pool_mint",
-        "balances.vault.mint == registrar.mint"
-    )]
+    #[account("BalanceSandbox::from(&balances) == member.balances")]
     balances: BalanceSandboxAccounts<'info>,
-    #[account(
-        "&balances_locked.spt.owner == member_signer.key",
-        "balances_locked.spt.mint == registrar.pool_mint",
-        "balances_locked.vault.mint == registrar.mint"
-    )]
+    #[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
     balances_locked: BalanceSandboxAccounts<'info>,
 
     // Programmatic signers.
@@ -859,16 +772,50 @@ pub struct EndUnstake<'info> {
 
 #[derive(Accounts)]
 pub struct Withdraw<'info> {
-    // Lockup whitelist relay interface.
-    vesting: AccountInfo<'info>,
+    // Stake instance.
+    registrar: ProgramAccount<'info, Registrar>,
+    // Member.
+    #[account(belongs_to = registrar, has_one = beneficiary)]
+    member: ProgramAccount<'info, Member>,
+    #[account(signer)]
+    beneficiary: AccountInfo<'info>,
+    #[account(mut, "vault.to_account_info().key == &member.balances.vault")]
+    vault: CpiAccount<'info, TokenAccount>,
+    #[account(
+        seeds = [
+            registrar.to_account_info().key.as_ref(),
+            member.to_account_info().key.as_ref(),
+            &[member.nonce],
+        ]
+    )]
+    member_signer: AccountInfo<'info>,
+    // Receiver.
     #[account(mut)]
     depositor: AccountInfo<'info>,
+    // Misc.
+    #[account("token_program.key == &token::ID")]
+    token_program: AccountInfo<'info>,
+}
+
+#[derive(Accounts)]
+pub struct WithdrawLocked<'info> {
+    // Lockup whitelist relay interface.
+    #[account(
+        "vesting.to_account_info().owner == &registry.lockup_program",
+        "vesting.beneficiary == member.beneficiary"
+    )]
+    vesting: CpiAccount<'info, Vesting>,
+    #[account(mut, "vesting_vault.key == &vesting.vault")]
+    vesting_vault: AccountInfo<'info>,
     #[account(signer)]
-    depositor_authority: AccountInfo<'info>,
+    vesting_signer: AccountInfo<'info>,
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
-    #[account(mut, "&vault.owner == member_signer.key")]
-    vault: CpiAccount<'info, TokenAccount>,
+    #[account(
+        mut,
+        "member_vault.to_account_info().key == &member.balances_locked.vault"
+    )]
+    member_vault: CpiAccount<'info, TokenAccount>,
     #[account(
         seeds = [
             registrar.to_account_info().key.as_ref(),
@@ -879,11 +826,12 @@ pub struct Withdraw<'info> {
     member_signer: AccountInfo<'info>,
 
     // Program specific.
+    registry: ProgramState<'info, Registry>,
     registrar: ProgramAccount<'info, Registrar>,
+    #[account(belongs_to = registrar, has_one = beneficiary)]
+    member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
-    #[account(belongs_to = registrar, belongs_to = beneficiary)]
-    member: ProgramAccount<'info, Member>,
 }
 
 #[derive(Accounts)]
@@ -894,19 +842,16 @@ pub struct DropReward<'info> {
     #[account(mut)]
     reward_event_q: ProgramAccount<'info, RewardQueue>,
     pool_mint: CpiAccount<'info, Mint>,
-
     // Vendor.
     #[account(init)]
     vendor: ProgramAccount<'info, RewardVendor>,
     #[account(mut)]
     vendor_vault: CpiAccount<'info, TokenAccount>,
-
     // Depositor.
     #[account(mut)]
     depositor: AccountInfo<'info>,
     #[account(signer)]
     depositor_authority: AccountInfo<'info>,
-
     // Misc.
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
@@ -914,32 +859,38 @@ pub struct DropReward<'info> {
     rent: Sysvar<'info, Rent>,
 }
 
-impl<'a, 'b, 'c, 'info> From<&mut DropReward<'info>>
-    for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
-{
-    fn from(accounts: &mut DropReward<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
-        let cpi_accounts = Transfer {
-            from: accounts.depositor.clone(),
-            to: accounts.vendor_vault.to_account_info(),
-            authority: accounts.depositor_authority.clone(),
-        };
-        let cpi_program = accounts.token_program.clone();
-        CpiContext::new(cpi_program, cpi_accounts)
+impl<'info> DropReward<'info> {
+    fn accounts(ctx: &Context<DropReward>, nonce: u8) -> Result<(), Error> {
+        let vendor_signer = Pubkey::create_program_address(
+            &[
+                ctx.accounts.registrar.to_account_info().key.as_ref(),
+                ctx.accounts.vendor.to_account_info().key.as_ref(),
+                &[nonce],
+            ],
+            ctx.program_id,
+        )
+        .map_err(|_| ErrorCode::InvalidNonce)?;
+        if vendor_signer != ctx.accounts.vendor_vault.owner {
+            return Err(ErrorCode::InvalidVaultOwner.into());
+        }
+
+        Ok(())
     }
 }
 
 #[derive(Accounts)]
-pub struct ClaimRewardUnlocked<'info> {
+pub struct ClaimReward<'info> {
     cmn: ClaimRewardCommon<'info>,
     // Account to send reward to.
     #[account(mut)]
-    token: AccountInfo<'info>,
+    to: AccountInfo<'info>,
 }
 
 #[derive(Accounts)]
 pub struct ClaimRewardLocked<'info> {
     cmn: ClaimRewardCommon<'info>,
-    // TODO: assert on the lockup program id once deployed.
+    registry: ProgramState<'info, Registry>,
+    #[account("lockup_program.key == &registry.lockup_program")]
     lockup_program: AccountInfo<'info>,
 }
 
@@ -948,9 +899,8 @@ pub struct ClaimRewardLocked<'info> {
 pub struct ClaimRewardCommon<'info> {
     // Stake instance.
     registrar: ProgramAccount<'info, Registrar>,
-
     // Member.
-    #[account(mut, belongs_to = registrar)]
+    #[account(mut, belongs_to = registrar, has_one = beneficiary)]
     member: ProgramAccount<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
@@ -958,7 +908,6 @@ pub struct ClaimRewardCommon<'info> {
     balances: BalanceSandboxAccounts<'info>,
     #[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
     balances_locked: BalanceSandboxAccounts<'info>,
-
     // Vendor.
     #[account(belongs_to = registrar, has_one = vault)]
     vendor: ProgramAccount<'info, RewardVendor>,
@@ -972,7 +921,6 @@ pub struct ClaimRewardCommon<'info> {
         ]
     )]
     vendor_signer: AccountInfo<'info>,
-
     // Misc.
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
@@ -983,7 +931,6 @@ pub struct ClaimRewardCommon<'info> {
 pub struct ExpireReward<'info> {
     // Staking instance globals.
     registrar: ProgramAccount<'info, Registrar>,
-
     // Vendor.
     #[account(mut, belongs_to = registrar, has_one = vault, has_one = expiry_receiver)]
     vendor: ProgramAccount<'info, RewardVendor>,
@@ -997,13 +944,11 @@ pub struct ExpireReward<'info> {
         ]
     )]
     vendor_signer: AccountInfo<'info>,
-
     // Receiver.
     #[account(signer)]
     expiry_receiver: AccountInfo<'info>,
     #[account(mut)]
-    token: AccountInfo<'info>,
-
+    expiry_receiver_token: AccountInfo<'info>,
     // Misc.
     #[account("token_program.key == &token::ID")]
     token_program: AccountInfo<'info>,
@@ -1052,14 +997,13 @@ pub struct Member {
 }
 
 // BalanceSandbox defines isolated funds that can only be deposited/withdrawn
-// into the program if the `owner` signs off on the transaction.
+// into the program.
 //
 // Once controlled by the program, the associated `Member` account's beneficiary
 // can send funds to/from any of the accounts within the sandbox, e.g., to
 // stake.
 #[derive(AnchorSerialize, AnchorDeserialize, Default, Debug, Clone, PartialEq)]
 pub struct BalanceSandbox {
-    pub balance_id: Pubkey,
     // Staking pool token.
     pub spt: Pubkey,
     // Free balance (deposit) vaults.
@@ -1086,8 +1030,8 @@ pub struct PendingWithdrawal {
     pub end_ts: i64,
     /// The number of tokens redeemed from the staking pool.
     pub amount: u64,
-    /// The Member account's set of vaults this withdrawal belongs to.
-    pub balance_id: Pubkey,
+    /// True if the withdrawal applies to locked balances.
+    pub locked: bool,
 }
 
 #[account]
@@ -1209,4 +1153,104 @@ pub enum ErrorCode {
     ExpectedLockedVendor,
     #[msg("Unlocked reward vendor expected but a locked vendor was given.")]
     ExpectedUnlockedVendor,
+    #[msg("Locked deposit from an invalid deposit authority.")]
+    InvalidVestingSigner,
+}
+
+impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>>
+    for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
+{
+    fn from(accounts: &mut Deposit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
+        let cpi_accounts = Transfer {
+            from: accounts.depositor.clone(),
+            to: accounts.vault.to_account_info(),
+            authority: accounts.depositor_authority.clone(),
+        };
+        let cpi_program = accounts.token_program.clone();
+        CpiContext::new(cpi_program, cpi_accounts)
+    }
+}
+
+impl<'a, 'b, 'c, 'info> From<&mut DepositLocked<'info>>
+    for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
+{
+    fn from(accounts: &mut DepositLocked<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
+        let cpi_accounts = Transfer {
+            from: accounts.vesting_vault.clone(),
+            to: accounts.member_vault.to_account_info(),
+            authority: accounts.depositor_authority.clone(),
+        };
+        let cpi_program = accounts.token_program.clone();
+        CpiContext::new(cpi_program, cpi_accounts)
+    }
+}
+
+impl<'a, 'b, 'c, 'info> From<&mut DropReward<'info>>
+    for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
+{
+    fn from(accounts: &mut DropReward<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
+        let cpi_accounts = Transfer {
+            from: accounts.depositor.clone(),
+            to: accounts.vendor_vault.to_account_info(),
+            authority: accounts.depositor_authority.clone(),
+        };
+        let cpi_program = accounts.token_program.clone();
+        CpiContext::new(cpi_program, cpi_accounts)
+    }
+}
+
+impl<'info> From<&BalanceSandboxAccounts<'info>> for BalanceSandbox {
+    fn from(accs: &BalanceSandboxAccounts<'info>) -> Self {
+        Self {
+            spt: *accs.spt.to_account_info().key,
+            vault: *accs.vault.to_account_info().key,
+            vault_stake: *accs.vault_stake.to_account_info().key,
+            vault_pw: *accs.vault_pw.to_account_info().key,
+        }
+    }
+}
+
+fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<(), Error> {
+    let vendor = &cmn.vendor;
+    let member = &cmn.member;
+    if vendor.expired {
+        return Err(ErrorCode::VendorExpired.into());
+    }
+    if member.rewards_cursor > vendor.reward_event_q_cursor {
+        return Err(ErrorCode::CursorAlreadyProcessed.into());
+    }
+    if member.last_stake_ts > vendor.start_ts {
+        return Err(ErrorCode::NotStakedDuringDrop.into());
+    }
+    Ok(())
+}
+
+// Asserts the user calling the `Stake` instruction has no rewards available
+// in the reward queue.
+pub fn no_available_rewards<'info>(
+    reward_q: &ProgramAccount<'info, RewardQueue>,
+    member: &ProgramAccount<'info, Member>,
+    balances: &BalanceSandboxAccounts<'info>,
+    balances_locked: &BalanceSandboxAccounts<'info>,
+) -> Result<(), Error> {
+    let mut cursor = member.rewards_cursor;
+
+    // If the member's cursor is less then the tail, then the ring buffer has
+    // overwritten those entries, so jump to the tail.
+    let tail = reward_q.tail();
+    if cursor < tail {
+        cursor = tail;
+    }
+
+    while cursor < reward_q.head() {
+        let r_event = reward_q.get(cursor);
+        if member.last_stake_ts < r_event.ts {
+            if balances.spt.amount > 0 || balances_locked.spt.amount > 0 {
+                return Err(ErrorCode::RewardsNeedsProcessing.into());
+            }
+        }
+        cursor += 1;
+    }
+
+    Ok(())
 }

+ 13 - 23
examples/lockup/tests/lockup.js

@@ -353,14 +353,12 @@ describe("Lockup and Registry", () => {
     const [mainTx, _balances] = await utils.createBalanceSandbox(
       provider,
       registrarAccount,
-      memberSigner,
-      provider.wallet.publicKey // Beneficiary,
+      memberSigner
     );
     const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox(
       provider,
       registrarAccount,
-      memberSigner,
-      vesting.publicKey // Lockup.
+      memberSigner
     );
 
     balances = _balances;
@@ -407,15 +405,10 @@ describe("Lockup and Registry", () => {
     const depositAmount = new anchor.BN(120);
     await registry.rpc.deposit(depositAmount, {
       accounts: {
-        // Whitelist relay.
-        vesting: anchor.web3.SYSVAR_RENT_PUBKEY,
         depositor: god,
         depositorAuthority: provider.wallet.publicKey,
         tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
         vault: memberAccount.balances.vault,
-        memberSigner,
-        // Program specific.
-        registrar: registrar.publicKey,
         beneficiary: provider.wallet.publicKey,
         member: member.publicKey,
       },
@@ -430,7 +423,7 @@ describe("Lockup and Registry", () => {
 
   it("Stakes to a member (unlocked)", async () => {
     const stakeAmount = new anchor.BN(10);
-    await registry.rpc.stake(stakeAmount, provider.wallet.publicKey, {
+    await registry.rpc.stake(stakeAmount, false, {
       accounts: {
         // Stake instance.
         registrar: registrar.publicKey,
@@ -553,9 +546,9 @@ describe("Lockup and Registry", () => {
       mint,
       provider.wallet.publicKey
     );
-    await registry.rpc.claimRewardUnlocked({
+    await registry.rpc.claimReward({
       accounts: {
-        token,
+        to: token,
         cmn: {
           registrar: registrar.publicKey,
 
@@ -696,6 +689,7 @@ describe("Lockup and Registry", () => {
 
     await registry.rpc.claimRewardLocked(nonce, {
       accounts: {
+        registry: await registry.state.address(),
         lockupProgram: lockup.programId,
         cmn: {
           registrar: registrar.publicKey,
@@ -744,10 +738,10 @@ describe("Lockup and Registry", () => {
 
   const pendingWithdrawal = new anchor.web3.Account();
 
-  it("Unstakes", async () => {
+  it("Unstakes (unlocked)", async () => {
     const unstakeAmount = new anchor.BN(10);
 
-    await registry.rpc.startUnstake(unstakeAmount, provider.wallet.publicKey, {
+    await registry.rpc.startUnstake(unstakeAmount, false, {
       accounts: {
         registrar: registrar.publicKey,
         rewardEventQ: rewardQ.publicKey,
@@ -853,17 +847,13 @@ describe("Lockup and Registry", () => {
     const withdrawAmount = new anchor.BN(100);
     await registry.rpc.withdraw(withdrawAmount, {
       accounts: {
-        // Whitelist relay.
-        vesting: anchor.web3.SYSVAR_RENT_PUBKEY,
-        depositor: token,
-        depositorAuthority: provider.wallet.publicKey,
-        tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
-        vault: memberAccount.balances.vault,
-        memberSigner,
-        // Program specific.
         registrar: registrar.publicKey,
-        beneficiary: provider.wallet.publicKey,
         member: member.publicKey,
+        beneficiary: provider.wallet.publicKey,
+        vault: memberAccount.balances.vault,
+        memberSigner,
+        depositor: token,
+        tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
       },
     });
 

+ 3 - 3
examples/lockup/tests/utils.js

@@ -1,7 +1,8 @@
-const anchor = require("@project-serum/anchor");
+// const anchor = require("@project-serum/anchor");
+const anchor = require("/home/armaniferrante/Documents/code/src/github.com/project-serum/anchor/ts");
 const serumCmn = require("@project-serum/common");
 
-async function createBalanceSandbox(provider, r, registrySigner, owner) {
+async function createBalanceSandbox(provider, r, registrySigner) {
   const spt = new anchor.web3.Account();
   const vault = new anchor.web3.Account();
   const vaultStake = new anchor.web3.Account();
@@ -53,7 +54,6 @@ async function createBalanceSandbox(provider, r, registrySigner, owner) {
   return [
     tx,
     {
-      balanceId: owner,
       spt: spt.publicKey,
       vault: vault.publicKey,
       vaultStake: vaultStake.publicKey,