Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MTG-758] [MTG-802] Rectify close_voter ix #41

Merged
merged 5 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defaults:
jobs:
lint:
name: Linter
runs-on: ubuntu-latest
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -90,4 +90,4 @@ jobs:
echo "debug = 0" >> Cargo.toml

- name: Run tests
run: cargo test-bpf
run: cargo test-sbf
4 changes: 2 additions & 2 deletions program-states/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
4 changes: 3 additions & 1 deletion program-states/src/state/registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Mint>::try_from(mint_account)?;
sum = sum
.checked_add(mint.supply)
Expand Down
60 changes: 40 additions & 20 deletions programs/voter-stake-registry/src/instructions/close_voter.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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>,
Expand Down Expand Up @@ -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::<Vec<_>>();

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()?;
Expand All @@ -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::<TokenAccount>::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::<TokenAccount>::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(),
};
Expand All @@ -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)?;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading