Skip to main content

sol_parser_sdk/logs/
meteora_dbc.rs

1//! Meteora DBC log parser.
2
3use super::utils::*;
4use crate::core::events::*;
5use solana_sdk::signature::Signature;
6
7pub mod discriminators {
8    pub const SWAP_EVENT: [u8; 8] = [27, 60, 21, 213, 138, 170, 187, 147];
9    pub const INITIALIZE_POOL_EVENT: [u8; 8] = [228, 50, 246, 85, 203, 66, 134, 37];
10    pub const CURVE_COMPLETE_EVENT: [u8; 8] = [229, 231, 86, 84, 156, 134, 75, 24];
11}
12
13pub fn parse_log(
14    log: &str,
15    signature: Signature,
16    slot: u64,
17    tx_index: u64,
18    block_time_us: Option<i64>,
19    grpc_recv_us: i64,
20) -> Option<DexEvent> {
21    let program_data = extract_program_data(log)?;
22    if program_data.len() < 8 {
23        return None;
24    }
25
26    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
27    let data = &program_data[8..];
28    let pool = read_pubkey(data, 0).unwrap_or_default();
29    let metadata =
30        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
31
32    match discriminator {
33        discriminators::SWAP_EVENT => parse_swap_from_data(data, metadata),
34        discriminators::INITIALIZE_POOL_EVENT => parse_initialize_pool_from_data(data, metadata),
35        discriminators::CURVE_COMPLETE_EVENT => parse_curve_complete_from_data(data, metadata),
36        _ => None,
37    }
38}
39
40#[inline(always)]
41pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
42    let mut offset = 0;
43
44    let pool = read_pubkey(data, offset)?;
45    offset += 32;
46    let config = read_pubkey(data, offset)?;
47    offset += 32;
48    let trade_direction = read_u8(data, offset)?;
49    offset += 1;
50    let has_referral = read_bool(data, offset)?;
51    offset += 1;
52    let params_amount_in = read_u64_le(data, offset)?;
53    offset += 8;
54    let minimum_amount_out = read_u64_le(data, offset)?;
55    offset += 8;
56    let actual_input_amount = read_u64_le(data, offset)?;
57    offset += 8;
58    let output_amount = read_u64_le(data, offset)?;
59    offset += 8;
60    let next_sqrt_price = read_u128_le(data, offset)?;
61    offset += 16;
62    let trading_fee = read_u64_le(data, offset)?;
63    offset += 8;
64    let protocol_fee = read_u64_le(data, offset)?;
65    offset += 8;
66    let referral_fee = read_u64_le(data, offset)?;
67    offset += 8;
68    let amount_in = read_u64_le(data, offset).unwrap_or(params_amount_in);
69    offset += 8;
70    let current_timestamp = read_u64_le(data, offset)?;
71
72    Some(DexEvent::MeteoraDbcSwap(MeteoraDbcSwapEvent {
73        metadata,
74        pool,
75        config,
76        trade_direction,
77        has_referral,
78        amount_in,
79        minimum_amount_out,
80        actual_input_amount,
81        output_amount,
82        next_sqrt_price,
83        trading_fee,
84        protocol_fee,
85        referral_fee,
86        current_timestamp,
87    }))
88}
89
90#[inline(always)]
91pub fn parse_initialize_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
92    let mut offset = 0;
93
94    let pool = read_pubkey(data, offset)?;
95    offset += 32;
96    let config = read_pubkey(data, offset)?;
97    offset += 32;
98    let creator = read_pubkey(data, offset)?;
99    offset += 32;
100    let base_mint = read_pubkey(data, offset)?;
101    offset += 32;
102    let pool_type = read_u8(data, offset)?;
103    offset += 1;
104    let activation_point = read_u64_le(data, offset)?;
105
106    Some(DexEvent::MeteoraDbcInitializePool(MeteoraDbcInitializePoolEvent {
107        metadata,
108        pool,
109        config,
110        creator,
111        base_mint,
112        pool_type,
113        activation_point,
114    }))
115}
116
117#[inline(always)]
118pub fn parse_curve_complete_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
119    let mut offset = 0;
120
121    let pool = read_pubkey(data, offset)?;
122    offset += 32;
123    let config = read_pubkey(data, offset)?;
124    offset += 32;
125    let base_reserve = read_u64_le(data, offset)?;
126    offset += 8;
127    let quote_reserve = read_u64_le(data, offset)?;
128
129    Some(DexEvent::MeteoraDbcCurveComplete(MeteoraDbcCurveCompleteEvent {
130        metadata,
131        pool,
132        config,
133        base_reserve,
134        quote_reserve,
135    }))
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use solana_sdk::pubkey::Pubkey;
142
143    fn push_pubkey(buf: &mut Vec<u8>, byte: u8) -> Pubkey {
144        let key = Pubkey::new_from_array([byte; 32]);
145        buf.extend_from_slice(key.as_ref());
146        key
147    }
148
149    #[test]
150    fn parses_dbc_swap_layout() {
151        let mut data = Vec::new();
152        let pool = push_pubkey(&mut data, 1);
153        let config = push_pubkey(&mut data, 2);
154        data.push(1);
155        data.push(1);
156        data.extend_from_slice(&10_u64.to_le_bytes());
157        data.extend_from_slice(&9_u64.to_le_bytes());
158        data.extend_from_slice(&10_u64.to_le_bytes());
159        data.extend_from_slice(&8_u64.to_le_bytes());
160        data.extend_from_slice(&(1_u128 << 64).to_le_bytes());
161        data.extend_from_slice(&1_u64.to_le_bytes());
162        data.extend_from_slice(&2_u64.to_le_bytes());
163        data.extend_from_slice(&3_u64.to_le_bytes());
164        data.extend_from_slice(&10_u64.to_le_bytes());
165        data.extend_from_slice(&123_u64.to_le_bytes());
166
167        let event = parse_swap_from_data(&data, EventMetadata::default()).unwrap();
168        match event {
169            DexEvent::MeteoraDbcSwap(event) => {
170                assert_eq!(event.pool, pool);
171                assert_eq!(event.config, config);
172                assert_eq!(event.trade_direction, 1);
173                assert!(event.has_referral);
174                assert_eq!(event.minimum_amount_out, 9);
175                assert_eq!(event.output_amount, 8);
176                assert_eq!(event.protocol_fee, 2);
177                assert_eq!(event.current_timestamp, 123);
178            }
179            other => panic!("expected MeteoraDbcSwap, got {other:?}"),
180        }
181    }
182
183    #[test]
184    fn parses_dbc_initialize_pool_layout() {
185        let mut data = Vec::new();
186        let pool = push_pubkey(&mut data, 1);
187        let config = push_pubkey(&mut data, 2);
188        let creator = push_pubkey(&mut data, 3);
189        let base_mint = push_pubkey(&mut data, 4);
190        data.push(2);
191        data.extend_from_slice(&456_u64.to_le_bytes());
192
193        let event = parse_initialize_pool_from_data(&data, EventMetadata::default()).unwrap();
194        match event {
195            DexEvent::MeteoraDbcInitializePool(event) => {
196                assert_eq!(event.pool, pool);
197                assert_eq!(event.config, config);
198                assert_eq!(event.creator, creator);
199                assert_eq!(event.base_mint, base_mint);
200                assert_eq!(event.pool_type, 2);
201                assert_eq!(event.activation_point, 456);
202            }
203            other => panic!("expected MeteoraDbcInitializePool, got {other:?}"),
204        }
205    }
206}