diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index 01cc17d7..46cf3c62 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -18,7 +18,7 @@ defaults: jobs: lint: name: Linter - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 @@ -50,13 +50,13 @@ jobs: tests: name: Tests - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install Linux dependencies - run: sudo apt-get update && sudo apt-get install -y pkg-config build-essential libudev-dev + run: sudo apt-get update && sudo apt-get install -y pkg-config build-essential libudev-dev libssl-dev - name: Install stable Rust uses: actions-rust-lang/setup-rust-toolchain@v1 @@ -90,4 +90,4 @@ jobs: echo "debug = 0" >> Cargo.toml - name: Run tests - run: cargo test-bpf \ No newline at end of file + run: cargo test-sbf \ No newline at end of file diff --git a/program-states/src/error.rs b/program-states/src/error.rs index 57122e99..ad86846a 100644 --- a/program-states/src/error.rs +++ b/program-states/src/error.rs @@ -129,6 +129,6 @@ pub enum MplStakingError { #[msg("Cannot deserialize an account")] DeserializationError, // 6042 / 0x179a - #[msg("Invalid governing token mint")] - InvalidGoverningTokenMint, + #[msg("Invalid associated token accounts, they should match the number mint configs of a registrar and must be passed in the strict order")] + InvalidAssoctiatedTokenAccounts, } diff --git a/program-states/src/state/registrar.rs b/program-states/src/state/registrar.rs index 7caefe4e..2dcec8ca 100644 --- a/program-states/src/state/registrar.rs +++ b/program-states/src/state/registrar.rs @@ -38,10 +38,12 @@ impl Registrar { if !voting_mint_config.in_use() { return Ok(sum); } + let mint_account = mint_accounts .iter() - .find(|a| a.key() == voting_mint_config.mint) + .find(|mint_account| mint_account.key() == voting_mint_config.mint) .ok_or_else(|| error!(MplStakingError::VotingMintNotFound))?; + let mint = Account::::try_from(mint_account)?; sum = sum .checked_add(mint.supply) diff --git a/programs/voter-stake-registry/src/instructions/close_voter.rs b/programs/voter-stake-registry/src/instructions/close_voter.rs index 3b6a52a2..7e0fcc33 100644 --- a/programs/voter-stake-registry/src/instructions/close_voter.rs +++ b/programs/voter-stake-registry/src/instructions/close_voter.rs @@ -1,5 +1,5 @@ use crate::cpi_instructions; -use anchor_lang::prelude::*; +use anchor_lang::{prelude::*, system_program}; use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; use bytemuck::bytes_of_mut; use mplx_staking_states::{ @@ -10,9 +10,15 @@ use mplx_staking_states::{ use spl_associated_token_account::get_associated_token_address; use std::ops::DerefMut; -// Remaining accounts must be all the token token accounts owned by voter, he wants to close, -// they should be writable so that they can be closed and sol required for rent -// can then be sent back to the sol_destination +/// Remaining accounts must be all the token token accounts owned by voter, +/// they should be writable so that they can be closed and sol required for rent +/// can then be sent back to the sol_destination +/// +/// Remaining account must be passed in the order of the mint configs in the registrar +/// that aren't default Pubkey addresses. E.g. +/// Registrar { voting_mint: [Pubkey::default, mint2, mint3] } +/// then remaining accounts must be: +/// [mint2 ATA, mint3 ATA] #[derive(Accounts)] pub struct CloseVoter<'info> { pub registrar: AccountLoader<'info, Registrar>, @@ -67,16 +73,24 @@ pub struct CloseVoter<'info> { /// 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()?; + let filtered_mints = registrar + .voting_mints + .iter() + .filter(|mint_config| mint_config.mint != Pubkey::default()) + .collect::>(); require!( ctx.accounts.rewards_program.key() == registrar.rewards_program, MplStakingError::InvalidRewardsProgram ); - require!( registrar.reward_pool == ctx.accounts.reward_pool.key(), MplStakingError::InvalidRewardPool ); + require!( + ctx.remaining_accounts.len() >= filtered_mints.len(), + MplStakingError::InvalidAssoctiatedTokenAccounts + ); { let voter = ctx.accounts.voter.load()?; @@ -97,31 +111,37 @@ pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> let voter_seeds = voter_seeds!(voter); - // 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)?; + let calculated_atas_to_close = filtered_mints.into_iter().map(|voting_mint_config| { + get_associated_token_address(&ctx.accounts.voter.key(), &voting_mint_config.mint) + }); + for (index, calculated_ata_to_close) in calculated_atas_to_close.enumerate() { + let ata_info_to_close = ctx.remaining_accounts[index].to_account_info(); require_keys_eq!( - *deposit_vault_info.key, - get_associated_token_address(&ctx.accounts.voter.key(), &deposit_vault_ta.mint), + *ata_info_to_close.key, + calculated_ata_to_close, + MplStakingError::InvalidAssoctiatedTokenAccounts ); + if ata_info_to_close.data_is_empty() + && ata_info_to_close.owner == &system_program::ID + && **ata_info_to_close.lamports.borrow() == 0 + { + continue; + } + + let ata = Account::::try_from(&ata_info_to_close) + .map_err(|_| MplStakingError::DeserializationError)?; require_keys_eq!( - deposit_vault_ta.owner, + ata.owner, ctx.accounts.voter.key(), MplStakingError::InvalidAuthority ); - require_eq!( - deposit_vault_ta.amount, - 0, - MplStakingError::VaultTokenNonZero - ); + require_eq!(ata.amount, 0, MplStakingError::VaultTokenNonZero); // close vault let cpi_close_accounts = CloseAccount { - account: deposit_vault_ta.to_account_info(), + account: ata.to_account_info(), destination: ctx.accounts.sol_destination.to_account_info(), authority: ctx.accounts.voter.to_account_info(), }; @@ -131,7 +151,7 @@ pub fn close_voter<'info>(ctx: Context<'_, '_, '_, 'info, CloseVoter<'info>>) -> &[voter_seeds], ))?; - deposit_vault_ta.exit(ctx.program_id)?; + ata.exit(ctx.program_id)?; } } diff --git a/programs/voter-stake-registry/src/instructions/configure_voting_mint.rs b/programs/voter-stake-registry/src/instructions/configure_voting_mint.rs index afa64124..2e413d48 100644 --- a/programs/voter-stake-registry/src/instructions/configure_voting_mint.rs +++ b/programs/voter-stake-registry/src/instructions/configure_voting_mint.rs @@ -24,7 +24,7 @@ pub struct ConfigureVotingMint<'info> { /// exchange rate per mint. /// /// * `idx`: index of the rate to be set -/// * `grand_authority`: The keypair that might be an authority for Grand/Clawback +/// * `grant_authority`: The keypair that might be an authority for Grant/Clawback /// /// This instruction can be called several times for the same mint and index to /// change the voting mint configuration. diff --git a/programs/voter-stake-registry/tests/close_voter.rs b/programs/voter-stake-registry/tests/close_voter.rs new file mode 100644 index 00000000..a0fc1ee5 --- /dev/null +++ b/programs/voter-stake-registry/tests/close_voter.rs @@ -0,0 +1,742 @@ +use anchor_spl::token::TokenAccount; +use assert_custom_on_chain_error::AssertCustomOnChainErr; +use mplx_staking_states::{ + error::MplStakingError, + state::{LockupKind, LockupPeriod}, +}; +use program_test::*; +use solana_program_test::*; +use solana_sdk::{signature::Keypair, signer::Signer, transport::TransportError}; + +mod program_test; + +#[tokio::test] +async fn two_the_same_voting_mints_fail() -> Result<(), TransportError> { + let context = TestContext::new().await; + + let payer = &context.users[0].key; + let realm_authority = Keypair::new(); + let realm = context + .governance + .create_realm( + "testrealm", + realm_authority.pubkey(), + &context.mints[0], + payer, + &context.addin.program_id, + ) + .await; + + let deposit_authority = &context.users[1].key; + let token_owner_record = realm + .create_token_owner_record(deposit_authority.pubkey(), payer) + .await; + + let fill_authority = Keypair::from_bytes(&context.users[3].key.to_bytes()).unwrap(); + let distribution_authority = Keypair::new(); + let (registrar, rewards_pool) = context + .addin + .create_registrar( + &realm, + &realm_authority, + payer, + &fill_authority.pubkey(), + &distribution_authority.pubkey(), + &context.rewards.program_id, + ) + .await; + + let mngo_voting_mint = context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 0, + &context.mints[0], + None, + Some(&[context.mints[1].pubkey.unwrap()]), + ) + .await; + + context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 1, + &context.mints[1], + None, + Some(&[context.mints[0].pubkey.unwrap()]), + ) + .await; + + // TODO: ??? voter_authority == deposit_authority ??? + let voter_authority = deposit_authority; + let (deposit_mining, _) = find_deposit_mining_addr( + &context.rewards.program_id, + &voter_authority.pubkey(), + &rewards_pool, + ); + + let voter = context + .addin + .create_voter( + ®istrar, + &token_owner_record, + voter_authority, + payer, + &rewards_pool, + &deposit_mining, + &context.rewards.program_id, + ) + .await; + + let balance_initial = voter.deposit_amount(&context.solana, 0).await; + assert_eq!(balance_initial, 0); + + context + .addin + .create_deposit_entry( + ®istrar, + &voter, + &voter, + &mngo_voting_mint, + 0, + LockupKind::None, + LockupPeriod::None, + ) + .await?; + + // test deposit and withdraw + let reference_account = context.users[1].token_accounts[0]; + context + .addin + .deposit( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + context + .addin + .withdraw( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + let mints = &[context.mints[0].clone(), context.mints[0].clone()]; + + context + .addin + .close_voter( + ®istrar, + &voter, + &mints[..], + voter_authority, + &context.rewards.program_id, + ) + .await + .assert_on_chain_err(MplStakingError::InvalidAssoctiatedTokenAccounts); + + Ok(()) +} + +#[tokio::test] +async fn zero_ata_passed_instead_of_two() -> Result<(), TransportError> { + let context = TestContext::new().await; + + let payer = &context.users[0].key; + let realm_authority = Keypair::new(); + let realm = context + .governance + .create_realm( + "testrealm", + realm_authority.pubkey(), + &context.mints[0], + payer, + &context.addin.program_id, + ) + .await; + + let deposit_authority = &context.users[1].key; + let token_owner_record = realm + .create_token_owner_record(deposit_authority.pubkey(), payer) + .await; + + let fill_authority = Keypair::from_bytes(&context.users[3].key.to_bytes()).unwrap(); + let distribution_authority = Keypair::new(); + let (registrar, rewards_pool) = context + .addin + .create_registrar( + &realm, + &realm_authority, + payer, + &fill_authority.pubkey(), + &distribution_authority.pubkey(), + &context.rewards.program_id, + ) + .await; + + let mngo_voting_mint = context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 0, + &context.mints[0], + None, + Some(&[context.mints[1].pubkey.unwrap()]), + ) + .await; + + context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 1, + &context.mints[1], + None, + Some(&[context.mints[0].pubkey.unwrap()]), + ) + .await; + + // TODO: ??? voter_authority == deposit_authority ??? + let voter_authority = deposit_authority; + let (deposit_mining, _) = find_deposit_mining_addr( + &context.rewards.program_id, + &voter_authority.pubkey(), + &rewards_pool, + ); + + let voter = context + .addin + .create_voter( + ®istrar, + &token_owner_record, + voter_authority, + payer, + &rewards_pool, + &deposit_mining, + &context.rewards.program_id, + ) + .await; + + let balance_initial = voter.deposit_amount(&context.solana, 0).await; + assert_eq!(balance_initial, 0); + + context + .addin + .create_deposit_entry( + ®istrar, + &voter, + &voter, + &mngo_voting_mint, + 0, + LockupKind::None, + LockupPeriod::None, + ) + .await?; + + // test deposit and withdraw + let reference_account = context.users[1].token_accounts[0]; + context + .addin + .deposit( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + context + .addin + .withdraw( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + let mints = &[]; + context + .addin + .close_voter( + ®istrar, + &voter, + &mints[..], + voter_authority, + &context.rewards.program_id, + ) + .await + .assert_on_chain_err(MplStakingError::InvalidAssoctiatedTokenAccounts); + + Ok(()) +} + +#[tokio::test] +async fn one_ata_passed_instead_of_two() -> Result<(), TransportError> { + let context = TestContext::new().await; + + let payer = &context.users[0].key; + let realm_authority = Keypair::new(); + let realm = context + .governance + .create_realm( + "testrealm", + realm_authority.pubkey(), + &context.mints[0], + payer, + &context.addin.program_id, + ) + .await; + + let deposit_authority = &context.users[1].key; + let token_owner_record = realm + .create_token_owner_record(deposit_authority.pubkey(), payer) + .await; + + let fill_authority = Keypair::from_bytes(&context.users[3].key.to_bytes()).unwrap(); + let distribution_authority = Keypair::new(); + let (registrar, rewards_pool) = context + .addin + .create_registrar( + &realm, + &realm_authority, + payer, + &fill_authority.pubkey(), + &distribution_authority.pubkey(), + &context.rewards.program_id, + ) + .await; + + let mngo_voting_mint = context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 0, + &context.mints[0], + None, + Some(&[context.mints[1].pubkey.unwrap()]), + ) + .await; + + context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 1, + &context.mints[1], + None, + Some(&[context.mints[0].pubkey.unwrap()]), + ) + .await; + + // TODO: ??? voter_authority == deposit_authority ??? + let voter_authority = deposit_authority; + let (deposit_mining, _) = find_deposit_mining_addr( + &context.rewards.program_id, + &voter_authority.pubkey(), + &rewards_pool, + ); + + let voter = context + .addin + .create_voter( + ®istrar, + &token_owner_record, + voter_authority, + payer, + &rewards_pool, + &deposit_mining, + &context.rewards.program_id, + ) + .await; + + let balance_initial = voter.deposit_amount(&context.solana, 0).await; + assert_eq!(balance_initial, 0); + + context + .addin + .create_deposit_entry( + ®istrar, + &voter, + &voter, + &mngo_voting_mint, + 0, + LockupKind::None, + LockupPeriod::None, + ) + .await?; + + // test deposit and withdraw + let reference_account = context.users[1].token_accounts[0]; + context + .addin + .deposit( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + context + .addin + .withdraw( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + let mints = &[context.mints[0].clone()]; + context + .addin + .close_voter( + ®istrar, + &voter, + &mints[..], + voter_authority, + &context.rewards.program_id, + ) + .await + .assert_on_chain_err(MplStakingError::InvalidAssoctiatedTokenAccounts); + + Ok(()) +} + +#[tokio::test] +async fn success() -> Result<(), TransportError> { + let context = TestContext::new().await; + + let payer = &context.users[0].key; + let realm_authority = Keypair::new(); + let realm = context + .governance + .create_realm( + "testrealm", + realm_authority.pubkey(), + &context.mints[0], + payer, + &context.addin.program_id, + ) + .await; + + let deposit_authority = &context.users[1].key; + let token_owner_record = realm + .create_token_owner_record(deposit_authority.pubkey(), payer) + .await; + + let fill_authority = Keypair::from_bytes(&context.users[3].key.to_bytes()).unwrap(); + let distribution_authority = Keypair::new(); + let (registrar, rewards_pool) = context + .addin + .create_registrar( + &realm, + &realm_authority, + payer, + &fill_authority.pubkey(), + &distribution_authority.pubkey(), + &context.rewards.program_id, + ) + .await; + + let mngo_voting_mint = context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 0, + &context.mints[0], + None, + Some(&[context.mints[1].pubkey.unwrap()]), + ) + .await; + + context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 1, + &context.mints[1], + None, + Some(&[context.mints[0].pubkey.unwrap()]), + ) + .await; + + // TODO: ??? voter_authority == deposit_authority ??? + let voter_authority = deposit_authority; + let (deposit_mining, _) = find_deposit_mining_addr( + &context.rewards.program_id, + &voter_authority.pubkey(), + &rewards_pool, + ); + + let voter = context + .addin + .create_voter( + ®istrar, + &token_owner_record, + voter_authority, + payer, + &rewards_pool, + &deposit_mining, + &context.rewards.program_id, + ) + .await; + + let balance_initial = voter.deposit_amount(&context.solana, 0).await; + assert_eq!(balance_initial, 0); + + context + .addin + .create_deposit_entry( + ®istrar, + &voter, + &voter, + &mngo_voting_mint, + 0, + LockupKind::None, + LockupPeriod::None, + ) + .await?; + + // test deposit and withdraw + let reference_account = context.users[1].token_accounts[0]; + context + .addin + .deposit( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + context + .addin + .withdraw( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + let lamports_before = context + .solana + .context + .borrow_mut() + .banks_client + .get_balance(voter_authority.pubkey()) + .await?; + + context + .addin + .close_voter( + ®istrar, + &voter, + &context.mints[..], + voter_authority, + &context.rewards.program_id, + ) + .await?; + + let lamports_after = context + .solana + .context + .borrow_mut() + .banks_client + .get_balance(voter_authority.pubkey()) + .await?; + assert!(lamports_after > lamports_before); + + Ok(()) +} + +#[tokio::test] +async fn wrong_order_of_passed_in_ata() -> Result<(), TransportError> { + let context = TestContext::new().await; + + let payer = &context.users[0].key; + let realm_authority = Keypair::new(); + let realm = context + .governance + .create_realm( + "testrealm", + realm_authority.pubkey(), + &context.mints[0], + payer, + &context.addin.program_id, + ) + .await; + + let deposit_authority = &context.users[1].key; + let token_owner_record = realm + .create_token_owner_record(deposit_authority.pubkey(), payer) + .await; + + let fill_authority = Keypair::from_bytes(&context.users[3].key.to_bytes()).unwrap(); + let distribution_authority = Keypair::new(); + let (registrar, rewards_pool) = context + .addin + .create_registrar( + &realm, + &realm_authority, + payer, + &fill_authority.pubkey(), + &distribution_authority.pubkey(), + &context.rewards.program_id, + ) + .await; + + let mngo_voting_mint = context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 0, + &context.mints[0], + None, + Some(&[context.mints[1].pubkey.unwrap()]), + ) + .await; + + context + .addin + .configure_voting_mint( + ®istrar, + &realm_authority, + payer, + 1, + &context.mints[1], + None, + Some(&[context.mints[0].pubkey.unwrap()]), + ) + .await; + + // TODO: ??? voter_authority == deposit_authority ??? + let voter_authority = deposit_authority; + let (deposit_mining, _) = find_deposit_mining_addr( + &context.rewards.program_id, + &voter_authority.pubkey(), + &rewards_pool, + ); + + let voter = context + .addin + .create_voter( + ®istrar, + &token_owner_record, + voter_authority, + payer, + &rewards_pool, + &deposit_mining, + &context.rewards.program_id, + ) + .await; + + let balance_initial = voter.deposit_amount(&context.solana, 0).await; + assert_eq!(balance_initial, 0); + + context + .addin + .create_deposit_entry( + ®istrar, + &voter, + &voter, + &mngo_voting_mint, + 0, + LockupKind::None, + LockupPeriod::None, + ) + .await?; + + // test deposit and withdraw + let reference_account = context.users[1].token_accounts[0]; + context + .addin + .deposit( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + context + .addin + .withdraw( + ®istrar, + &voter, + &mngo_voting_mint, + deposit_authority, + reference_account, + 0, + 10000, + ) + .await?; + + let mints = &[context.mints[1].clone(), context.mints[0].clone()]; + context + .addin + .close_voter( + ®istrar, + &voter, + &mints[..], + voter_authority, + &context.rewards.program_id, + ) + .await + .assert_on_chain_err(MplStakingError::InvalidAssoctiatedTokenAccounts); + + Ok(()) +} diff --git a/programs/voter-stake-registry/tests/program_test/addin.rs b/programs/voter-stake-registry/tests/program_test/addin.rs index 7d86d71b..c0316700 100644 --- a/programs/voter-stake-registry/tests/program_test/addin.rs +++ b/programs/voter-stake-registry/tests/program_test/addin.rs @@ -1,4 +1,5 @@ use crate::*; +use anchor_lang::prelude::AccountMeta; use mplx_staking_states::state::Voter; use solana_sdk::{ instruction::Instruction, @@ -371,7 +372,7 @@ impl AddinCookie { lockup_kind: LockupKind, period: LockupPeriod, ) -> std::result::Result<(), BanksClientError> { - let vault = voter.vault_address(voting_mint); + let vault = voter.vault_address(voting_mint.mint.pubkey.as_ref().unwrap()); let data = anchor_lang::InstructionData::data(&mpl_staking::instruction::CreateDepositEntry { @@ -418,7 +419,7 @@ impl AddinCookie { deposit_entry_index: u8, amount: u64, ) -> std::result::Result<(), BanksClientError> { - let vault = voter.vault_address(voting_mint); + let vault = voter.vault_address(voting_mint.mint.pubkey.as_ref().unwrap()); let data = anchor_lang::InstructionData::data(&mpl_staking::instruction::Deposit { deposit_entry_index, @@ -563,7 +564,7 @@ impl AddinCookie { deposit_entry_index: u8, amount: u64, ) -> std::result::Result<(), BanksClientError> { - let vault = voter.vault_address(voting_mint); + let vault = voter.vault_address(voting_mint.mint.pubkey.as_ref().unwrap()); let data = anchor_lang::InstructionData::data(&mpl_staking::instruction::Withdraw { deposit_entry_index, @@ -599,12 +600,10 @@ impl AddinCookie { &self, registrar: &RegistrarCookie, voter: &VoterCookie, - voting_mint: &VotingMintConfigCookie, + voting_mints: &[MintCookie], voter_authority: &Keypair, rewards_program: &Pubkey, ) -> std::result::Result<(), BanksClientError> { - let vault = voter.vault_address(voting_mint); - let (deposit_mining, _) = find_deposit_mining_addr( rewards_program, &voter_authority.pubkey(), @@ -626,7 +625,11 @@ impl AddinCookie { }, None, ); - accounts.push(anchor_lang::prelude::AccountMeta::new(vault, false)); + let vaults = voting_mints + .iter() + .map(|mint| AccountMeta::new(voter.vault_address(mint.pubkey.as_ref().unwrap()), false)) + .collect::>(); + accounts.extend(vaults); let instructions = vec![Instruction { program_id: self.program_id, @@ -854,7 +857,7 @@ impl AddinCookie { impl VotingMintConfigCookie { pub async fn vault_balance(&self, solana: &SolanaCookie, voter: &VoterCookie) -> u64 { - let vault = voter.vault_address(self); + let vault = voter.vault_address(self.mint.pubkey.as_ref().unwrap()); solana.get_account::(vault).await.amount } } @@ -869,10 +872,7 @@ impl VoterCookie { voter.deposits[deposit_id as usize].amount_deposited_native } - pub fn vault_address(&self, mint: &VotingMintConfigCookie) -> Pubkey { - spl_associated_token_account::get_associated_token_address( - &self.address, - &mint.mint.pubkey.unwrap(), - ) + pub fn vault_address(&self, mint: &Pubkey) -> Pubkey { + spl_associated_token_account::get_associated_token_address(&self.address, mint) } } diff --git a/programs/voter-stake-registry/tests/program_test/mod.rs b/programs/voter-stake-registry/tests/program_test/mod.rs index 8b83345e..7a6b1af2 100644 --- a/programs/voter-stake-registry/tests/program_test/mod.rs +++ b/programs/voter-stake-registry/tests/program_test/mod.rs @@ -141,7 +141,7 @@ impl TestContext { unit: 10u64.pow(6) as f64, base_lot: 100_f64, quote_lot: 10_f64, - pubkey: None, //Some(mngo_token::ID), + pubkey: Some(Pubkey::new_unique()), //Some(mngo_token::ID), authority: Keypair::new(), }, // symbol: "MNGO".to_string() MintCookie { @@ -150,20 +150,14 @@ impl TestContext { unit: 10u64.pow(6) as f64, base_lot: 0 as f64, quote_lot: 0 as f64, - pubkey: None, + pubkey: Some(Pubkey::new_unique()), authority: Keypair::new(), }, // symbol: "USDC".to_string() ]; // Add mints in loop for mint in mints.iter_mut() { - let mint_pk = if mint.pubkey.is_none() { - Pubkey::new_unique() - } else { - mint.pubkey.unwrap() - }; - test.add_packable_account( - mint_pk, + mint.pubkey.unwrap(), u32::MAX as u64, &Mint { is_initialized: true, @@ -173,7 +167,7 @@ impl TestContext { }, &spl_token::id(), ); - mint.pubkey = Some(mint_pk); + mint.pubkey = Some(mint.pubkey.unwrap()); } let quote_index = mints.len() - 1; diff --git a/programs/voter-stake-registry/tests/test_basic.rs b/programs/voter-stake-registry/tests/test_basic.rs index 23081ae5..0d8fe61c 100644 --- a/programs/voter-stake-registry/tests/test_basic.rs +++ b/programs/voter-stake-registry/tests/test_basic.rs @@ -224,7 +224,7 @@ async fn test_basic() -> Result<(), TransportError> { .close_voter( ®istrar, &voter, - &mngo_voting_mint, + &context.mints[..], voter_authority, &context.rewards.program_id, ) @@ -380,7 +380,7 @@ async fn close_voter_with_locked_tokens_should_fail() -> Result<(), TransportErr .close_voter( ®istrar, &voter, - &mngo_voting_mint, + &context.mints[..], voter_authority, &context.rewards.program_id, )