Skip to main content

sol_parser_sdk/grpc/
program_ids.rs

1use crate::grpc::types::Protocol;
2use once_cell::sync::Lazy;
3use solana_sdk::pubkey;
4use solana_sdk::pubkey::Pubkey;
5use std::collections::HashMap;
6
7// Program IDs for supported DEX protocols (string format)
8pub const PUMPFUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
9pub const PUMPSWAP_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA";
10pub const PUMPSWAP_FEES_PROGRAM_ID: &str = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ";
11pub const RAYDIUM_LAUNCHLAB_PROGRAM_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
12pub const RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
13pub const RAYDIUM_CLMM_PROGRAM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK";
14pub const RAYDIUM_AMM_V4_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
15pub const ORCA_WHIRLPOOL_PROGRAM_ID: &str = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc";
16pub const METEORA_POOLS_PROGRAM_ID: &str = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB";
17pub const METEORA_DAMM_V2_PROGRAM_ID: &str = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG";
18pub const METEORA_DLMM_PROGRAM_ID: &str = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo";
19pub const METEORA_DBC_PROGRAM_ID: &str = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN";
20
21// Compatibility names reuse the canonical instruction-level Pubkey constants.
22pub use crate::instr::program_ids::{
23    METEORA_DAMM_V2_PROGRAM_ID as METEORA_DAMM_V2_PROGRAM,
24    METEORA_DBC_PROGRAM_ID as METEORA_DBC_PROGRAM, METEORA_DLMM_PROGRAM_ID as METEORA_DLMM_PROGRAM,
25    METEORA_POOLS_PROGRAM_ID as METEORA_POOLS_PROGRAM,
26    ORCA_WHIRLPOOL_PROGRAM_ID as ORCA_WHIRLPOOL_PROGRAM, PUMPFUN_PROGRAM_ID as PUMPFUN_PROGRAM,
27    PUMPSWAP_PROGRAM_ID as PUMPSWAP_PROGRAM, PUMP_FEES_PROGRAM_ID as PUMPSWAP_FEES_PROGRAM,
28    RAYDIUM_AMM_V4_PROGRAM_ID as RAYDIUM_AMM_V4_PROGRAM,
29    RAYDIUM_CLMM_PROGRAM_ID as RAYDIUM_CLMM_PROGRAM,
30    RAYDIUM_CPMM_PROGRAM_ID as RAYDIUM_CPMM_PROGRAM,
31    RAYDIUM_LAUNCHLAB_PROGRAM_ID as RAYDIUM_LAUNCHLAB_PROGRAM,
32};
33
34/// Precompiles are not invoked through the BPF loader and emit no
35/// `Program … invoke [1]` log line, so log-derived outer indices must skip them.
36pub const LOGLESS_PRECOMPILES: [Pubkey; 3] = [
37    pubkey!("Ed25519SigVerify111111111111111111111111111"),
38    pubkey!("KeccakSecp256k11111111111111111111111111111"),
39    pubkey!("Secp256r1SigVerify1111111111111111111111111"),
40];
41
42/// Avoid base58 decoding for program ids emitted by the DEXes this crate parses.
43#[inline(always)]
44pub(crate) fn known_program_id(program_id: &str) -> Option<Pubkey> {
45    match program_id {
46        PUMPFUN_PROGRAM_ID => Some(PUMPFUN_PROGRAM),
47        PUMPSWAP_PROGRAM_ID => Some(PUMPSWAP_PROGRAM),
48        PUMPSWAP_FEES_PROGRAM_ID => Some(PUMPSWAP_FEES_PROGRAM),
49        RAYDIUM_LAUNCHLAB_PROGRAM_ID => Some(RAYDIUM_LAUNCHLAB_PROGRAM),
50        RAYDIUM_CPMM_PROGRAM_ID => Some(RAYDIUM_CPMM_PROGRAM),
51        RAYDIUM_CLMM_PROGRAM_ID => Some(RAYDIUM_CLMM_PROGRAM),
52        RAYDIUM_AMM_V4_PROGRAM_ID => Some(RAYDIUM_AMM_V4_PROGRAM),
53        ORCA_WHIRLPOOL_PROGRAM_ID => Some(ORCA_WHIRLPOOL_PROGRAM),
54        METEORA_POOLS_PROGRAM_ID => Some(METEORA_POOLS_PROGRAM),
55        METEORA_DAMM_V2_PROGRAM_ID => Some(METEORA_DAMM_V2_PROGRAM),
56        METEORA_DLMM_PROGRAM_ID => Some(METEORA_DLMM_PROGRAM),
57        METEORA_DBC_PROGRAM_ID => Some(METEORA_DBC_PROGRAM),
58        _ => None,
59    }
60}
61
62/// Programs whose instruction positions are queried later by account/data fillers.
63#[inline(always)]
64pub(crate) fn needs_invoke_context(program_id: &Pubkey) -> bool {
65    matches!(
66        *program_id,
67        PUMPFUN_PROGRAM
68            | PUMPSWAP_PROGRAM
69            | PUMPSWAP_FEES_PROGRAM
70            | RAYDIUM_LAUNCHLAB_PROGRAM
71            | RAYDIUM_CPMM_PROGRAM
72            | RAYDIUM_CLMM_PROGRAM
73            | RAYDIUM_AMM_V4_PROGRAM
74            | ORCA_WHIRLPOOL_PROGRAM
75            | METEORA_POOLS_PROGRAM
76            | METEORA_DAMM_V2_PROGRAM
77            | METEORA_DLMM_PROGRAM
78    )
79}
80
81pub static PROTOCOL_PROGRAM_IDS: Lazy<HashMap<Protocol, Vec<&'static str>>> = Lazy::new(|| {
82    let mut map = HashMap::new();
83    map.insert(Protocol::PumpFun, vec![PUMPFUN_PROGRAM_ID]);
84    map.insert(Protocol::PumpSwap, vec![PUMPSWAP_PROGRAM_ID]);
85    map.insert(Protocol::PumpFees, vec![PUMPSWAP_FEES_PROGRAM_ID]);
86    for protocol in [Protocol::LaunchLab, Protocol::StonkFun, Protocol::RaydiumLaunchlab] {
87        map.insert(protocol, vec![RAYDIUM_LAUNCHLAB_PROGRAM_ID]);
88    }
89    map.insert(Protocol::RaydiumCpmm, vec![RAYDIUM_CPMM_PROGRAM_ID]);
90    map.insert(Protocol::RaydiumClmm, vec![RAYDIUM_CLMM_PROGRAM_ID]);
91    map.insert(Protocol::RaydiumAmmV4, vec![RAYDIUM_AMM_V4_PROGRAM_ID]);
92    map.insert(Protocol::OrcaWhirlpool, vec![ORCA_WHIRLPOOL_PROGRAM_ID]);
93    map.insert(Protocol::MeteoraPools, vec![METEORA_POOLS_PROGRAM_ID]);
94    map.insert(Protocol::MeteoraDammV2, vec![METEORA_DAMM_V2_PROGRAM_ID]);
95    map.insert(Protocol::MeteoraDlmm, vec![METEORA_DLMM_PROGRAM_ID]);
96    map.insert(Protocol::MeteoraDbc, vec![METEORA_DBC_PROGRAM_ID]);
97    map
98});
99
100pub fn get_program_ids_for_protocols(protocols: &[Protocol]) -> Vec<String> {
101    let mut program_ids = Vec::new();
102    for protocol in protocols {
103        if let Some(ids) = PROTOCOL_PROGRAM_IDS.get(protocol) {
104            for id in ids {
105                program_ids.push(id.to_string());
106            }
107        }
108    }
109    program_ids.sort();
110    program_ids.dedup();
111    program_ids
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::instr;
118
119    #[test]
120    fn grpc_program_ids_match_instruction_program_ids() {
121        assert_eq!(PUMPFUN_PROGRAM, instr::program_ids::PUMPFUN_PROGRAM_ID);
122        assert_eq!(PUMPSWAP_PROGRAM, instr::program_ids::PUMPSWAP_PROGRAM_ID);
123        assert_eq!(PUMPSWAP_FEES_PROGRAM, instr::program_ids::PUMP_FEES_PROGRAM_ID);
124        assert_eq!(RAYDIUM_LAUNCHLAB_PROGRAM, instr::program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID);
125        assert_eq!(RAYDIUM_CPMM_PROGRAM, instr::program_ids::RAYDIUM_CPMM_PROGRAM_ID);
126        assert_eq!(RAYDIUM_CLMM_PROGRAM, instr::program_ids::RAYDIUM_CLMM_PROGRAM_ID);
127        assert_eq!(RAYDIUM_AMM_V4_PROGRAM, instr::program_ids::RAYDIUM_AMM_V4_PROGRAM_ID);
128        assert_eq!(ORCA_WHIRLPOOL_PROGRAM, instr::program_ids::ORCA_WHIRLPOOL_PROGRAM_ID);
129        assert_eq!(METEORA_POOLS_PROGRAM, instr::program_ids::METEORA_POOLS_PROGRAM_ID);
130        assert_eq!(METEORA_DAMM_V2_PROGRAM, instr::program_ids::METEORA_DAMM_V2_PROGRAM_ID);
131        assert_eq!(METEORA_DLMM_PROGRAM, instr::program_ids::METEORA_DLMM_PROGRAM_ID);
132        assert_eq!(METEORA_DBC_PROGRAM, instr::program_ids::METEORA_DBC_PROGRAM_ID);
133    }
134
135    #[test]
136    fn protocol_filter_maps_all_supported_protocols() {
137        let protocols = [
138            Protocol::PumpFun,
139            Protocol::PumpSwap,
140            Protocol::PumpFees,
141            Protocol::RaydiumLaunchlab,
142            Protocol::RaydiumCpmm,
143            Protocol::RaydiumClmm,
144            Protocol::RaydiumAmmV4,
145            Protocol::OrcaWhirlpool,
146            Protocol::MeteoraPools,
147            Protocol::MeteoraDammV2,
148            Protocol::MeteoraDlmm,
149            Protocol::MeteoraDbc,
150        ];
151        for protocol in protocols {
152            assert!(
153                PROTOCOL_PROGRAM_IDS.contains_key(&protocol),
154                "missing program id mapping for {protocol:?}"
155            );
156        }
157    }
158
159    #[test]
160    fn invoke_context_only_tracks_programs_used_by_fillers() {
161        for program_id in [
162            PUMPFUN_PROGRAM,
163            PUMPSWAP_PROGRAM,
164            PUMPSWAP_FEES_PROGRAM,
165            RAYDIUM_LAUNCHLAB_PROGRAM,
166            RAYDIUM_CPMM_PROGRAM,
167            RAYDIUM_CLMM_PROGRAM,
168            RAYDIUM_AMM_V4_PROGRAM,
169            ORCA_WHIRLPOOL_PROGRAM,
170            METEORA_POOLS_PROGRAM,
171            METEORA_DAMM_V2_PROGRAM,
172            METEORA_DLMM_PROGRAM,
173        ] {
174            assert!(needs_invoke_context(&program_id));
175        }
176        assert!(!needs_invoke_context(&Pubkey::new_unique()));
177    }
178
179    #[test]
180    fn known_log_program_ids_map_without_decoding() {
181        for (encoded, expected) in [
182            (PUMPFUN_PROGRAM_ID, PUMPFUN_PROGRAM),
183            (PUMPSWAP_PROGRAM_ID, PUMPSWAP_PROGRAM),
184            (PUMPSWAP_FEES_PROGRAM_ID, PUMPSWAP_FEES_PROGRAM),
185            (RAYDIUM_LAUNCHLAB_PROGRAM_ID, RAYDIUM_LAUNCHLAB_PROGRAM),
186            (RAYDIUM_CPMM_PROGRAM_ID, RAYDIUM_CPMM_PROGRAM),
187            (RAYDIUM_CLMM_PROGRAM_ID, RAYDIUM_CLMM_PROGRAM),
188            (RAYDIUM_AMM_V4_PROGRAM_ID, RAYDIUM_AMM_V4_PROGRAM),
189            (ORCA_WHIRLPOOL_PROGRAM_ID, ORCA_WHIRLPOOL_PROGRAM),
190            (METEORA_POOLS_PROGRAM_ID, METEORA_POOLS_PROGRAM),
191            (METEORA_DAMM_V2_PROGRAM_ID, METEORA_DAMM_V2_PROGRAM),
192            (METEORA_DLMM_PROGRAM_ID, METEORA_DLMM_PROGRAM),
193            (METEORA_DBC_PROGRAM_ID, METEORA_DBC_PROGRAM),
194        ] {
195            assert_eq!(known_program_id(encoded), Some(expected));
196        }
197        assert_eq!(known_program_id("11111111111111111111111111111111"), None);
198    }
199}