1use std::{future::Future, ops::Deref};
2
3use anchor_client::{
4 solana_client::rpc_config::RpcAccountInfoConfig,
5 solana_sdk::{
6 account::ReadableAccount,
7 address_lookup_table::{self, state::AddressLookupTable, AddressLookupTableAccount},
8 pubkey::Pubkey,
9 signer::Signer,
10 },
11};
12use gmsol_solana_utils::{bundle_builder::BundleBuilder, transaction_builder::TransactionBuilder};
13use solana_account_decoder::UiAccountEncoding;
14
15use crate::utils::{rpc::accounts::get_account_with_context, WithSlot};
16
17pub trait AddressLookupTableOps<C> {
19 fn alt_with_config(
21 &self,
22 address: &Pubkey,
23 config: RpcAccountInfoConfig,
24 ) -> impl Future<Output = crate::Result<WithSlot<Option<AddressLookupTableAccount>>>>;
25
26 fn alt(
28 &self,
29 address: &Pubkey,
30 ) -> impl Future<Output = crate::Result<Option<AddressLookupTableAccount>>> {
31 async {
32 Ok(self
33 .alt_with_config(
34 address,
35 RpcAccountInfoConfig {
36 encoding: Some(UiAccountEncoding::Base64),
37 ..Default::default()
38 },
39 )
40 .await?
41 .into_value())
42 }
43 }
44
45 fn create_alt(&self) -> impl Future<Output = crate::Result<(TransactionBuilder<C>, Pubkey)>>;
47
48 fn extend_alt(
50 &self,
51 alt: &Pubkey,
52 new_addresses: Vec<Pubkey>,
53 chunk_size: Option<usize>,
54 ) -> crate::Result<BundleBuilder<C>>;
55 fn deactivate_alt(&self, alt: &Pubkey) -> TransactionBuilder<C>;
57
58 fn close_alt(&self, alt: &Pubkey) -> TransactionBuilder<C>;
60}
61
62impl<C: Deref<Target = impl Signer> + Clone> AddressLookupTableOps<C> for crate::Client<C> {
63 async fn alt_with_config(
64 &self,
65 address: &Pubkey,
66 config: RpcAccountInfoConfig,
67 ) -> crate::Result<WithSlot<Option<AddressLookupTableAccount>>> {
68 let client = self.store_program().rpc();
69 let account: WithSlot<_> = get_account_with_context(&client, address, config)
70 .await?
71 .into();
72 account
73 .map(|a| {
74 a.map(|account| {
75 let table = AddressLookupTable::deserialize(account.data())
76 .map_err(crate::Error::invalid_argument)?;
77 Ok(AddressLookupTableAccount {
78 key: *address,
79 addresses: table.addresses.iter().copied().collect(),
80 })
81 })
82 .transpose()
83 })
84 .transpose()
85 }
86
87 async fn create_alt(&self) -> crate::Result<(TransactionBuilder<C>, Pubkey)> {
88 let slot = self.get_slot(None).await?;
89 let payer = self.payer();
90 let (ix, address) =
91 address_lookup_table::instruction::create_lookup_table(payer, payer, slot);
92 let rpc = self
93 .store_transaction()
94 .program(address_lookup_table::program::ID)
95 .pre_instruction(ix);
96
97 Ok((rpc, address))
98 }
99
100 fn extend_alt(
101 &self,
102 alt: &Pubkey,
103 new_addresses: Vec<Pubkey>,
104 chunk_size: Option<usize>,
105 ) -> crate::Result<BundleBuilder<C>> {
106 let mut tx = self.bundle();
107 let payer = self.payer();
108
109 let chunk_size = chunk_size.unwrap_or(10);
110 for new_addresses in new_addresses.chunks(chunk_size) {
111 let ix = address_lookup_table::instruction::extend_lookup_table(
112 *alt,
113 payer,
114 Some(payer),
115 new_addresses.to_owned(),
116 );
117 let rpc = self
118 .store_transaction()
119 .program(address_lookup_table::program::ID)
120 .pre_instruction(ix);
121 tx.try_push(rpc)?;
122 }
123 Ok(tx)
124 }
125
126 fn deactivate_alt(&self, alt: &Pubkey) -> TransactionBuilder<C> {
127 let payer = self.payer();
128 let ix = address_lookup_table::instruction::deactivate_lookup_table(*alt, payer);
129 self.store_transaction()
130 .program(address_lookup_table::program::ID)
131 .pre_instruction(ix)
132 }
133
134 fn close_alt(&self, alt: &Pubkey) -> TransactionBuilder<C> {
135 let payer = self.payer();
136 let ix = address_lookup_table::instruction::close_lookup_table(*alt, payer, payer);
137 self.store_transaction()
138 .program(address_lookup_table::program::ID)
139 .pre_instruction(ix)
140 }
141}