1use hpsvm::{HPSVM, types::FailedTransactionMetadata};
4use solana_address::Address;
5use solana_instruction::error::InstructionError;
6use solana_keypair::Keypair;
7use solana_loader_v3_interface::{
8 instruction as bpf_loader_upgradeable, state::UpgradeableLoaderState,
9};
10use solana_signer::Signer;
11use solana_transaction::Transaction;
12use solana_transaction_error::TransactionError;
13
14const CHUNK_SIZE: usize = 512;
15
16fn loader_instruction_error(source: InstructionError) -> FailedTransactionMetadata {
17 FailedTransactionMetadata {
18 err: TransactionError::InstructionError(0, source),
19 meta: Default::default(),
20 }
21}
22
23pub fn set_upgrade_authority(
25 svm: &mut HPSVM,
26 from_keypair: &Keypair,
27 program_address: &Address,
28 current_authority_keypair: &Keypair,
29 new_authority_address: Option<&Address>,
30) -> Result<(), FailedTransactionMetadata> {
31 let mut signers: Vec<&dyn Signer> = vec![from_keypair];
32 if from_keypair.pubkey() != current_authority_keypair.pubkey() {
33 signers.push(current_authority_keypair);
34 }
35
36 let tx = Transaction::new_signed_with_payer(
37 &[bpf_loader_upgradeable::set_upgrade_authority(
38 program_address,
39 ¤t_authority_keypair.pubkey(),
40 new_authority_address,
41 )],
42 Some(&from_keypair.pubkey()),
43 &signers,
44 svm.latest_blockhash(),
45 );
46
47 svm.send_transaction(tx)?;
48
49 Ok(())
50}
51
52fn load_upgradeable_buffer(
53 svm: &mut HPSVM,
54 payer_kp: &Keypair,
55 program_bytes: &[u8],
56) -> Result<Address, FailedTransactionMetadata> {
57 let payer_pk = payer_kp.pubkey();
58 let buffer_kp = Keypair::new();
59 let buffer_pk = buffer_kp.pubkey();
60 let buffer_len = UpgradeableLoaderState::size_of_buffer(program_bytes.len());
62 let lamports = svm.minimum_balance_for_rent_exemption(buffer_len);
63
64 let create_buffer_ixs = bpf_loader_upgradeable::create_buffer(
65 &payer_pk,
66 &buffer_pk,
67 &payer_pk,
68 lamports,
69 program_bytes.len(),
70 )
71 .map_err(loader_instruction_error)?;
72 let tx = Transaction::new_signed_with_payer(
73 &create_buffer_ixs,
74 Some(&payer_pk),
75 &[payer_kp, &buffer_kp],
76 svm.latest_blockhash(),
77 );
78
79 svm.send_transaction(tx)?;
80
81 let chunk_size = CHUNK_SIZE;
82 let mut offset = 0;
83 for chunk in program_bytes.chunks(chunk_size) {
84 let tx = Transaction::new_signed_with_payer(
85 &[bpf_loader_upgradeable::write(&buffer_pk, &payer_pk, offset, chunk.to_vec())],
86 Some(&payer_pk),
87 &[payer_kp],
88 svm.latest_blockhash(),
89 );
90
91 svm.send_transaction(tx)?;
92 offset += chunk_size as u32;
93 }
94
95 Ok(buffer_pk)
96}
97
98pub fn deploy_upgradeable_program(
100 svm: &mut HPSVM,
101 payer_kp: &Keypair,
102 program_kp: &Keypair,
103 program_bytes: &[u8],
104) -> Result<(), FailedTransactionMetadata> {
105 let program_pk = program_kp.pubkey();
106 let payer_pk = payer_kp.pubkey();
107 let buffer_pk = load_upgradeable_buffer(svm, payer_kp, program_bytes)?;
108
109 let lamports = svm.minimum_balance_for_rent_exemption(program_bytes.len());
110 let deploy_ixs = bpf_loader_upgradeable::deploy_with_max_program_len(
111 &payer_pk,
112 &program_pk,
113 &buffer_pk,
114 &payer_pk,
115 lamports,
116 program_bytes.len() * 2,
117 )
118 .map_err(loader_instruction_error)?;
119 let tx = Transaction::new_signed_with_payer(
120 &deploy_ixs,
121 Some(&payer_pk),
122 &[&payer_kp, &program_kp],
123 svm.latest_blockhash(),
124 );
125
126 svm.send_transaction(tx)?;
127
128 Ok(())
129}