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: u64 = 0;
83 for chunk in program_bytes.chunks(chunk_size) {
84 let offset_u32: u32 =
85 offset.try_into().map_err(|_| loader_instruction_error(InstructionError::Custom(0)))?;
86 let tx = Transaction::new_signed_with_payer(
87 &[bpf_loader_upgradeable::write(&buffer_pk, &payer_pk, offset_u32, chunk.to_vec())],
88 Some(&payer_pk),
89 &[payer_kp],
90 svm.latest_blockhash(),
91 );
92
93 svm.send_transaction(tx)?;
94 offset += chunk_size as u64;
95 }
96
97 Ok(buffer_pk)
98}
99
100pub fn deploy_upgradeable_program(
102 svm: &mut HPSVM,
103 payer_kp: &Keypair,
104 program_kp: &Keypair,
105 program_bytes: &[u8],
106) -> Result<(), FailedTransactionMetadata> {
107 let program_pk = program_kp.pubkey();
108 let payer_pk = payer_kp.pubkey();
109 let buffer_pk = load_upgradeable_buffer(svm, payer_kp, program_bytes)?;
110
111 let lamports = svm.minimum_balance_for_rent_exemption(program_bytes.len());
112 let deploy_ixs = bpf_loader_upgradeable::deploy_with_max_program_len(
113 &payer_pk,
114 &program_pk,
115 &buffer_pk,
116 &payer_pk,
117 lamports,
118 program_bytes.len() * 2,
119 )
120 .map_err(loader_instruction_error)?;
121 let tx = Transaction::new_signed_with_payer(
122 &deploy_ixs,
123 Some(&payer_pk),
124 &[&payer_kp, &program_kp],
125 svm.latest_blockhash(),
126 );
127
128 svm.send_transaction(tx)?;
129
130 Ok(())
131}