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:
parent
cce3836b09
commit
1c02ccd21d
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue