Transfer dust from bank vaults to a dust account, without this we wont be able to close bank vaults (#88)

This commit is contained in:
microwavedcola1 2022-06-29 09:11:14 +02:00 committed by GitHub
parent cce3836b09
commit 1c02ccd21d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 97 deletions

View File

@ -1,5 +1,5 @@
use anchor_lang::prelude::*;
use anchor_spl::token::{self, CloseAccount, Token};
use anchor_spl::token::{self, CloseAccount, Token, TokenAccount};
use crate::{accounts_zerocopy::LoadZeroCopyRef, state::*};
use anchor_lang::AccountsClose;
@ -23,6 +23,9 @@ pub struct TokenDeregister<'info> {
)]
pub mint_info: AccountLoader<'info, MintInfo>,
#[account(mut)]
pub dust_vault: Account<'info, TokenAccount>,
#[account(mut)]
/// CHECK: target for account rent needs no checks
pub sol_destination: UncheckedAccount<'info>,
@ -48,10 +51,12 @@ pub fn token_deregister<'key, 'accounts, 'remaining, 'info>(
let group = ctx.accounts.group.load()?;
let group_seeds = group_seeds!(group);
let dust_vault_ai = &ctx.accounts.dust_vault;
// 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];
let vault_ai = &ctx.remaining_accounts[i + 1];
require_eq!(bank_ai.key(), mint_info.banks[i / 2]);
require_eq!(vault_ai.key(), mint_info.vaults[i / 2]);
@ -64,9 +69,24 @@ pub fn token_deregister<'key, 'accounts, 'remaining, 'info>(
require_keys_eq!(bank.vault, vault_ai.key());
}
// transfer dust to another token account
let amount = Account::<TokenAccount>::try_from(vault_ai).unwrap().amount;
if amount > 0 {
token::transfer(
{
let accounts = token::Transfer {
from: vault_ai.to_account_info(),
to: dust_vault_ai.to_account_info(),
authority: ctx.accounts.group.to_account_info(),
};
CpiContext::new(ctx.accounts.token_program.to_account_info(), accounts)
.with_signer(&[group_seeds])
},
amount,
)?;
}
// 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(),

View File

@ -13,6 +13,7 @@ use solana_program::instruction::Instruction;
use solana_sdk::instruction;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transport::TransportError;
use spl_associated_token_account::get_associated_token_address;
use std::str::FromStr;
use std::sync::Arc;
@ -970,6 +971,7 @@ pub struct TokenDeregisterInstruction<'keypair> {
pub mint_info: Pubkey,
pub banks: Vec<Pubkey>,
pub vaults: Vec<Pubkey>,
pub dust_vault: Pubkey,
pub token_index: TokenIndex,
pub sol_destination: Pubkey,
}
@ -991,6 +993,7 @@ impl<'keypair> ClientInstruction for TokenDeregisterInstruction<'keypair> {
admin: self.admin.pubkey(),
group: self.group,
mint_info: self.mint_info,
dust_vault: self.dust_vault,
sol_destination: self.sol_destination,
token_program: Token::id(),
};
@ -1018,7 +1021,6 @@ impl<'keypair> ClientInstruction for TokenDeregisterInstruction<'keypair> {
})
.flat_map(|vec| vec.into_iter())
.collect::<Vec<_>>();
dbg!(ams.clone());
instruction.accounts.append(&mut ams);
(accounts, instruction)

View File

