Repository: sevenlabs-hq/carbon
Pull request #290: add ALT decoder
Author: login: nogo6260
Created: 2025-05-09T12:14:20Z | Merged: 2025-05-10T07:15:41Z



Diff:
diff --git a/Cargo.lock b/Cargo.lock
index ebe14b6e3..1e04f6d69 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -997,9 +997,9 @@ dependencies = [
 
 [[package]]
 name = "bytemuck"
-version = "1.22.0"
+version = "1.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
 dependencies = [
  "bytemuck_derive",
 ]
@@ -1037,6 +1037,21 @@ dependencies = [
  "thiserror 1.0.69",
 ]
 
+[[package]]
+name = "carbon-address-lookup-table-decoder"
+version = "0.8.0"
+dependencies = [
+ "bincode",
+ "bytemuck",
+ "carbon-core",
+ "carbon-macros",
+ "carbon-proc-macros",
+ "serde",
+ "solana-account",
+ "solana-instruction",
+ "solana-pubkey",
+]
+
 [[package]]
 name = "carbon-associated-token-account-decoder"
 version = "0.8.0"
diff --git a/Cargo.toml b/Cargo.toml
index 97e04ea80..aac8db2f3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,6 +32,7 @@ carbon-log-metrics = { path = "metrics/log-metrics", version = "0.8.0" }
 carbon-prometheus-metrics = { path = "metrics/prometheus-metrics", version = "0.8.0" }
 
 # decoders
+carbon-address-lookup-table-decoder = { path = "decoders/address-lookup-table-decoder", version = "0.8.0" }
 carbon-associated-token-account-decoder = { path = "decoders/associated-token-account-decoder", version = "0.8.0" }
 carbon-boop-decoder = { path = "decoders/boop-decoder", version = "0.8.0" }
 carbon-drift-v2-decoder = { path = "decoders/drift-v2-decoder", version = "0.8.0" }
@@ -137,12 +138,12 @@ serde-big-array = "0.5.1"
 serde_json = "1.0.138"
 sha2 = "0.10.8"
 sqlx = { version = "0.8.5", features = [
-    "macros",
-    "runtime-tokio-rustls",
-    "postgres",
-    "uuid",
-    "chrono",
-    "rust_decimal",
+  "macros",
+  "runtime-tokio-rustls",
+  "postgres",
+  "uuid",
+  "chrono",
+  "rust_decimal",
 ] }
 sqlx_migrator = { version = "0.17.0", features = ["postgres"] }
 syn = { version = "1.0", features = ["full"] }
diff --git a/README.md b/README.md
index 1aa4eea4e..4496fe41d 100644
--- a/README.md
+++ b/README.md
@@ -230,6 +230,7 @@ Decoders for most popular Solana programs are published and maintained:
 
 | Crate Name                                 | Description                               | Program ID                                   |
 | ------------------------------------------ | ----------------------------------------- | -------------------------------------------- |
+| `carbon-address-lookup-table-decoder`  | Address Lookup Table Decoder          | AddressLookupTab1e1111111111111111111111111 |
 | `carbon-associated-token-account-decoder`  | Associated Token Account Decoder          | ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL |
 | `carbon-boop-decoder`                      | Boop Decoder                              | boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4  |
 | `carbon-drift-v2-decoder`                  | Drift V2 Program Decoder                  | dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH  |
diff --git a/crates/core/src/account.rs b/crates/core/src/account.rs
index d55bfd12f..08d989b5a 100644
--- a/crates/core/src/account.rs
+++ b/crates/core/src/account.rs
@@ -165,7 +165,7 @@ pub struct AccountPipe<T: Send> {
 /// use carbon_core::account::AccountPipes;
 /// use std::sync::Arc;
 /// use async_trait::async_trait;
-/// 
+///
 /// #[async_trait]
 /// impl AccountPipes for MyAccountPipe {
 ///     async fn run(
diff --git a/crates/core/src/account_deletion.rs b/crates/core/src/account_deletion.rs
index 24663ea99..f41a922fa 100644
--- a/crates/core/src/account_deletion.rs
+++ b/crates/core/src/account_deletion.rs
@@ -42,7 +42,7 @@ use {
 /// use carbon_core::processor::Processor;
 /// use async_trait::async_trait;
 /// use std::sync::Arc;
-/// 
+///
 /// struct MyAccountDeletionProcessor;
 ///
 /// #[async_trait]
@@ -102,7 +102,7 @@ pub struct AccountDeletionPipe {
 /// use carbon_core::datasource::AccountDeletion;
 /// use carbon_core::account_deletion::AccountDeletionPipe;
 /// use async_trait::async_trait;
-/// 
+///
 /// struct MyAccountDeletionPipe;
 ///
 /// #[async_trait]
diff --git a/crates/core/src/datasource.rs b/crates/core/src/datasource.rs
index d3b731a61..57b7dc009 100644
--- a/crates/core/src/datasource.rs
+++ b/crates/core/src/datasource.rs
@@ -74,7 +74,7 @@ use {
 /// use carbon_core::datasource::Datasource;
 /// use tokio_util::sync::CancellationToken;
 /// use async_trait::async_trait;
-/// 
+///
 /// #[async_trait]
 /// impl Datasource for MyDatasource {
 ///     async fn consume(
@@ -122,7 +122,7 @@ pub enum Update {
     Account(AccountUpdate),
     Transaction(Box<TransactionUpdate>),
     AccountDeletion(AccountDeletion),
-    BlockDetails(BlockDetails)
+    BlockDetails(BlockDetails),
 }
 
 /// Enumerates the types of updates a datasource can provide.
@@ -158,7 +158,7 @@ pub struct AccountUpdate {
 
 /// Represents the details of a Solana block, including its slot, hashes, rewards, and timing information.
 ///
-/// The `BlockDetails` struct encapsulates the essential information for a block, 
+/// The `BlockDetails` struct encapsulates the essential information for a block,
 /// providing details about its slot, blockhashes, rewards, and other metadata.
 ///
 /// - `slot`: The slot number in which this block was recorded.
diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs
index d3ccd4f4a..da0791f53 100644
--- a/crates/core/src/error.rs
+++ b/crates/core/src/error.rs
@@ -60,7 +60,7 @@ pub enum Error {
 /// ```ignore
 /// use core::error::Error;
 /// use carbon_core::error::CarbonResult;
-/// 
+///
 /// fn example_function(success: bool) -> CarbonResult<()> {
 ///     if success {
 ///         Ok(())
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
index ae4f0d89b..9bce818cb 100644
--- a/crates/core/src/lib.rs
+++ b/crates/core/src/lib.rs
@@ -72,7 +72,7 @@
 //!
 //! ```ignore
 //! use std::sync::Arc;
-//! 
+//!
 //! carbon_core::pipeline::Pipeline::builder()
 //! .datasource(transaction_crawler)
 //! .metrics(Arc::new(LogMetrics::new()))
@@ -114,6 +114,7 @@
 
 pub mod account;
 pub mod account_deletion;
+mod block_details;
 pub mod collection;
 pub mod datasource;
 pub mod deserialize;
@@ -125,7 +126,6 @@ pub mod processor;
 pub mod schema;
 pub mod transaction;
 pub mod transformers;
-mod block_details;
 
 pub use borsh;
 #[cfg(feature = "macros")]
diff --git a/crates/core/src/processor.rs b/crates/core/src/processor.rs
index 66371a59c..81a451b45 100644
--- a/crates/core/src/processor.rs
+++ b/crates/core/src/processor.rs
@@ -84,7 +84,7 @@ use {
 /// use carbon_core::metrics::MetricsCollection;
 /// use carbon_core::processor::Processor;
 /// use std::sync::Arc;
-/// 
+///
 /// struct CustomProcessor;
 ///
 /// #[async_trait]
diff --git a/crates/macros/src/schemas.rs b/crates/macros/src/schemas.rs
index 6ec0c775b..802b3a745 100644
--- a/crates/macros/src/schemas.rs
+++ b/crates/macros/src/schemas.rs
@@ -100,7 +100,7 @@
 /// ```ignore
 /// use carbon_macros::schema;
 /// use carbon_macros::schema_inner;
-/// 
+///
 /// let transaction_schema = schema![
 ///     any
 ///     [
diff --git a/crates/macros/src/try_decode_ixs.rs b/crates/macros/src/try_decode_ixs.rs
index ba9e7d443..52fa7abee 100644
--- a/crates/macros/src/try_decode_ixs.rs
+++ b/crates/macros/src/try_decode_ixs.rs
@@ -37,7 +37,7 @@
 ///
 /// ```ignore
 ///use carbon_macros::try_decode_instructions;
-/// 
+///
 /// let instruction = Instruction { /* initialize with program_id, accounts, and data */ };
 ///
 /// let decoded = try_decode_instructions!(
diff --git a/crates/proc-macros/src/lib.rs b/crates/proc-macros/src/lib.rs
index 34efc3a92..cf44efa48 100644
--- a/crates/proc-macros/src/lib.rs
+++ b/crates/proc-macros/src/lib.rs
@@ -83,7 +83,7 @@ use {
 ///
 /// ```ignore
 /// use carbon_proc_macros::CarbonDeserialize;
-/// 
+///
 /// #[derive(CarbonDeserialize)]
 /// #[carbon(discriminator = "0x01")]
 /// struct Message {
@@ -176,7 +176,7 @@ pub fn carbon_deserialize_derive(input_token_stream: TokenStream) -> TokenStream
 ///
 /// ```ignore
 /// use carbon_proc_macros::CarbonDeserialize;
-/// 
+///
 /// #[derive(CarbonDeserialize)]
 /// #[carbon(discriminator = "0x1234")]
 /// struct MyStruct {
@@ -253,7 +253,7 @@ fn gen_borsh_deserialize(input: TokenStream) -> TokenStream2 {
 ///
 /// ```ignore
 /// use syn::Attribute;
-/// 
+///
 /// // Example attribute with a discriminator
 /// let attrs: Vec<Attribute> = vec![parse_quote!(#[carbon(discriminator = "0x1234")])];
 /// let discriminator = get_discriminator(&attrs);
@@ -344,7 +344,7 @@ fn get_discriminator(attrs: &[syn::Attribute]) -> Option<quote::__private::Token
 /// ```ignore
 /// use syn::Ident;
 /// use syn::parse_quote;
-/// 
+///
 /// let instructions_enum_name: Ident = parse_quote!(InstructionsEnum);
 /// let instruction_types_enum_name: Ident = parse_quote!(InstructionTypesEnum);
 /// let programs_enum_name: Ident = parse_quote!(ProgramsEnum);
@@ -739,7 +739,7 @@ pub fn instruction_decoder_collection(input: TokenStream) -> TokenStream {
 ///
 /// ```rust
 /// use carbon_proc_macros::InstructionType;
-/// 
+///
 /// #[derive(InstructionType)]
 /// enum Instructions {
 ///     NoData,
diff --git a/decoders/address_lookup_table_decoder/Cargo.toml b/decoders/address_lookup_table_decoder/Cargo.toml
new file mode 100644
index 000000000..33e4350b0
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "carbon-address-lookup-table-decoder"
+version = "0.8.0"
+description = "Address lookup table program decoder"
+edition = { workspace = true }
+license = { workspace = true }
+readme = "README.md"
+keywords = ["solana", "decoder", "address-lookup-table", "alt"]
+categories = ["encoding"]
+
+[lib]
+crate-type = ["rlib"]
+
+[dependencies]
+bincode = { workspace = true }
+bytemuck = "1.23.0"
+carbon-core = { workspace = true }
+carbon-macros = { workspace = true }
+carbon-proc-macros = { workspace = true }
+serde = { workspace = true }
+solana-account = { workspace = true }
+solana-instruction = { workspace = true, default-features = false }
+solana-pubkey = { workspace = true }
diff --git a/decoders/address_lookup_table_decoder/src/accounts/address_lookup_table.rs b/decoders/address_lookup_table_decoder/src/accounts/address_lookup_table.rs
new file mode 100644
index 000000000..c44a043f4
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/accounts/address_lookup_table.rs
@@ -0,0 +1,7 @@
+use super::super::types::*;
+
+#[derive(Debug)]
+pub struct AddressLookupTable {
+    pub meta: LookupTableMeta,
+    pub addresses: LookupTableAddresses,
+}
diff --git a/decoders/address_lookup_table_decoder/src/accounts/mod.rs b/decoders/address_lookup_table_decoder/src/accounts/mod.rs
new file mode 100644
index 000000000..0f7c6562e
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/accounts/mod.rs
@@ -0,0 +1,50 @@
+use {
+    super::AddressLookupTableDecoder,
+    crate::{
+        types::{LookupTableAddresses, ProgramState},
+        PROGRAM_ID,
+    },
+    address_lookup_table::AddressLookupTable,
+    carbon_core::account::AccountDecoder,
+    solana_pubkey::Pubkey,
+};
+pub mod address_lookup_table;
+
+#[derive(Debug)]
+pub enum AddressLookupTableAccount {
+    AddressLookupTable(address_lookup_table::AddressLookupTable),
+}
+
+impl AccountDecoder<'_> for AddressLookupTableDecoder {
+    type AccountType = AddressLookupTableAccount;
+
+    fn decode_account(
+        &self,
+        account: &solana_account::Account,
+    ) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
+        if !account.owner.eq(&PROGRAM_ID) {
+            return None;
+        }
+
+        let (meta, raw_data) = account.data.split_at(56);
+        let program_data: ProgramState = bincode::deserialize(meta).ok()?;
+        let ProgramState::LookupTable(meta) = program_data else {
+            return None;
+        };
+
+        let addresses: &[Pubkey] = bytemuck::try_cast_slice(raw_data).ok()?;
+
+        Some(carbon_core::account::DecodedAccount {
+            lamports: account.lamports,
+            data: AddressLookupTableAccount::AddressLookupTable(AddressLookupTable {
+                meta,
+                addresses: LookupTableAddresses {
+                    addresses: addresses.to_vec(),
+                },
+            }),
+            owner: account.owner,
+            executable: account.executable,
+            rent_epoch: account.rent_epoch,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/close_lookup_table.rs b/decoders/address_lookup_table_decoder/src/instructions/close_lookup_table.rs
new file mode 100644
index 000000000..c3f3d56fa
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/close_lookup_table.rs
@@ -0,0 +1,32 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+#[carbon(discriminator = "0x04000000")]
+pub struct CloseLookupTable {}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+pub struct CloseLookupTableInstructionAccounts {
+    pub look_up_table: solana_pubkey::Pubkey,
+    pub authority: solana_pubkey::Pubkey,
+    pub lamports_recipient: solana_pubkey::Pubkey,
+}
+
+impl carbon_core::deserialize::ArrangeAccounts for CloseLookupTable {
+    type ArrangedAccounts = CloseLookupTableInstructionAccounts;
+
+    fn arrange_accounts(
+        accounts: &[solana_instruction::AccountMeta],
+    ) -> Option<Self::ArrangedAccounts> {
+        let [look_up_table, authority, lamports_recipient, _remaining @ ..] = accounts else {
+            return None;
+        };
+
+        Some(CloseLookupTableInstructionAccounts {
+            look_up_table: look_up_table.pubkey,
+            authority: authority.pubkey,
+            lamports_recipient: lamports_recipient.pubkey,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/create_lookup_table.rs b/decoders/address_lookup_table_decoder/src/instructions/create_lookup_table.rs
new file mode 100644
index 000000000..80b251d16
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/create_lookup_table.rs
@@ -0,0 +1,37 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+#[carbon(discriminator = "0x00000000")]
+pub struct CreateLookupTable {
+    pub recent_slot: u64,
+    pub bump_seed: u8,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+pub struct CreateLookupTableInstructionAccounts {
+    pub look_up_table: solana_pubkey::Pubkey,
+    pub authority: solana_pubkey::Pubkey,
+    pub funder: solana_pubkey::Pubkey,
+    pub system_program: solana_pubkey::Pubkey,
+}
+
+impl carbon_core::deserialize::ArrangeAccounts for CreateLookupTable {
+    type ArrangedAccounts = CreateLookupTableInstructionAccounts;
+
+    fn arrange_accounts(
+        accounts: &[solana_instruction::AccountMeta],
+    ) -> Option<Self::ArrangedAccounts> {
+        let [look_up_table, authority, funder, system_program, _remaining @ ..] = accounts else {
+            return None;
+        };
+
+        Some(CreateLookupTableInstructionAccounts {
+            look_up_table: look_up_table.pubkey,
+            authority: authority.pubkey,
+            funder: funder.pubkey,
+            system_program: system_program.pubkey,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/deactivate_lookup_table.rs b/decoders/address_lookup_table_decoder/src/instructions/deactivate_lookup_table.rs
new file mode 100644
index 000000000..220a8383a
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/deactivate_lookup_table.rs
@@ -0,0 +1,30 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+#[carbon(discriminator = "0x03000000")]
+pub struct DeactivateLookupTable {}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+pub struct DeactivateLookupTableInstructionAccounts {
+    pub look_up_table: solana_pubkey::Pubkey,
+    pub authority: solana_pubkey::Pubkey,
+}
+
+impl carbon_core::deserialize::ArrangeAccounts for DeactivateLookupTable {
+    type ArrangedAccounts = DeactivateLookupTableInstructionAccounts;
+
+    fn arrange_accounts(
+        accounts: &[solana_instruction::AccountMeta],
+    ) -> Option<Self::ArrangedAccounts> {
+        let [look_up_table, authority, _remaining @ ..] = accounts else {
+            return None;
+        };
+
+        Some(DeactivateLookupTableInstructionAccounts {
+            look_up_table: look_up_table.pubkey,
+            authority: authority.pubkey,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/extend_lookup_table.rs b/decoders/address_lookup_table_decoder/src/instructions/extend_lookup_table.rs
new file mode 100644
index 000000000..f5f7fbe22
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/extend_lookup_table.rs
@@ -0,0 +1,36 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+#[carbon(discriminator = "0x02000000")]
+pub struct ExtendLookupTable {
+    pub new_addresses: Vec<solana_pubkey::Pubkey>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+pub struct ExtendLookupTableInstructionAccounts {
+    pub look_up_table: solana_pubkey::Pubkey,
+    pub authority: solana_pubkey::Pubkey,
+    pub funder: solana_pubkey::Pubkey,
+    pub system_program: solana_pubkey::Pubkey,
+}
+
+impl carbon_core::deserialize::ArrangeAccounts for ExtendLookupTable {
+    type ArrangedAccounts = ExtendLookupTableInstructionAccounts;
+
+    fn arrange_accounts(
+        accounts: &[solana_instruction::AccountMeta],
+    ) -> Option<Self::ArrangedAccounts> {
+        let [look_up_table, authority, funder, system_program, _remaining @ ..] = accounts else {
+            return None;
+        };
+
+        Some(ExtendLookupTableInstructionAccounts {
+            look_up_table: look_up_table.pubkey,
+            authority: authority.pubkey,
+            funder: funder.pubkey,
+            system_program: system_program.pubkey,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/freeze_lookup_table.rs b/decoders/address_lookup_table_decoder/src/instructions/freeze_lookup_table.rs
new file mode 100644
index 000000000..51d1a08e8
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/freeze_lookup_table.rs
@@ -0,0 +1,30 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+#[carbon(discriminator = "0x01000000")]
+pub struct FreezeLookupTable {}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize, serde::Deserialize)]
+pub struct FreezeLookupTableInstructionAccounts {
+    pub look_up_table: solana_pubkey::Pubkey,
+    pub authority: solana_pubkey::Pubkey,
+}
+
+impl carbon_core::deserialize::ArrangeAccounts for FreezeLookupTable {
+    type ArrangedAccounts = FreezeLookupTableInstructionAccounts;
+
+    fn arrange_accounts(
+        accounts: &[solana_instruction::AccountMeta],
+    ) -> Option<Self::ArrangedAccounts> {
+        let [look_up_table, authority, _remaining @ ..] = accounts else {
+            return None;
+        };
+
+        Some(FreezeLookupTableInstructionAccounts {
+            look_up_table: look_up_table.pubkey,
+            authority: authority.pubkey,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/instructions/mod.rs b/decoders/address_lookup_table_decoder/src/instructions/mod.rs
new file mode 100644
index 000000000..57284b802
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/instructions/mod.rs
@@ -0,0 +1,76 @@
+use {super::AddressLookupTableDecoder, crate::PROGRAM_ID};
+pub mod close_lookup_table;
+pub mod create_lookup_table;
+pub mod deactivate_lookup_table;
+pub mod extend_lookup_table;
+pub mod freeze_lookup_table;
+
+#[derive(
+    carbon_core::InstructionType,
+    serde::Serialize,
+    serde::Deserialize,
+    PartialEq,
+    Eq,
+    Debug,
+    Clone,
+    Hash,
+)]
+
+pub enum AddressLookupTableInstruction {
+    CreateLookupTable(create_lookup_table::CreateLookupTable),
+    FreezeLookupTable(freeze_lookup_table::FreezeLookupTable),
+    ExtendLookupTable(extend_lookup_table::ExtendLookupTable),
+    DeactivateLookupTable(deactivate_lookup_table::DeactivateLookupTable),
+    CloseLookupTable(close_lookup_table::CloseLookupTable),
+}
+
+impl carbon_core::instruction::InstructionDecoder<'_> for AddressLookupTableDecoder {
+    type InstructionType = AddressLookupTableInstruction;
+
+    fn decode_instruction(
+        &self,
+        instruction: &solana_instruction::Instruction,
+    ) -> Option<carbon_core::instruction::DecodedInstruction<Self::InstructionType>> {
+        if !instruction.program_id.eq(&PROGRAM_ID) {
+            return None;
+        }
+        if instruction.data.len() < 4 {
+            return None;
+        }
+        let (discriminator, data) = instruction.data.split_at(4);
+        let entry = match discriminator {
+            [0, 0, 0, 0] => {
+                let args: create_lookup_table::CreateLookupTable =
+                    bincode::deserialize(data).ok()?;
+                AddressLookupTableInstruction::CreateLookupTable(args)
+            }
+            [1, 0, 0, 0] => {
+                let args: freeze_lookup_table::FreezeLookupTable =
+                    bincode::deserialize(data).ok()?;
+                AddressLookupTableInstruction::FreezeLookupTable(args)
+            }
+            [2, 0, 0, 0] => {
+                let args: extend_lookup_table::ExtendLookupTable =
+                    bincode::deserialize(data).ok()?;
+                AddressLookupTableInstruction::ExtendLookupTable(args)
+            }
+            [3, 0, 0, 0] => {
+                let args: deactivate_lookup_table::DeactivateLookupTable =
+                    bincode::deserialize(data).ok()?;
+
+                AddressLookupTableInstruction::DeactivateLookupTable(args)
+            }
+            [4, 0, 0, 0] => {
+                let args: close_lookup_table::CloseLookupTable = bincode::deserialize(data).ok()?;
+                AddressLookupTableInstruction::CloseLookupTable(args)
+            }
+            _ => return None,
+        };
+
+        Some(carbon_core::instruction::DecodedInstruction {
+            program_id: instruction.program_id,
+            accounts: instruction.accounts.clone(),
+            data: entry,
+        })
+    }
+}
diff --git a/decoders/address_lookup_table_decoder/src/lib.rs b/decoders/address_lookup_table_decoder/src/lib.rs
new file mode 100644
index 000000000..0521208fa
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/lib.rs
@@ -0,0 +1,9 @@
+use solana_pubkey::Pubkey;
+
+pub struct AddressLookupTableDecoder;
+pub mod accounts;
+pub mod instructions;
+pub mod types;
+
+pub const PROGRAM_ID: Pubkey =
+    Pubkey::from_str_const("AddressLookupTab1e1111111111111111111111111");
diff --git a/decoders/address_lookup_table_decoder/src/types/lookup_table_addresses.rs b/decoders/address_lookup_table_decoder/src/types/lookup_table_addresses.rs
new file mode 100644
index 000000000..571dc61cf
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/types/lookup_table_addresses.rs
@@ -0,0 +1,8 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+pub struct LookupTableAddresses {
+    pub addresses: Vec<solana_pubkey::Pubkey>,
+}
diff --git a/decoders/address_lookup_table_decoder/src/types/lookup_table_meta.rs b/decoders/address_lookup_table_decoder/src/types/lookup_table_meta.rs
new file mode 100644
index 000000000..2b7e7a005
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/types/lookup_table_meta.rs
@@ -0,0 +1,12 @@
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(
+    CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Hash,
+)]
+pub struct LookupTableMeta {
+    pub deactivation_slot: u64,
+    pub last_extended_slot: u64,
+    pub last_extended_slot_start_index: u8,
+    pub authority: Option<solana_pubkey::Pubkey>,
+    pub padding: u16,
+}
diff --git a/decoders/address_lookup_table_decoder/src/types/mod.rs b/decoders/address_lookup_table_decoder/src/types/mod.rs
new file mode 100644
index 000000000..3e0e65a5b
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/types/mod.rs
@@ -0,0 +1,6 @@
+pub mod lookup_table_meta;
+pub use lookup_table_meta::*;
+pub mod lookup_table_addresses;
+pub use lookup_table_addresses::*;
+pub mod state;
+pub use state::*;
diff --git a/decoders/address_lookup_table_decoder/src/types/state.rs b/decoders/address_lookup_table_decoder/src/types/state.rs
new file mode 100644
index 000000000..e06e0f5af
--- /dev/null
+++ b/decoders/address_lookup_table_decoder/src/types/state.rs
@@ -0,0 +1,10 @@
+use {
+    super::LookupTableMeta,
+    serde::{Deserialize, Serialize},
+};
+
+#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
+pub enum ProgramState {
+    Uninitialized,
+    LookupTable(LookupTableMeta),
+}
diff --git a/decoders/token-program-decoder/src/lib.rs b/decoders/token-program-decoder/src/lib.rs
index 403bc90d6..191b3a347 100644
--- a/decoders/token-program-decoder/src/lib.rs
+++ b/decoders/token-program-decoder/src/lib.rs
@@ -5,4 +5,4 @@ pub mod instructions;
 pub mod types;
 
 #[cfg(feature = "postgres")]
-pub mod storage;
\ No newline at end of file
+pub mod storage;
diff --git a/examples/token-indexing/src/main.rs b/examples/token-indexing/src/main.rs
index 8d195e6d5..8d254ff21 100644
--- a/examples/token-indexing/src/main.rs
+++ b/examples/token-indexing/src/main.rs
@@ -69,7 +69,10 @@ pub async fn main() -> CarbonResult<()> {
     let mut transaction_filters: HashMap<String, SubscribeRequestFilterTransactions> =
         HashMap::new();
 
-    transaction_filters.insert("spl_token_transaction_filter".to_string(), transaction_filter);
+    transaction_filters.insert(
+        "spl_token_transaction_filter".to_string(),
+        transaction_filter,
+    );
 
     let yellowstone_grpc = YellowstoneGrpcGeyserClient::new(
         env::var("GEYSER_URL").unwrap_or_default(),
diff --git a/scripts/publish-crate.sh b/scripts/publish-crate.sh
index f8418ba44..4d1589e3d 100644
--- a/scripts/publish-crate.sh
+++ b/scripts/publish-crate.sh
@@ -21,6 +21,7 @@ workspace_crates=(
     carbon-log-metrics
     carbon-prometheus-metrics
 
+    address-lookup-table-decoder
     carbon-associated-token-account-decoder
     carbon-boop-decoder
     carbon-drift-v2-decoder


Reviews:
user: login: sonicfromnewyoke
body: some small nits, and i'll be tthankfull for adding this program in the [root's readme table](https://github.com/sevenlabs-hq/carbon?tab=readme-ov-file#available-program-decoders)
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T12:29:42Z
user: login: sonicfromnewyoke
body: LG2M
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-10T07:15:12Z

Review comments:
diff_hunk: @@ -21,6 +21,7 @@ workspace_crates=(
     carbon-log-metrics
     carbon-prometheus-metrics
 
+    address-lookup-table-decoder
path: scripts/publish-crate.sh
user: login: sonicfromnewyoke
body: ```suggestion
    carbon-address-lookup-table-decoder
```
created_at: 2025-05-09T12:25:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,10 @@
+use super::super::types::*;
+
+use carbon_core::{borsh, CarbonDeserialize};
+
+#[derive(CarbonDeserialize, Debug, serde::Serialize, serde::Deserialize)]
+#[carbon(discriminator = "0x51c5b457ed001663")]
path: decoders/address_lookup_table_decoder/src/accounts/address_lookup_table.rs
user: login: sonicfromnewyoke
body: with 100% can say this is not working part. Tables doesn't have discriminator
created_at: 2025-05-09T12:26:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+[package]
+name = "carbon-address_lookup_table_decoder"
path: decoders/address_lookup_table_decoder/Cargo.toml
user: login: sonicfromnewyoke
body: ```suggestion
name = "carbon-address-lookup-table-decoder"
```
created_at: 2025-05-09T12:26:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,45 @@
+use {
+    super::AddressLookupTableDecoder,
+    carbon_core::{account::AccountDecoder, deserialize::CarbonDeserialize},
+};
+pub mod address_lookup_table;
+pub mod uninitialized;
+
+pub enum AddressLookupTableAccount {
+    Uninitialized(uninitialized::Uninitialized),
+    AddressLookupTable(address_lookup_table::AddressLookupTable),
+}
+
+impl AccountDecoder<'_> for AddressLookupTableDecoder {
+    type AccountType = AddressLookupTableAccount;
+    fn decode_account(
+        &self,
+        account: &solana_account::Account,
+    ) -> Option<carbon_core::account::DecodedAccount<Self::AccountType>> {
path: decoders/address_lookup_table_decoder/src/accounts/mod.rs
user: login: sonicfromnewyoke
body: pls add check against the `program_id`
created_at: 2025-05-09T12:28:01Z
author_association: COLLABORATOR

PR comments:
user: login: nogo6260
created_at: 2025-05-10T01:34:14Z
body: ALT deserialize using `bincode`
author_association: CONTRIBUTOR
user: login: sonicfromnewyoke
created_at: 2025-05-10T07:15:55Z
body: @nogo6260 thank you for the contribution 🚀
author_association: COLLABORATOR