litesvm_loader/
lib.rs

1use {
2    litesvm::{types::FailedTransactionMetadata, LiteSVM},
3    solana_keypair::Keypair,
4    solana_loader_v3_interface::{
5        instruction as bpf_loader_upgradeable, state::UpgradeableLoaderState,
6    },
7    solana_pubkey::Pubkey,
8    solana_signer::Signer,
9    solana_transaction::Transaction,
10};
11
12const CHUNK_SIZE: usize = 512;
13
14pub fn set_upgrade_authority(
15    svm: &mut LiteSVM,
16    from_keypair: &Keypair,
17    program_pubkey: &Pubkey,
18    current_authority_keypair: &Keypair,
19    new_authority_pubkey: Option<&Pubkey>,
20) -> Result<(), FailedTransactionMetadata> {
21    let tx = Transaction::new_signed_with_payer(
22        &[bpf_loader_upgradeable::set_upgrade_authority(
23            program_pubkey,
24            &current_authority_keypair.pubkey(),
25            new_authority_pubkey,
26        )],
27        Some(&from_keypair.pubkey()),
28        &[&from_keypair],
29        svm.latest_blockhash(),
30    );
31
32    svm.send_transaction(tx)?;
33
34    Ok(())
35}
36
37fn load_upgradeable_buffer(
38    svm: &mut LiteSVM,
39    payer_kp: &Keypair,
40    program_bytes: &[u8],
41) -> Result<Pubkey, FailedTransactionMetadata> {
42    let payer_pk = payer_kp.pubkey();
43    let buffer_kp = Keypair::new();
44    let buffer_pk = buffer_kp.pubkey();
45    // loader
46    let buffer_len = UpgradeableLoaderState::size_of_buffer(program_bytes.len());
47    let lamports = svm.minimum_balance_for_rent_exemption(buffer_len);
48
49    let tx = Transaction::new_signed_with_payer(
50        &bpf_loader_upgradeable::create_buffer(
51            &payer_pk,
52            &buffer_pk,
53            &payer_pk,
54            lamports,
55            program_bytes.len(),
56        )
57        .unwrap(),
58        Some(&payer_pk),
59        &[payer_kp, &buffer_kp],
60        svm.latest_blockhash(),
61    );
62
63    svm.send_transaction(tx)?;
64
65    let chunk_size = CHUNK_SIZE;
66    let mut offset = 0;
67    for chunk in program_bytes.chunks(chunk_size) {
68        let tx = Transaction::new_signed_with_payer(
69            &[bpf_loader_upgradeable::write(
70                &buffer_pk,
71                &payer_pk,
72                offset,
73                chunk.to_vec(),
74            )],
75            Some(&payer_pk),
76            &[payer_kp],
77            svm.latest_blockhash(),
78        );
79
80        svm.send_transaction(tx)?;
81        offset += chunk_size as u32;
82    }
83
84    Ok(buffer_pk)
85}
86
87pub fn deploy_upgradeable_program(
88    svm: &mut LiteSVM,
89    payer_kp: &Keypair,
90    program_kp: &Keypair,
91    program_bytes: &[u8],
92) -> Result<(), FailedTransactionMetadata> {
93    let program_pk = program_kp.pubkey();
94    let payer_pk = payer_kp.pubkey();
95    let buffer_pk = load_upgradeable_buffer(svm, payer_kp, program_bytes)?;
96
97    let lamports = svm.minimum_balance_for_rent_exemption(program_bytes.len());
98    #[allow(deprecated)]
99    let tx = Transaction::new_signed_with_payer(
100        &bpf_loader_upgradeable::deploy_with_max_program_len(
101            &payer_pk,
102            &program_pk,
103            &buffer_pk,
104            &payer_pk,
105            lamports,
106            program_bytes.len() * 2,
107        )
108        .unwrap(),
109        Some(&payer_pk),
110        &[&payer_kp, &program_kp],
111        svm.latest_blockhash(),
112    );
113
114    svm.send_transaction(tx)?;
115
116    Ok(())
117}