@ -196,15 +196,13 @@ impl SolanaCookie {
#[allow(dead_code)]
pub async fn get_account_opt<T: AccountDeserialize>(&self, address: Pubkey) -> Option<T> {
let account = self
.context
self.context
.borrow_mut()
.banks_client
.get_account(address)
.await
.unwrap()
.unwrap();
println!("{:#?}", account.owner);
let data = self.get_account_data(address).await?;
let mut data_slice: &[u8] = &data;

View File

@ -194,6 +194,7 @@ async fn test_basic() -> Result<(), TransportError> {
let mint_info: MintInfo = solana.get_account(tokens[0].mint_info).await;
mint_info.vaults.to_vec()
},
dust_vault: payer_mint0_account,
token_index: bank_data.token_index,
sol_destination: payer.pubkey(),
},

View File

@ -124,7 +124,6 @@ export class MangoClient {
const groups = (await this.program.account.group.all(filters)).map(
(tuple) => Group.from(tuple.publicKey, tuple.account),
);
console.log(groups);
await groups[0].reloadAll(this);
return groups[0];
}
@ -187,14 +186,34 @@ export class MangoClient {
tokenName: string,
): Promise<TransactionSignature> {
const bank = group.banksMap.get(tokenName)!;
const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey;
const dustVaultPk = await getAssociatedTokenAddress(bank.mint, adminPk);
const ai = await this.program.provider.connection.getAccountInfo(
dustVaultPk,
);
if (!ai) {
const tx = new Transaction();
tx.add(
Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
bank.mint,
dustVaultPk,
adminPk,
adminPk,
),
);
await this.program.provider.sendAndConfirm(tx);
}
return await this.program.methods
.tokenDeregister(bank.tokenIndex)
.accounts({
group: group.publicKey,
admin: adminPk,
mintInfo: group.mintInfosMap.get(bank.tokenIndex)?.publicKey,
dustVault: dustVaultPk,
solDestination: (this.program.provider as AnchorProvider).wallet
.publicKey,
})

View File

@ -420,6 +420,11 @@ export type MangoV4 = {
"isMut": true,
"isSigner": false
},
{
"name": "dustVault",
"isMut": true,
"isSigner": false
},
{
"name": "solDestination",
"isMut": true,
@ -4001,6 +4006,11 @@ export const IDL: MangoV4 = {
"isMut": true,
"isSigner": false
},
{
"name": "dustVault",
"isMut": true,
"isSigner": false
},
{
"name": "solDestination",
"isMut": true,

View File

@ -36,64 +36,45 @@ async function main() {
// close stub oracle
const usdcDevnetMint = new PublicKey(DEVNET_MINTS.get('USDC')!);
try {
const usdcDevnetOracle = await client.getStubOracle(
group,
usdcDevnetMint,
)[0];
let sig = await client.closeStubOracle(group, usdcDevnetOracle.publicKey);
console.log(
`Closed USDC stub oracle, sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
);
} catch (error) {
console.error(error);
}
const usdcDevnetOracle = (
await client.getStubOracle(group, usdcDevnetMint)
)[0];
sig = await client.closeStubOracle(group, usdcDevnetOracle.publicKey);
console.log(
`Closed USDC stub oracle, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
);
// close all bank
for (const bank of group.banksMap.values()) {
try {
sig = await client.tokenDeregister(group, bank.name);
console.log(
`Removed token ${bank.name}, sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
);
} catch (error) {
console.error(error);
}
sig = await client.tokenDeregister(group, bank.name);
console.log(
`Removed token ${bank.name}, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
);
}
// deregister all serum markets
for (const market of group.serum3MarketsMap.values()) {
try {
sig = await client.serum3deregisterMarket(group, market.name);
console.log(
`Deregistered serum market ${market.name}, sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
);
} catch (error) {
console.error(error);
}
sig = await client.serum3deregisterMarket(group, market.name);
console.log(
`Deregistered serum market ${market.name}, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
);
}
// close all perp markets
for (const market of group.perpMarketsMap.values()) {
try {
sig = await client.perpCloseMarket(group, market.name);
console.log(
`Closed perp market ${market.name}, sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
);
} catch (error) {
console.error(error);
}
sig = await client.perpCloseMarket(group, market.name);
console.log(
`Closed perp market ${market.name}, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
);
}
// finally, close the group
try {
sig = await client.closeGroup(group);
console.log(
`Closed group, sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
);
} catch (error) {
console.error(error);
}
sig = await client.closeGroup(group);
console.log(
`Closed group, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
);
process.exit();
}

View File

@ -36,63 +36,42 @@ async function main() {
let sig;
// close stub oracle
const usdcDevnetMint = new PublicKey(MAINNET_MINTS.get('USDC')!);
try {
const usdcDevnetOracle = await client.getStubOracle(
group,
usdcDevnetMint,
)[0];
let sig = await client.closeStubOracle(group, usdcDevnetOracle.publicKey);
console.log(
`Closed USDC stub oracle, sig https://explorer.solana.com/address/${sig}`,
);
} catch (error) {
console.error(error);
}
const usdcMainnetBetaMint = new PublicKey(MAINNET_MINTS.get('USDC')!);
const usdcMainnetBetaOracle = (
await client.getStubOracle(group, usdcMainnetBetaMint)
)[0];
sig = await client.closeStubOracle(group, usdcMainnetBetaOracle.publicKey);
console.log(
`Closed USDC stub oracle, sig https://explorer.solana.com/tx/${sig}`,
);
// close all bank
for (const bank of group.banksMap.values()) {
try {
sig = await client.tokenDeregister(group, bank.name);
console.log(
`Removed token ${bank.name}, sig https://explorer.solana.com/address/${sig}`,
);
} catch (error) {
console.error(error);
}
sig = await client.tokenDeregister(group, bank.name);
console.log(
`Removed token ${bank.name}, sig https://explorer.solana.com/tx/${sig}`,
);
}
// deregister all serum markets
for (const market of group.serum3MarketsMap.values()) {
try {
sig = await client.serum3deregisterMarket(group, market.name);
console.log(
`Deregistered serum market ${market.name}, sig https://explorer.solana.com/address/${sig}`,
);
} catch (error) {
console.error(error);
}
sig = await client.serum3deregisterMarket(group, market.name);
console.log(
`Deregistered serum market ${market.name}, sig https://explorer.solana.com/tx/${sig}`,
);
}
// close all perp markets
for (const market of group.perpMarketsMap.values()) {
try {
sig = await client.perpCloseMarket(group, market.name);
console.log(
`Closed perp market ${market.name}, sig https://explorer.solana.com/address/${sig}`,
);
} catch (error) {
console.error(error);
}
sig = await client.perpCloseMarket(group, market.name);
console.log(
`Closed perp market ${market.name}, sig https://explorer.solana.com/tx/${sig}`,
);
}
// finally, close the group
try {
sig = await client.closeGroup(group);
console.log(`Closed group, sig https://explorer.solana.com/address/${sig}`);
} catch (error) {
console.error(error);
}
sig = await client.closeGroup(group);
console.log(`Closed group, sig https://explorer.solana.com/tx/${sig}`);
process.exit();
}