sol_parser_sdk/logs/
zero_copy_parser.rs1use super::perf_hints::prefetch_read;
4use crate::core::events::*;
5use base64::{engine::general_purpose, Engine as _};
6use memchr::memmem;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
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 =
54 general_purpose::STANDARD.decode_slice(data_part.as_bytes(), &mut decode_buf).ok()?;
55
56 if decoded_len < 96 {
57 return None;
58 }
59
60 let data = &decode_buf[8..decoded_len];
61 let mut offset = 0;
62
63 unsafe {
65 if data.len() >= 64 {
66 prefetch_read(data.as_ptr().add(32));
67 }
68 }
69
70 let mint = read_pubkey_inline(data, offset)?;
72 offset += 32;
73
74 let sol_amount = read_u64_le_inline(data, offset)?;
75 offset += 8;
76
77 let token_amount = read_u64_le_inline(data, offset)?;
78 offset += 8;
79
80 let is_buy = read_u8_inline(data, offset)?;
81 offset += 1;
82
83 let user = read_pubkey_inline(data, offset)?;
84 offset += 32;
85
86 let timestamp = read_i64_le_inline(data, offset)?;
87 offset += 8;
88
89 let virtual_sol_reserves = read_u64_le_inline(data, offset)?;
90 offset += 8;
91
92 let virtual_token_reserves = read_u64_le_inline(data, offset)?;
93 offset += 8;
94
95 let real_sol_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
96 offset += 8;
97
98 let real_token_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
99 offset += 8;
100
101 let fee_recipient = read_pubkey_inline(data, offset).unwrap_or_default();
102 offset += 32;
103
104 let fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
105 offset += 8;
106
107 let fee = read_u64_le_inline(data, offset).unwrap_or(0);
108 offset += 8;
109
110 let creator = read_pubkey_inline(data, offset).unwrap_or_default();
111 offset += 32;
112
113 let creator_fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
114 offset += 8;
115
116 let creator_fee = read_u64_le_inline(data, offset).unwrap_or(0);
117 offset += 8;
118
119 let track_volume = read_u8_inline(data, offset).unwrap_or(0) != 0;
120 offset += 1;
121
122 let total_unclaimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
123 offset += 8;
124
125 let total_claimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
126 offset += 8;
127
128 let current_sol_volume = read_u64_le_inline(data, offset).unwrap_or(0);
129
130 let metadata = EventMetadata {
131 signature,
132 slot,
133 tx_index,
134 block_time_us: block_time_us.unwrap_or(0),
135 grpc_recv_us,
136 recent_blockhash: None,
137 };
138
139 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
140 metadata,
141 mint,
142 sol_amount,
143 token_amount,
144 is_buy: is_buy != 0,
145 is_created_buy,
146 user,
147 timestamp,
148 virtual_sol_reserves,
149 virtual_token_reserves,
150 real_sol_reserves,
151 real_token_reserves,
152 fee_recipient,
153 fee_basis_points,
154 fee,
155 creator,
156 creator_fee_basis_points,
157 creator_fee,
158 track_volume,
159 total_unclaimed_tokens,
160 total_claimed_tokens,
161 current_sol_volume,
162 ..Default::default()
163 }))
164}
165
166#[inline(always)]
168fn read_pubkey_inline(data: &[u8], offset: usize) -> Option<Pubkey> {
169 if offset + 32 <= data.len() {
170 let mut bytes = [0u8; 32];
171 bytes.copy_from_slice(&data[offset..offset + 32]);
172 Some(Pubkey::new_from_array(bytes))
173 } else {
174 None
175 }
176}
177
178#[inline(always)]
180fn read_u64_le_inline(data: &[u8], offset: usize) -> Option<u64> {
181 if offset + 8 <= data.len() {
182 let mut bytes = [0u8; 8];
183 bytes.copy_from_slice(&data[offset..offset + 8]);
184 Some(u64::from_le_bytes(bytes))
185 } else {
186 None
187 }
188}
189
190#[inline(always)]
192fn read_i64_le_inline(data: &[u8], offset: usize) -> Option<i64> {
193 if offset + 8 <= data.len() {
194 let mut bytes = [0u8; 8];
195 bytes.copy_from_slice(&data[offset..offset + 8]);
196 Some(i64::from_le_bytes(bytes))
197 } else {
198 None
199 }
200}
201
202#[inline(always)]
204fn read_u8_inline(data: &[u8], offset: usize) -> Option<u8> {
205 data.get(offset).copied()
206}