|
@@ -336,6 +336,10 @@ pub enum IdlCommand {
|
|
},
|
|
},
|
|
Close {
|
|
Close {
|
|
program_id: Pubkey,
|
|
program_id: Pubkey,
|
|
|
|
+ /// When used, the content of the instruction will only be printed in base64 form and not executed.
|
|
|
|
+ /// Useful for multisig execution when the local wallet keypair is not available.
|
|
|
|
+ #[clap(long)]
|
|
|
|
+ print_only: bool,
|
|
},
|
|
},
|
|
/// Writes an IDL into a buffer account. This can be used with SetBuffer
|
|
/// Writes an IDL into a buffer account. This can be used with SetBuffer
|
|
/// to perform an upgrade.
|
|
/// to perform an upgrade.
|
|
@@ -350,6 +354,10 @@ pub enum IdlCommand {
|
|
/// Address of the buffer account to set as the idl on the program.
|
|
/// Address of the buffer account to set as the idl on the program.
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
buffer: Pubkey,
|
|
buffer: Pubkey,
|
|
|
|
+ /// When used, the content of the instruction will only be printed in base64 form and not executed.
|
|
|
|
+ /// Useful for multisig execution when the local wallet keypair is not available.
|
|
|
|
+ #[clap(long)]
|
|
|
|
+ print_only: bool,
|
|
},
|
|
},
|
|
/// Upgrades the IDL to the new file. An alias for first writing and then
|
|
/// Upgrades the IDL to the new file. An alias for first writing and then
|
|
/// then setting the idl buffer account.
|
|
/// then setting the idl buffer account.
|
|
@@ -369,6 +377,10 @@ pub enum IdlCommand {
|
|
/// New authority of the IDL account.
|
|
/// New authority of the IDL account.
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
new_authority: Pubkey,
|
|
new_authority: Pubkey,
|
|
|
|
+ /// When used, the content of the instruction will only be printed in base64 form and not executed.
|
|
|
|
+ /// Useful for multisig execution when the local wallet keypair is not available.
|
|
|
|
+ #[clap(long)]
|
|
|
|
+ print_only: bool,
|
|
},
|
|
},
|
|
/// Command to remove the ability to modify the IDL account. This should
|
|
/// Command to remove the ability to modify the IDL account. This should
|
|
/// likely be used in conjection with eliminating an "upgrade authority" on
|
|
/// likely be used in conjection with eliminating an "upgrade authority" on
|
|
@@ -1823,14 +1835,19 @@ fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> {
|
|
program_id,
|
|
program_id,
|
|
filepath,
|
|
filepath,
|
|
} => idl_init(cfg_override, program_id, filepath),
|
|
} => idl_init(cfg_override, program_id, filepath),
|
|
- IdlCommand::Close { program_id } => idl_close(cfg_override, program_id),
|
|
|
|
|
|
+ IdlCommand::Close {
|
|
|
|
+ program_id,
|
|
|
|
+ print_only,
|
|
|
|
+ } => idl_close(cfg_override, program_id, print_only),
|
|
IdlCommand::WriteBuffer {
|
|
IdlCommand::WriteBuffer {
|
|
program_id,
|
|
program_id,
|
|
filepath,
|
|
filepath,
|
|
} => idl_write_buffer(cfg_override, program_id, filepath).map(|_| ()),
|
|
} => idl_write_buffer(cfg_override, program_id, filepath).map(|_| ()),
|
|
- IdlCommand::SetBuffer { program_id, buffer } => {
|
|
|
|
- idl_set_buffer(cfg_override, program_id, buffer)
|
|
|
|
- }
|
|
|
|
|
|
+ IdlCommand::SetBuffer {
|
|
|
|
+ program_id,
|
|
|
|
+ buffer,
|
|
|
|
+ print_only,
|
|
|
|
+ } => idl_set_buffer(cfg_override, program_id, buffer, print_only),
|
|
IdlCommand::Upgrade {
|
|
IdlCommand::Upgrade {
|
|
program_id,
|
|
program_id,
|
|
filepath,
|
|
filepath,
|
|
@@ -1839,7 +1856,8 @@ fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> {
|
|
program_id,
|
|
program_id,
|
|
address,
|
|
address,
|
|
new_authority,
|
|
new_authority,
|
|
- } => idl_set_authority(cfg_override, program_id, address, new_authority),
|
|
|
|
|
|
+ print_only,
|
|
|
|
+ } => idl_set_authority(cfg_override, program_id, address, new_authority, print_only),
|
|
IdlCommand::EraseAuthority { program_id } => idl_erase_authority(cfg_override, program_id),
|
|
IdlCommand::EraseAuthority { program_id } => idl_erase_authority(cfg_override, program_id),
|
|
IdlCommand::Authority { program_id } => idl_authority(cfg_override, program_id),
|
|
IdlCommand::Authority { program_id } => idl_authority(cfg_override, program_id),
|
|
IdlCommand::Parse {
|
|
IdlCommand::Parse {
|
|
@@ -1852,6 +1870,12 @@ fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+fn get_idl_account(client: &RpcClient, idl_address: &Pubkey) -> Result<IdlAccount> {
|
|
|
|
+ let account = client.get_account(idl_address)?;
|
|
|
|
+ let mut data: &[u8] = &account.data;
|
|
|
|
+ AccountDeserialize::try_deserialize(&mut data).map_err(|e| anyhow!("{:?}", e))
|
|
|
|
+}
|
|
|
|
+
|
|
fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: String) -> Result<()> {
|
|
fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: String) -> Result<()> {
|
|
with_workspace(cfg_override, |cfg| {
|
|
with_workspace(cfg_override, |cfg| {
|
|
let keypair = cfg.provider.wallet.to_string();
|
|
let keypair = cfg.provider.wallet.to_string();
|
|
@@ -1866,12 +1890,14 @@ fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: Str
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
-fn idl_close(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
|
|
|
|
|
|
+fn idl_close(cfg_override: &ConfigOverride, program_id: Pubkey, print_only: bool) -> Result<()> {
|
|
with_workspace(cfg_override, |cfg| {
|
|
with_workspace(cfg_override, |cfg| {
|
|
let idl_address = IdlAccount::address(&program_id);
|
|
let idl_address = IdlAccount::address(&program_id);
|
|
- idl_close_account(cfg, &program_id, idl_address)?;
|
|
|
|
|
|
+ idl_close_account(cfg, &program_id, idl_address, print_only)?;
|
|
|
|
|
|
- println!("Idl account closed: {idl_address:?}");
|
|
|
|
|
|
+ if !print_only {
|
|
|
|
+ println!("Idl account closed: {idl_address:?}");
|
|
|
|
+ }
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
})
|
|
})
|
|
@@ -1897,19 +1923,30 @@ fn idl_write_buffer(
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
-fn idl_set_buffer(cfg_override: &ConfigOverride, program_id: Pubkey, buffer: Pubkey) -> Result<()> {
|
|
|
|
|
|
+fn idl_set_buffer(
|
|
|
|
+ cfg_override: &ConfigOverride,
|
|
|
|
+ program_id: Pubkey,
|
|
|
|
+ buffer: Pubkey,
|
|
|
|
+ print_only: bool,
|
|
|
|
+) -> Result<()> {
|
|
with_workspace(cfg_override, |cfg| {
|
|
with_workspace(cfg_override, |cfg| {
|
|
let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
|
|
let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
|
|
.map_err(|_| anyhow!("Unable to read keypair file"))?;
|
|
.map_err(|_| anyhow!("Unable to read keypair file"))?;
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let client = RpcClient::new(url);
|
|
let client = RpcClient::new(url);
|
|
|
|
|
|
|
|
+ let idl_address = IdlAccount::address(&program_id);
|
|
|
|
+ let idl_authority = if print_only {
|
|
|
|
+ get_idl_account(&client, &idl_address)?.authority
|
|
|
|
+ } else {
|
|
|
|
+ keypair.pubkey()
|
|
|
|
+ };
|
|
// Instruction to set the buffer onto the IdlAccount.
|
|
// Instruction to set the buffer onto the IdlAccount.
|
|
- let set_buffer_ix = {
|
|
|
|
|
|
+ let ix = {
|
|
let accounts = vec![
|
|
let accounts = vec![
|
|
AccountMeta::new(buffer, false),
|
|
AccountMeta::new(buffer, false),
|
|
- AccountMeta::new(IdlAccount::address(&program_id), false),
|
|
|
|
- AccountMeta::new(keypair.pubkey(), true),
|
|
|
|
|
|
+ AccountMeta::new(idl_address, false),
|
|
|
|
+ AccountMeta::new(idl_authority, true),
|
|
];
|
|
];
|
|
let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec();
|
|
let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec();
|
|
data.append(&mut IdlInstruction::SetBuffer.try_to_vec()?);
|
|
data.append(&mut IdlInstruction::SetBuffer.try_to_vec()?);
|
|
@@ -1920,20 +1957,24 @@ fn idl_set_buffer(cfg_override: &ConfigOverride, program_id: Pubkey, buffer: Pub
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- // Build the transaction.
|
|
|
|
- let latest_hash = client.get_latest_blockhash()?;
|
|
|
|
- let tx = Transaction::new_signed_with_payer(
|
|
|
|
- &[set_buffer_ix],
|
|
|
|
- Some(&keypair.pubkey()),
|
|
|
|
- &[&keypair],
|
|
|
|
- latest_hash,
|
|
|
|
- );
|
|
|
|
|
|
+ if print_only {
|
|
|
|
+ print_idl_instruction("SetBuffer", &ix, &idl_address)?;
|
|
|
|
+ } else {
|
|
|
|
+ // Build the transaction.
|
|
|
|
+ let latest_hash = client.get_latest_blockhash()?;
|
|
|
|
+ let tx = Transaction::new_signed_with_payer(
|
|
|
|
+ &[ix],
|
|
|
|
+ Some(&keypair.pubkey()),
|
|
|
|
+ &[&keypair],
|
|
|
|
+ latest_hash,
|
|
|
|
+ );
|
|
|
|
|
|
- // Send the transaction.
|
|
|
|
- client.send_and_confirm_transaction_with_spinner_and_commitment(
|
|
|
|
- &tx,
|
|
|
|
- CommitmentConfig::confirmed(),
|
|
|
|
- )?;
|
|
|
|
|
|
+ // Send the transaction.
|
|
|
|
+ client.send_and_confirm_transaction_with_spinner_and_commitment(
|
|
|
|
+ &tx,
|
|
|
|
+ CommitmentConfig::confirmed(),
|
|
|
|
+ )?;
|
|
|
|
+ }
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
})
|
|
})
|
|
@@ -1945,7 +1986,7 @@ fn idl_upgrade(
|
|
idl_filepath: String,
|
|
idl_filepath: String,
|
|
) -> Result<()> {
|
|
) -> Result<()> {
|
|
let buffer = idl_write_buffer(cfg_override, program_id, idl_filepath)?;
|
|
let buffer = idl_write_buffer(cfg_override, program_id, idl_filepath)?;
|
|
- idl_set_buffer(cfg_override, program_id, buffer)
|
|
|
|
|
|
+ idl_set_buffer(cfg_override, program_id, buffer, false)
|
|
}
|
|
}
|
|
|
|
|
|
fn idl_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
|
|
fn idl_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
|
|
@@ -1964,9 +2005,7 @@ fn idl_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- let account = client.get_account(&idl_address)?;
|
|
|
|
- let mut data: &[u8] = &account.data;
|
|
|
|
- let idl_account: IdlAccount = AccountDeserialize::try_deserialize(&mut data)?;
|
|
|
|
|
|
+ let idl_account = get_idl_account(&client, &idl_address)?;
|
|
|
|
|
|
println!("{:?}", idl_account.authority);
|
|
println!("{:?}", idl_account.authority);
|
|
|
|
|
|
@@ -1979,6 +2018,7 @@ fn idl_set_authority(
|
|
program_id: Pubkey,
|
|
program_id: Pubkey,
|
|
address: Option<Pubkey>,
|
|
address: Option<Pubkey>,
|
|
new_authority: Pubkey,
|
|
new_authority: Pubkey,
|
|
|
|
+ print_only: bool,
|
|
) -> Result<()> {
|
|
) -> Result<()> {
|
|
with_workspace(cfg_override, |cfg| {
|
|
with_workspace(cfg_override, |cfg| {
|
|
// Misc.
|
|
// Misc.
|
|
@@ -1991,6 +2031,12 @@ fn idl_set_authority(
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let client = RpcClient::new(url);
|
|
let client = RpcClient::new(url);
|
|
|
|
|
|
|
|
+ let idl_authority = if print_only {
|
|
|
|
+ get_idl_account(&client, &idl_address)?.authority
|
|
|
|
+ } else {
|
|
|
|
+ keypair.pubkey()
|
|
|
|
+ };
|
|
|
|
+
|
|
// Instruction data.
|
|
// Instruction data.
|
|
let data =
|
|
let data =
|
|
serialize_idl_ix(anchor_lang::idl::IdlInstruction::SetAuthority { new_authority })?;
|
|
serialize_idl_ix(anchor_lang::idl::IdlInstruction::SetAuthority { new_authority })?;
|
|
@@ -1998,7 +2044,7 @@ fn idl_set_authority(
|
|
// Instruction accounts.
|
|
// Instruction accounts.
|
|
let accounts = vec![
|
|
let accounts = vec![
|
|
AccountMeta::new(idl_address, false),
|
|
AccountMeta::new(idl_address, false),
|
|
- AccountMeta::new_readonly(keypair.pubkey(), true),
|
|
|
|
|
|
+ AccountMeta::new_readonly(idl_authority, true),
|
|
];
|
|
];
|
|
|
|
|
|
// Instruction.
|
|
// Instruction.
|
|
@@ -2007,20 +2053,25 @@ fn idl_set_authority(
|
|
accounts,
|
|
accounts,
|
|
data,
|
|
data,
|
|
};
|
|
};
|
|
- // 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_commitment(
|
|
|
|
- &tx,
|
|
|
|
- CommitmentConfig::confirmed(),
|
|
|
|
- )?;
|
|
|
|
|
|
|
|
- println!("Authority update complete.");
|
|
|
|
|
|
+ if print_only {
|
|
|
|
+ print_idl_instruction("SetAuthority", &ix, &idl_address)?;
|
|
|
|
+ } else {
|
|
|
|
+ // 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_commitment(
|
|
|
|
+ &tx,
|
|
|
|
+ CommitmentConfig::confirmed(),
|
|
|
|
+ )?;
|
|
|
|
+
|
|
|
|
+ println!("Authority update complete.");
|
|
|
|
+ }
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
})
|
|
})
|
|
@@ -2037,22 +2088,32 @@ fn idl_erase_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Res
|
|
return Ok(());
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
|
|
- idl_set_authority(cfg_override, program_id, None, ERASED_AUTHORITY)?;
|
|
|
|
|
|
+ idl_set_authority(cfg_override, program_id, None, ERASED_AUTHORITY, false)?;
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
-fn idl_close_account(cfg: &Config, program_id: &Pubkey, idl_address: Pubkey) -> Result<()> {
|
|
|
|
|
|
+fn idl_close_account(
|
|
|
|
+ cfg: &Config,
|
|
|
|
+ program_id: &Pubkey,
|
|
|
|
+ idl_address: Pubkey,
|
|
|
|
+ print_only: bool,
|
|
|
|
+) -> Result<()> {
|
|
let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
|
|
let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
|
|
.map_err(|_| anyhow!("Unable to read keypair file"))?;
|
|
.map_err(|_| anyhow!("Unable to read keypair file"))?;
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let url = cluster_url(cfg, &cfg.test_validator);
|
|
let client = RpcClient::new(url);
|
|
let client = RpcClient::new(url);
|
|
|
|
|
|
|
|
+ let idl_authority = if print_only {
|
|
|
|
+ get_idl_account(&client, &idl_address)?.authority
|
|
|
|
+ } else {
|
|
|
|
+ keypair.pubkey()
|
|
|
|
+ };
|
|
// Instruction accounts.
|
|
// Instruction accounts.
|
|
let accounts = vec![
|
|
let accounts = vec![
|
|
AccountMeta::new(idl_address, false),
|
|
AccountMeta::new(idl_address, false),
|
|
- AccountMeta::new_readonly(keypair.pubkey(), true),
|
|
|
|
- AccountMeta::new(keypair.pubkey(), true),
|
|
|
|
|
|
+ AccountMeta::new_readonly(idl_authority, true),
|
|
|
|
+ AccountMeta::new(keypair.pubkey(), false),
|
|
];
|
|
];
|
|
// Instruction.
|
|
// Instruction.
|
|
let ix = Instruction {
|
|
let ix = Instruction {
|
|
@@ -2060,18 +2121,23 @@ fn idl_close_account(cfg: &Config, program_id: &Pubkey, idl_address: Pubkey) ->
|
|
accounts,
|
|
accounts,
|
|
data: { serialize_idl_ix(anchor_lang::idl::IdlInstruction::Close {})? },
|
|
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_commitment(
|
|
|
|
- &tx,
|
|
|
|
- CommitmentConfig::confirmed(),
|
|
|
|
- )?;
|
|
|
|
|
|
+
|
|
|
|
+ if print_only {
|
|
|
|
+ print_idl_instruction("Close", &ix, &idl_address)?;
|
|
|
|
+ } else {
|
|
|
|
+ // 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_commitment(
|
|
|
|
+ &tx,
|
|
|
|
+ CommitmentConfig::confirmed(),
|
|
|
|
+ )?;
|
|
|
|
+ }
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
@@ -2179,6 +2245,35 @@ fn write_idl(idl: &Idl, out: OutFile) -> Result<()> {
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Print `base64+borsh` encoded IDL instruction.
|
|
|
|
+fn print_idl_instruction(ix_name: &str, ix: &Instruction, idl_address: &Pubkey) -> Result<()> {
|
|
|
|
+ println!("Print only mode. No execution!");
|
|
|
|
+ println!("Instruction: {ix_name}");
|
|
|
|
+ println!("IDL address: {idl_address}");
|
|
|
|
+ println!("Program: {}", ix.program_id);
|
|
|
|
+
|
|
|
|
+ // Serialize with `bincode` because `Instruction` does not implement `BorshSerialize`
|
|
|
|
+ let mut serialized_ix = bincode::serialize(ix)?;
|
|
|
|
+
|
|
|
|
+ // Remove extra bytes in order to make the serialized instruction `borsh` compatible
|
|
|
|
+ // `bincode` uses 8 bytes(LE) for length meanwhile `borsh` uses 4 bytes(LE)
|
|
|
|
+ let mut remove_extra_vec_bytes = |index: usize| {
|
|
|
|
+ serialized_ix.drain((index + 4)..(index + 8));
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let accounts_index = std::mem::size_of_val(&ix.program_id);
|
|
|
|
+ remove_extra_vec_bytes(accounts_index);
|
|
|
|
+ let data_index = accounts_index + 4 + std::mem::size_of_val(&*ix.accounts);
|
|
|
|
+ remove_extra_vec_bytes(data_index);
|
|
|
|
+
|
|
|
|
+ println!(
|
|
|
|
+ "Base64 encoded instruction: {}",
|
|
|
|
+ base64::encode(serialized_ix)
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
|
|
+}
|
|
|
|
+
|
|
fn account(
|
|
fn account(
|
|
cfg_override: &ConfigOverride,
|
|
cfg_override: &ConfigOverride,
|
|
account_type: String,
|
|
account_type: String,
|