1use std::str::FromStr;
2
3use base64::{engine::general_purpose, Engine as _};
4#[allow(unused_imports)]
5pub use borsh::{BorshDeserialize, BorshSerialize};
6pub use solana_address_lookup_table_program::state::AddressLookupTable;
7pub use solana_client::{
8 rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig, rpc_request::RpcRequest,
9};
10pub use solana_sdk::instruction::AccountMeta;
11#[allow(unused_imports)]
12pub use solana_sdk::{
13 address_lookup_table::AddressLookupTableAccount,
14 commitment_config::{CommitmentConfig, CommitmentLevel},
15 instruction::Instruction,
16 message::VersionedMessage,
17 pubkey::Pubkey,
18 signature::Signature,
19 signature::{Keypair, Signer},
20 signer::EncodableKey,
21 transaction::{Transaction, VersionedTransaction},
22};
23pub use solana_transaction_status::UiTransactionEncoding;
24
25use crate::legacy_tx::get_discriminant;
26
27pub fn call_with_lookup_table<T>(
98 connection: &RpcClient,
99 program_id: &Pubkey,
100 instruction_name: &str,
101 instruction_data: T,
102 lookup_table_key: &Pubkey,
103 payer: &Keypair,
104 signers: &[&Keypair],
105 accounts: Vec<AccountMeta>,
106) -> Result<Signature, Box<dyn std::error::Error>>
107where
108 T: BorshSerialize,
109{
110 let lookup_table_account = connection.get_account(lookup_table_key)?;
112 let address_lookup_table = AddressLookupTable::deserialize(&lookup_table_account.data)?;
113 let address_lookup_table_account = AddressLookupTableAccount {
114 key: lookup_table_key.clone(),
115 addresses: address_lookup_table.addresses.to_vec(),
116 };
117
118 let instruction_discriminant = get_discriminant("global", instruction_name);
120 let ix = Instruction::new_with_borsh(
121 program_id.clone(),
122 &(instruction_discriminant, instruction_data),
123 accounts,
124 );
125
126 let blockhash = connection.get_latest_blockhash()?;
128 let tx = VersionedTransaction::try_new(
129 VersionedMessage::V0(solana_sdk::message::v0::Message::try_compile(
130 &payer.pubkey(),
131 &[ix],
132 &[address_lookup_table_account],
133 blockhash,
134 )?),
135 signers,
136 )?;
137
138 let serialized_tx = bincode::serialize(&tx)?;
140 let serialized_encoded_tx = general_purpose::STANDARD.encode(serialized_tx);
141
142 let config = RpcSendTransactionConfig {
144 skip_preflight: false,
145 preflight_commitment: Some(CommitmentLevel::Confirmed),
146 encoding: Some(UiTransactionEncoding::Base64),
147 ..RpcSendTransactionConfig::default()
148 };
149
150 let signature = connection.send::<String>(
152 RpcRequest::SendTransaction,
153 serde_json::json!([serialized_encoded_tx, config]),
154 )?;
155
156 connection.confirm_transaction_with_commitment(
158 &Signature::from_str(signature.as_str())?,
159 CommitmentConfig::finalized(),
160 )?;
161
162 Ok(Signature::from_str(&signature)?)
163}
164
165pub fn create_lookup_table(
194 connection: &RpcClient,
195 payer: &Keypair,
196 latest_blockhash: solana_sdk::hash::Hash,
197) -> Result<Pubkey, Box<dyn std::error::Error>> {
198 let recent_slot = connection.get_slot()?;
199 let (create_ix, table_pk) =
200 solana_address_lookup_table_program::instruction::create_lookup_table(
201 payer.pubkey(),
202 payer.pubkey(),
203 recent_slot,
204 );
205
206 connection.send_and_confirm_transaction(&Transaction::new_signed_with_payer(
207 &[create_ix],
208 Some(&payer.pubkey()),
209 &[&payer],
210 latest_blockhash,
211 ))?;
212
213 Ok(table_pk)
214}
215
216pub fn extend_lookup_table(
254 connection: &RpcClient,
255 payer: &Keypair,
256 latest_blockhash: solana_sdk::hash::Hash,
257 table_pk: Pubkey,
258 new_accounts: Vec<Pubkey>,
259) -> Result<bool, Box<dyn std::error::Error>> {
260 let extend_ix = solana_address_lookup_table_program::instruction::extend_lookup_table(
262 table_pk,
263 payer.pubkey(),
264 Some(payer.pubkey()),
265 new_accounts,
266 );
267
268 let signature =
269 connection.send_and_confirm_transaction(&Transaction::new_signed_with_payer(
270 &[extend_ix],
271 Some(&payer.pubkey()),
272 &[&payer],
273 latest_blockhash,
274 ))?;
275
276 Ok(connection
277 .confirm_transaction_with_spinner(
278 &signature,
279 &latest_blockhash,
280 CommitmentConfig::confirmed(),
281 )
282 .is_ok())
283}
284
285#[cfg(test)]
286mod test {
287 use super::*;
288
289 #[test]
290 fn test_create_lookup_table() {
291 let connection = RpcClient::new("https://api.devnet.solana.com");
292 let payer: Keypair =
293 Keypair::read_from_file("/Users/cenwadike/.config/solana/solfate-dev.json").unwrap();
294
295 let latest_blockhash = connection
296 .get_latest_blockhash()
297 .expect("latest block hash");
298
299 let lookup_table_pk = create_lookup_table(&connection, &payer, latest_blockhash);
300 assert!(lookup_table_pk.is_ok())
301 }
302
303 #[test]
304 fn test_update_lookup_table() {
305 let connection = RpcClient::new("https://api.devnet.solana.com");
306 let payer: Keypair =
307 Keypair::read_from_file("/Users/cenwadike/.config/solana/solfate-dev.json").unwrap();
308
309 let latest_blockhash = connection
310 .get_latest_blockhash()
311 .expect("latest block hash");
312
313 let table_pk = create_lookup_table(&connection, &payer, latest_blockhash).unwrap();
314 let new_accounts = vec![Pubkey::new_unique()];
315 let res = extend_lookup_table(
316 &connection,
317 &payer,
318 latest_blockhash,
319 table_pk,
320 new_accounts,
321 )
322 .unwrap();
323 assert!(res);
324 }
325
326 #[derive(BorshSerialize, BorshDeserialize)]
327 pub struct UpdateBlob {
328 pub data: Vec<u8>,
329 }
330
331 #[test]
332 fn test_call_with_lookup_table() {
333 let connection = RpcClient::new("https://api.devnet.solana.com");
334 let program_id = blob::ID;
335 let instruction_name = "update_blob";
336 let instruction_data = UpdateBlob {
337 data: "another data".as_bytes().to_vec(),
338 };
339 let payer: Keypair =
340 Keypair::read_from_file("/Users/cenwadike/.config/solana/solfate-dev.json").unwrap();
341
342 let signers = &[&payer];
343 let latest_blockhash = connection
345 .get_latest_blockhash()
346 .expect("latest block hash");
347 let table_pk = create_lookup_table(&connection, &payer, latest_blockhash).unwrap();
348
349 let new_accounts = vec![program_id, payer.pubkey()];
351 extend_lookup_table(
352 &connection,
353 &payer,
354 latest_blockhash,
355 table_pk,
356 new_accounts,
357 )
358 .unwrap();
359
360 let (blob_account, _) = Pubkey::find_program_address(&[&b"blob"[..]], &program_id);
362 let accounts = vec![
363 AccountMeta::new(blob_account, false),
364 AccountMeta::new(payer.pubkey(), true),
365 ];
366
367 let res = call_with_lookup_table(
368 &connection,
369 &program_id,
370 instruction_name,
371 instruction_data,
372 &table_pk,
373 &payer,
374 signers,
375 accounts,
376 );
377
378 assert!(res.is_ok());
379 }
380}