diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 2b11d36d..921435b7 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -314,6 +314,9 @@ pub enum IdlCommand { #[clap(short, long)] filepath: String, }, + Close { + program_id: Pubkey, + }, /// Writes an IDL into a buffer account. This can be used with SetBuffer /// to perform an upgrade. WriteBuffer { @@ -1522,6 +1525,7 @@ fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> { program_id, filepath, } => idl_init(cfg_override, program_id, filepath), + IdlCommand::Close { program_id } => idl_close(cfg_override, program_id), IdlCommand::WriteBuffer { program_id, filepath, @@ -1564,6 +1568,17 @@ fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: Str }) } +fn idl_close(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> { + with_workspace(cfg_override, |cfg| { + let idl_address = IdlAccount::address(&program_id); + idl_close_account(&cfg, &program_id, idl_address)?; + + println!("Idl account closed: {:?}", idl_address); + + Ok(()) + }) +} + fn idl_write_buffer( cfg_override: &ConfigOverride, program_id: Pubkey, @@ -1737,6 +1752,44 @@ fn idl_erase_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Res Ok(()) } +fn idl_close_account(cfg: &Config, program_id: &Pubkey, idl_address: Pubkey) -> Result<()> { + let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string()) + .map_err(|_| anyhow!("Unable to read keypair file"))?; + let url = cluster_url(cfg, &cfg.test_validator); + let client = RpcClient::new(url); + + // Instruction accounts. + let accounts = vec![ + AccountMeta::new(idl_address, false), + AccountMeta::new_readonly(keypair.pubkey(), true), + AccountMeta::new(keypair.pubkey(), true), + ]; + // Instruction. + let ix = Instruction { + program_id: *program_id, + accounts, + data: { serialize_idl_ix(anchor_lang::idl::IdlInstruction::Close {})? }, + }; + // Send transaction. + let latest_hash = client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&keypair.pubkey()), + &[&keypair], + latest_hash, + ); + client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + CommitmentConfig::confirmed(), + RpcSendTransactionConfig { + skip_preflight: true, + ..RpcSendTransactionConfig::default() + }, + )?; + + Ok(()) +} + // Write the idl to the account buffer, chopping up the IDL into pieces // and sending multiple transactions in the event the IDL doesn't fit into // a single transaction. @@ -2517,7 +2570,12 @@ fn create_idl_account( // Run `Create instruction. { let data = serialize_idl_ix(anchor_lang::idl::IdlInstruction::Create { - data_len: (idl_data.len() as u64) * 2, // Double for future growth. + // Double for future growth. + data_len: if (idl_data.len() as u64) * 2 > 10240 { + 10196 // note: 44 bytes get added somewhere along the line + } else { + (idl_data.len() as u64) * 2 + }, })?; let program_signer = Pubkey::find_program_address(&[], program_id).0; let accounts = vec![ diff --git a/lang/src/idl.rs b/lang/src/idl.rs index 8bbd8931..2c19b4f6 100644 --- a/lang/src/idl.rs +++ b/lang/src/idl.rs @@ -45,6 +45,7 @@ pub enum IdlInstruction { SetBuffer, // Sets a new authority on the IdlAccount. SetAuthority { new_authority: Pubkey }, + Close, } // Accounts for the Create instruction. @@ -85,6 +86,18 @@ pub struct IdlSetBuffer<'info> { pub authority: Signer<'info>, } +// Accounts for closing the canonical Idl buffer. +#[derive(Accounts)] +pub struct IdlCloseAccount<'info> { + #[account(mut, has_one = authority, close = sol_destination)] + #[allow(deprecated)] + pub account: ProgramAccount<'info, IdlAccount>, + #[account(constraint = authority.key != &ERASED_AUTHORITY)] + pub authority: Signer<'info>, + #[account(mut)] + pub sol_destination: AccountInfo<'info>, +} + // The account holding a program's IDL. This is stored on chain so that clients // can fetch it and generate a client with nothing but a program's ID. // diff --git a/lang/syn/src/codegen/program/handlers.rs b/lang/syn/src/codegen/program/handlers.rs index f965bd3f..d6ba5b02 100644 --- a/lang/syn/src/codegen/program/handlers.rs +++ b/lang/syn/src/codegen/program/handlers.rs @@ -32,6 +32,14 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { __idl_create_account(program_id, &mut accounts, data_len)?; accounts.exit(program_id)?; }, + anchor_lang::idl::IdlInstruction::Close => { + let mut bumps = std::collections::BTreeMap::new(); + let mut reallocs = std::collections::BTreeSet::new(); + let mut accounts = + anchor_lang::idl::IdlCloseAccount::try_accounts(program_id, &mut accounts, &[], &mut bumps, &mut reallocs)?; + __idl_close_account(program_id, &mut accounts)?; + accounts.exit(program_id)?; + }, anchor_lang::idl::IdlInstruction::CreateBuffer => { let mut bumps = std::collections::BTreeMap::new(); let mut reallocs = std::collections::BTreeSet::new(); @@ -140,6 +148,17 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { Ok(()) } + #[inline(never)] + pub fn __idl_close_account( + program_id: &Pubkey, + accounts: &mut anchor_lang::idl::IdlCloseAccount, + ) -> anchor_lang::Result<()> { + #[cfg(not(feature = "no-log-ix-name"))] + anchor_lang::prelude::msg!("Instruction: IdlCloseAccount"); + + Ok(()) + } + #[inline(never)] pub fn __idl_create_buffer( program_id: &Pubkey, diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index f5345cfc..ad711090 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -264,7 +264,9 @@ fn parse_ty(f: &syn::Field) -> ParseResult { "UncheckedAccount" => Ty::UncheckedAccount, "Loader" => Ty::Loader(parse_program_account_zero_copy(&path)?), "AccountLoader" => Ty::AccountLoader(parse_program_account_loader(&path)?), - "AccountLoaderDynamic" => Ty::AccountLoaderDynamic(parse_program_mango_account_loader(&path)?), + "AccountLoaderDynamic" => { + Ty::AccountLoaderDynamic(parse_program_mango_account_loader(&path)?) + } "Account" => Ty::Account(parse_account_ty(&path)?), "Program" => Ty::Program(parse_program_ty(&path)?), "Signer" => Ty::Signer,