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