sol_parser_sdk/logs/
zero_copy_parser.rs1use solana_sdk::{pubkey::Pubkey, signature::Signature};
4use crate::core::events::*;
5use memchr::memmem;
6use base64::{Engine as _, engine::general_purpose};
7use super::perf_hints::prefetch_read;
8
9#[inline(always)]
11pub fn parse_pumpfun_trade(
12 log: &str,
13 signature: Signature,
14 slot: u64,
15 tx_index: u64,
16 block_time_us: Option<i64>,
17 grpc_recv_us: i64,
18 is_created_buy: bool,
19) -> Option<DexEvent> {
20 const MAX_DECODE_SIZE: usize = 512;
23 let mut decode_buf: [u8; MAX_DECODE_SIZE] = [0u8; MAX_DECODE_SIZE];
24
25 let log_bytes = log.as_bytes();
27 let pos = memmem::find(log_bytes, b"Program data: ")?;
28 let data_part = log[pos + 14..].trim();
29
30 if data_part.len() < 12 {
33 return None;
34 }
35
36 let disc_decoded_len = general_purpose::STANDARD
38 .decode_slice(&data_part.as_bytes()[..12], &mut decode_buf[..9])
39 .ok()?;
40
41 if disc_decoded_len < 8 {
42 return None;
43 }
44
45 const TRADE_DISCRIMINATOR: [u8; 8] = [189, 219, 127, 211, 78, 230, 97, 238];
47
48 if decode_buf[..8] != TRADE_DISCRIMINATOR {
49 return None;
50 }
51
52 let decoded_len = general_purpose::STANDARD
54 .decode_slice(data_part.as_bytes(), &mut decode_buf)
55 .ok()?;
56
57 if decoded_len < 96 {
58 return None;
59 }
60
61 let data = &decode_buf[8..decoded_len];
62 let mut offset = 0;
63
64 unsafe {
66 if data.len() >= 64 {
67 prefetch_read(data.as_ptr().add(32));
68 }
69 }
70
71 let mint = read_pubkey_inline(data, offset)?;
73 offset += 32;
74
75 let sol_amount = read_u64_le_inline(data, offset)?;
76 offset += 8;
77
78 let token_amount = read_u64_le_inline(data, offset)?;
79 offset += 8;
80
81 let is_buy = read_u8_inline(data, offset)?;
82 offset += 1;
83
84 let user = read_pubkey_inline(data, offset)?;
85 offset += 32;
86
87 let timestamp = read_i64_le_inline(data, offset)?;
88 offset += 8;
89
90 let virtual_sol_reserves = read_u64_le_inline(data, offset)?;
91 offset += 8;
92
93 let virtual_token_reserves = read_u64_le_inline(data, offset)?;
94 offset += 8;
95
96 let real_sol_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
97 offset += 8;
98
99 let real_token_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
100 offset += 8;
101
102 let fee_recipient = read_pubkey_inline(data, offset).unwrap_or_default();
103 offset += 32;
104
105 let fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
106 offset += 8;
107
108 let fee = read_u64_le_inline(data, offset).unwrap_or(0);
109 offset += 8;
110
111 let creator = read_pubkey_inline(data, offset).unwrap_or_default();
112 offset += 32;
113
114 let creator_fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
115 offset += 8;
116
117 let creator_fee = read_u64_le_inline(data, offset).unwrap_or(0);
118 offset += 8;
119
120 let track_volume = read_u8_inline(data, offset).unwrap_or(0) != 0;
121 offset += 1;
122
123 let total_unclaimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
124 offset += 8;
125
126 let total_claimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
127 offset += 8;
128
129 let current_sol_volume = read_u64_le_inline(data, offset).unwrap_or(0);
130
131 let metadata = EventMetadata {
132 signature,
133 slot,
134 tx_index,
135 block_time_us: block_time_us.unwrap_or(0),
136 grpc_recv_us,
137 recent_blockhash: None,
138 };
139
140 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
141 metadata,
142 mint,
143 sol_amount,
144 token_amount,
145 is_buy: is_buy != 0,
146 is_created_buy,
147 user,
148 timestamp,
149 virtual_sol_reserves,
150 virtual_token_reserves,
151 real_sol_reserves,
152 real_token_reserves,
153 fee_recipient,
154 fee_basis_points,
155 fee,
156 creator,
157 creator_fee_basis_points,
158 creator_fee,
159 track_volume,
160 total_unclaimed_tokens,
161 total_claimed_tokens,
162 current_sol_volume,
163 ..Default::default()
164 }))
165}
166
167#[inline(always)]
169fn read_pubkey_inline(data: &[u8], offset: usize) -> Option<Pubkey> {
170 if offset + 32 <= data.len() {
171 let mut bytes = [0u8; 32];
172 bytes.copy_from_slice(&data[offset..offset + 32]);
173 Some(Pubkey::new_from_array(bytes))
174 } else {
175 None
176 }
177}
178
179#[inline(always)]
181fn read_u64_le_inline(data: &[u8], offset: usize) -> Option<u64> {
182 if offset + 8 <= data.len() {
183 let mut bytes = [0u8; 8];
184 bytes.copy_from_slice(&data[offset..offset + 8]);
185 Some(u64::from_le_bytes(bytes))
186 } else {
187 None
188 }
189}
190
191#[inline(always)]
193fn read_i64_le_inline(data: &[u8], offset: usize) -> Option<i64> {
194 if offset + 8 <= data.len() {
195 let mut bytes = [0u8; 8];
196 bytes.copy_from_slice(&data[offset..offset + 8]);
197 Some(i64::from_le_bytes(bytes))
198 } else {
199 None
200 }
201}
202
203#[inline(always)]
205fn read_u8_inline(data: &[u8], offset: usize) -> Option<u8> {
206 data.get(offset).copied()
207}