mango-v4/programs/mango-v4/src/instructions/token_deregister.rs

94 lines
3.1 KiB
Rust

use anchor_lang::prelude::*;
use anchor_spl::token::{self, CloseAccount, Token};
use crate::{accounts_zerocopy::LoadZeroCopyRef, state::*};
use anchor_lang::AccountsClose;
#[derive(Accounts)]
#[instruction(token_index: TokenIndex)]
pub struct TokenDeregister<'info> {
#[account(
constraint = group.load()?.testing == 1,
has_one = admin,
)]
pub group: AccountLoader<'info, Group>,
pub admin: Signer<'info>,
// match mint info to bank
#[account(
mut,
has_one = group,
constraint = mint_info.load()?.token_index == token_index,
close = sol_destination
)]
pub mint_info: AccountLoader<'info, MintInfo>,
#[account(mut)]
/// CHECK: target for account rent needs no checks
pub sol_destination: UncheckedAccount<'info>,
pub token_program: Program<'info, Token>,
}
#[allow(clippy::too_many_arguments)]
pub fn token_deregister<'key, 'accounts, 'remaining, 'info>(
ctx: Context<'key, 'accounts, 'remaining, 'info, TokenDeregister<'info>>,
token_index: TokenIndex,
) -> Result<()> {
let mint_info = ctx.accounts.mint_info.load()?;
{
let total_banks = mint_info
.banks
.iter()
.filter(|bank| *bank != &Pubkey::default())
.count();
require_eq!(total_banks * 2, ctx.remaining_accounts.len());
}
let group = ctx.accounts.group.load()?;
let group_seeds = group_seeds!(group);
// todo: use itertools::chunks(2)
for i in (0..ctx.remaining_accounts.len()).step_by(2) {
let vault_ai = &ctx.remaining_accounts[i + 1];
let bank_ai = &ctx.remaining_accounts[i];
require_eq!(bank_ai.key(), mint_info.banks[i / 2]);
require_eq!(vault_ai.key(), mint_info.vaults[i / 2]);
// todo: these checks might be superfluous, after above 2 checks
{
let bank = bank_ai.load::<Bank>()?;
require_keys_eq!(bank.group, ctx.accounts.group.key());
require_eq!(bank.token_index, token_index);
require_keys_eq!(bank.vault, vault_ai.key());
}
// note: vault seems to need closing before bank, weird solana oddity
// todo: add test to see if we can even close more than one bank in same ix
// close vault
let cpi_accounts = CloseAccount {
account: vault_ai.to_account_info(),
destination: ctx.accounts.sol_destination.to_account_info(),
authority: ctx.accounts.group.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
token::close_account(CpiContext::new_with_signer(
cpi_program,
cpi_accounts,
&[group_seeds],
))?;
vault_ai.exit(ctx.program_id)?;
}
// Close banks in a second step, because the cpi calls above don't like when someone
// else touches sol_destination.lamports in between.
for i in (0..ctx.remaining_accounts.len()).step_by(2) {
let bank_ai = &ctx.remaining_accounts[i];
let bank_al: AccountLoader<Bank> = AccountLoader::try_from(bank_ai)?;
bank_al.close(ctx.accounts.sol_destination.to_account_info())?;
}
Ok(())
}