pub fn build_program_injection(
programdata_address: Address,
elf: &[u8],
deploy_slot: u64,
upgrade_authority: Option<Address>,
lamports: u64,
) -> BTreeMap<Address, AccountData>Expand description
Build a BPF Loader Upgradeable ProgramData account modification from raw ELF bytes.
Returns a map of {programdata_address: AccountData} ready to pass to
Continue::builder().modify_accounts(...).
The ELF is wrapped in the standard ProgramData header format:
[0..4] variant = 3 (u32 LE, UpgradeableLoaderState::ProgramData)
[4..12] deploy_slot (u64 LE)
[12] upgrade_authority discriminant (0 = None, 1 = Some)
[13..45] upgrade_authority pubkey bytes (32 bytes, present only when Some)
[45..] ELF bytecodedeploy_slot should be set to start_slot.saturating_sub(1) so the program
appears deployed before the first executed slot. Deploying at start_slot
itself triggers the SVM’s same-slot restriction, marking the program unloaded.
lamports must be at least rent-exempt for the resulting account size.
With upgrade_authority = None the data length is 13 + elf.len();
with Some(authority) it is 45 + elf.len().
Fetch the exact minimum with
rpc.get_minimum_balance_for_rent_exemption(data_len).await?.
§Example
use simulator_client::build_program_injection;
use solana_address::Address;
let program_id: Address = "YourProgramId...".parse().unwrap();
// Compute the programdata PDA using solana_loader_v3_interface::get_program_data_address,
// then convert to Address.
let programdata_addr: Address = "ProgramDataAddr...".parse().unwrap();
let elf = std::fs::read("my_program.so").unwrap();
let deploy_slot = 399_834_991; // start_slot - 1
let mods = build_program_injection(programdata_addr, &elf, deploy_slot, None, 10_000_000_000);
// Pass mods to Continue::builder().modify_accounts(mods).build()