diff --git a/programs/voter-stake-registry/src/instructions/claim.rs b/programs/voter-stake-registry/src/instructions/claim.rs index d56dea1c..aec24b9c 100644 --- a/programs/voter-stake-registry/src/instructions/claim.rs +++ b/programs/voter-stake-registry/src/instructions/claim.rs @@ -20,16 +20,26 @@ pub struct Claim<'info> { /// CHECK: Rewards mint addr will be checked in the rewards contract pub reward_mint: UncheckedAccount<'info>, - #[account(mut)] + #[account( + mut, + seeds = [b"vault", reward_pool.key().as_ref(), reward_mint.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] /// CHECK: Rewards vault is used as a source of rewards and /// is checked on the rewards contract /// PDA(["vault", reward_pool, reward_mint], reward_program) pub vault: UncheckedAccount<'info>, /// CHECK: mining PDA will be checked in the rewards contract - /// PDA(["mining", mining owner , reward_pool], + /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", mining_owner.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, pub mining_owner: Signer<'info>, diff --git a/programs/voter-stake-registry/src/instructions/close_voter.rs b/programs/voter-stake-registry/src/instructions/close_voter.rs index 573c2edb..3b6a52a2 100644 --- a/programs/voter-stake-registry/src/instructions/close_voter.rs +++ b/programs/voter-stake-registry/src/instructions/close_voter.rs @@ -1,6 +1,6 @@ use crate::cpi_instructions; use anchor_lang::prelude::*; -use anchor_spl::token::{self, CloseAccount, Mint, Token, TokenAccount}; +use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; use bytemuck::bytes_of_mut; use mplx_staking_states::{ error::MplStakingError, @@ -21,7 +21,7 @@ pub struct CloseVoter<'info> { // the other constraints must be exhaustive #[account( mut, - seeds = [voter.load()?.registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()], + seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()], bump = voter.load()?.voter_bump, has_one = voter_authority, close = sol_destination @@ -34,7 +34,12 @@ pub struct CloseVoter<'info> { /// CHECK: mining PDA will be checked in the rewards contract /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", voter_authority.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, /// CHECK: @@ -43,8 +48,6 @@ pub struct CloseVoter<'info> { /// keep track of all rewards and staking logic. pub reward_pool: UncheckedAccount<'info>, - pub deposit_mint: Account<'info, Mint>, - #[account(mut)] /// CHECK: Destination may be any address. pub sol_destination: UncheckedAccount<'info>, @@ -56,17 +59,15 @@ pub struct CloseVoter<'info> { pub rewards_program: UncheckedAccount<'info>, } -/// Closes the voter account, and specified token vault if any provided, +/// Closes the voter account, and specified token vaults if provided in the remaining accounts, /// allowing to retrieve rent examption SOL. /// Only accounts with no remaining stakes can be closed. +/// +/// Remaining accounts should containt the complete list of ATA that must be closed, +/// the length of those accounts should be equal to the number of mint configs in the registrar. pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> Result<()> { let registrar = ctx.accounts.registrar.load()?; - require!( - ctx.accounts.deposit_mint.key() == registrar.realm_governing_token_mint, - MplStakingError::InvalidGoverningTokenMint, - ); - require!( ctx.accounts.rewards_program.key() == registrar.rewards_program, MplStakingError::InvalidRewardsProgram @@ -98,16 +99,15 @@ pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> // will close all the token accounts owned by the voter for deposit_vault_info in ctx.remaining_accounts { + let deposit_vault_ta = Account::::try_from(deposit_vault_info) + .map_err(|_| MplStakingError::DeserializationError)?; + registrar.voting_mint_config_index(deposit_vault_ta.mint)?; + require_keys_eq!( *deposit_vault_info.key, - get_associated_token_address( - &ctx.accounts.voter.key(), - &ctx.accounts.deposit_mint.key() - ), + get_associated_token_address(&ctx.accounts.voter.key(), &deposit_vault_ta.mint), ); - let deposit_vault_ta = Account::::try_from(&deposit_vault_info) - .map_err(|_| MplStakingError::DeserializationError)?; require_keys_eq!( deposit_vault_ta.owner, ctx.accounts.voter.key(), diff --git a/programs/voter-stake-registry/src/instructions/create_registrar.rs b/programs/voter-stake-registry/src/instructions/create_registrar.rs index 08d83c6b..d8fbbf48 100644 --- a/programs/voter-stake-registry/src/instructions/create_registrar.rs +++ b/programs/voter-stake-registry/src/instructions/create_registrar.rs @@ -45,7 +45,12 @@ pub struct CreateRegistrar<'info> { /// CHECK: any address is allowed /// This account is responsible for storing money for rewards /// PDA(["vault", reward_pool, reward_mint], reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"vault", reward_pool.key().as_ref(), realm_governing_token_mint.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] reward_vault: UncheckedAccount<'info>, #[account(mut)] diff --git a/programs/voter-stake-registry/src/instructions/create_voter.rs b/programs/voter-stake-registry/src/instructions/create_voter.rs index 1e068b75..b0902af8 100644 --- a/programs/voter-stake-registry/src/instructions/create_voter.rs +++ b/programs/voter-stake-registry/src/instructions/create_voter.rs @@ -55,7 +55,12 @@ pub struct CreateVoter<'info> { /// CHECK: mining PDA will be checked in the rewards contract /// PDA(["mining", mining owner , reward_pool], /// reward_program) - #[account(mut)] + #[account( + mut, + seeds = [b"mining", voter_authority.key().as_ref(), reward_pool.key().as_ref()], + seeds::program = rewards_program.key(), + bump, + )] pub deposit_mining: UncheckedAccount<'info>, /// CHECK: Rewards program ID diff --git a/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so b/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so index eaea9c9f..07990684 100755 Binary files a/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so and b/programs/voter-stake-registry/tests/fixtures/mplx_rewards.so differ diff --git a/programs/voter-stake-registry/tests/program_test/addin.rs b/programs/voter-stake-registry/tests/program_test/addin.rs index 827fb073..7d86d71b 100644 --- a/programs/voter-stake-registry/tests/program_test/addin.rs +++ b/programs/voter-stake-registry/tests/program_test/addin.rs @@ -620,7 +620,6 @@ impl AddinCookie { voter_authority: voter_authority.pubkey(), deposit_mining, reward_pool: registrar.reward_pool, - deposit_mint: voting_mint.mint.pubkey.unwrap(), sol_destination: voter_authority.pubkey(), token_program: spl_token::id(), rewards_program: *rewards_program,