diff --git a/Cargo.lock b/Cargo.lock index 0e87ebb773..5a2b68855a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12558,6 +12558,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "solana-client", + "solana-loader-v3-interface", "solana-program", "solana-sdk", "solana-transaction-status", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 4e9e672f69..31e51dd641 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -35,6 +35,7 @@ solana-client = { workspace = true } solana-transaction-status = { workspace = true } light-batched-merkle-tree = { workspace = true } light-registry = { workspace = true } +solana-loader-v3-interface = { workspace = true, features = ["bincode"] } light-compressible = { workspace = true } anchor-lang = { workspace = true } light-token = { workspace = true } diff --git a/xtask/src/close_buffer.rs b/xtask/src/close_buffer.rs new file mode 100644 index 0000000000..12405a7527 --- /dev/null +++ b/xtask/src/close_buffer.rs @@ -0,0 +1,50 @@ +use std::str::FromStr; + +use clap::Parser; +use solana_loader_v3_interface::instruction as bpf_loader_instruction; +use solana_sdk::{bs58, message::Message, pubkey::Pubkey}; + +#[derive(Debug, Parser)] +pub struct Options { + /// The buffer account pubkey to close + #[clap(long)] + buffer: String, + /// The multisig authority pubkey + #[clap(long, default_value = "7PeqkcCXeqgsp5Mi15gjJh8qvSLk7n3dgNuyfPhJJgqY")] + authority: String, + /// The recipient pubkey for reclaimed lamports + #[clap(long)] + recipient: String, +} + +/// Creates a serialized BPF Loader Close instruction for use with Squads TX builder. +/// +/// Steps: +/// 1. Build the close instruction for the BPF Upgradeable Loader +/// 2. Serialize the message to bs58 +/// 3. Print bs58 for use in Squads +pub fn close_buffer(options: Options) -> anyhow::Result<()> { + let buffer = Pubkey::from_str(&options.buffer) + .map_err(|e| anyhow::anyhow!("Invalid buffer pubkey: {e}"))?; + let authority = Pubkey::from_str(&options.authority) + .map_err(|e| anyhow::anyhow!("Invalid authority pubkey: {e}"))?; + let recipient = Pubkey::from_str(&options.recipient) + .map_err(|e| anyhow::anyhow!("Invalid recipient pubkey: {e}"))?; + + let instruction = + bpf_loader_instruction::close_any(&buffer, &recipient, Some(&authority), None); + + println!("instruction: {:?}", instruction); + println!( + "Serialized instruction data: {}", + bs58::encode(instruction.data.clone()).into_string() + ); + let message = Message::new(&[instruction], Some(&authority)); + let serialized_message = bs58::encode(message.serialize()).into_string(); + println!( + "\n ----------- Use the serialized message in the squads tx builder. --------------- \n" + ); + println!("serialized message: {}", serialized_message); + + Ok(()) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 49f2290dd4..5c30f4045c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,6 +1,7 @@ use clap::{Parser, ValueEnum}; mod bench; +mod close_buffer; mod create_batch_address_tree; mod create_batch_state_tree; mod create_compressible_config; @@ -89,6 +90,10 @@ enum Command { /// Example: cargo xtask create-ctoken-account --network devnet /// Example with existing mint: cargo xtask create-ctoken-account --mint --network devnet CreateCtokenAccount(create_ctoken_account::Options), + /// Close a BPF Upgradeable Loader buffer account via Squads multisig. + /// Serializes the Close instruction as a bs58 message for the Squads TX builder. + /// Example: cargo xtask close-buffer --buffer FMkzXMexKDUKGxAm7oGsjs4LGEMhzk9C6uuYJBwJbjiN + CloseBuffer(close_buffer::Options), } #[tokio::main] @@ -131,5 +136,6 @@ async fn main() -> Result<(), anyhow::Error> { Command::CreateCtokenAccount(opts) => { create_ctoken_account::create_ctoken_account(opts).await } + Command::CloseBuffer(opts) => close_buffer::close_buffer(opts), } }