gmsol/
alt.rs

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
17/// Address Lookup Table operations.
18pub trait AddressLookupTableOps<C> {
19    /// Fetch address lookup table with the given config.
20    fn alt_with_config(
21        &self,
22        address: &Pubkey,
23        config: RpcAccountInfoConfig,
24    ) -> impl Future<Output = crate::Result<WithSlot<Option<AddressLookupTableAccount>>>>;
25
26    /// Fetch address lookup table.
27    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    /// Create a [`TransactionBuilder`] to create address lookup table.
46    fn create_alt(&self) -> impl Future<Output = crate::Result<(TransactionBuilder<C>, Pubkey)>>;
47
48    /// Create a [`BundleBuilder`] to extend the given address lookup table with new addresses.
49    fn extend_alt(
50        &self,
51        alt: &Pubkey,
52        new_addresses: Vec<Pubkey>,
53        chunk_size: Option<usize>,
54    ) -> crate::Result<BundleBuilder<C>>;
55    /// Create a [`TransactionBuilder`] to deactivate the given address lookup table
56    fn deactivate_alt(&self, alt: &Pubkey) -> TransactionBuilder<C>;
57
58    /// Create a [`TransactionBuilder`] to close the given address lookup table
59    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}