1use solana_sdk::pubkey::Pubkey;
13
14use crate::core::events::*;
15
16#[inline(always)]
28pub fn merge_events(base: &mut DexEvent, inner: DexEvent) {
29 let _ = try_merge_events(base, inner);
30}
31
32#[inline(always)]
36pub fn try_merge_events(base: &mut DexEvent, inner: DexEvent) -> Result<(), DexEvent> {
37 use DexEvent::*;
38
39 match (base, inner) {
40 (PumpFunTrade(b), PumpFunTrade(i))
42 | (PumpFunTrade(b), PumpFunBuy(i))
43 | (PumpFunTrade(b), PumpFunSell(i))
44 | (PumpFunTrade(b), PumpFunBuyExactSolIn(i))
45 | (PumpFunBuy(b), PumpFunTrade(i))
46 | (PumpFunBuy(b), PumpFunBuy(i))
47 | (PumpFunSell(b), PumpFunTrade(i))
48 | (PumpFunSell(b), PumpFunSell(i))
49 | (PumpFunBuyExactSolIn(b), PumpFunTrade(i))
50 | (PumpFunBuyExactSolIn(b), PumpFunBuyExactSolIn(i)) => merge_pumpfun_trade(b, i),
51
52 (PumpFunCreate(b), PumpFunCreate(i)) => merge_pumpfun_create(b, i),
53 (PumpFunCreateV2(b), PumpFunCreateV2(i)) => merge_pumpfun_create_v2(b, i),
54 (PumpFunMigrate(b), PumpFunMigrate(i)) => merge_pumpfun_migrate(b, i),
55 (PumpFunMigrateBondingCurveCreator(b), PumpFunMigrateBondingCurveCreator(i)) => {
56 merge_generic(b, i)
57 }
58
59 (PumpFeesCreateFeeSharingConfig(b), PumpFeesCreateFeeSharingConfig(i)) => {
61 merge_generic(b, i)
62 }
63 (PumpFeesInitializeFeeConfig(b), PumpFeesInitializeFeeConfig(i)) => merge_generic(b, i),
64 (PumpFeesResetFeeSharingConfig(b), PumpFeesResetFeeSharingConfig(i)) => merge_generic(b, i),
65 (PumpFeesRevokeFeeSharingAuthority(b), PumpFeesRevokeFeeSharingAuthority(i)) => {
66 merge_generic(b, i)
67 }
68 (PumpFeesTransferFeeSharingAuthority(b), PumpFeesTransferFeeSharingAuthority(i)) => {
69 merge_generic(b, i)
70 }
71 (PumpFeesUpdateAdmin(b), PumpFeesUpdateAdmin(i)) => merge_generic(b, i),
72 (PumpFeesUpdateFeeConfig(b), PumpFeesUpdateFeeConfig(i)) => merge_generic(b, i),
73 (PumpFeesUpdateFeeShares(b), PumpFeesUpdateFeeShares(i)) => merge_generic(b, i),
74 (PumpFeesUpsertFeeTiers(b), PumpFeesUpsertFeeTiers(i)) => merge_generic(b, i),
75
76 (PumpSwapTrade(b), PumpSwapTrade(i)) => merge_generic(b, i),
78 (PumpSwapBuy(b), PumpSwapBuy(i)) => merge_pumpswap_buy(b, i),
79 (PumpSwapSell(b), PumpSwapSell(i)) => merge_pumpswap_sell(b, i),
80 (PumpSwapCreatePool(b), PumpSwapCreatePool(i)) => merge_generic(b, i),
81 (PumpSwapLiquidityAdded(b), PumpSwapLiquidityAdded(i)) => merge_generic(b, i),
82 (PumpSwapLiquidityRemoved(b), PumpSwapLiquidityRemoved(i)) => merge_generic(b, i),
83
84 (RaydiumClmmSwap(b), RaydiumClmmSwap(i)) => merge_generic(b, i),
86 (RaydiumClmmIncreaseLiquidity(b), RaydiumClmmIncreaseLiquidity(i)) => merge_generic(b, i),
87 (RaydiumClmmDecreaseLiquidity(b), RaydiumClmmDecreaseLiquidity(i)) => merge_generic(b, i),
88 (RaydiumClmmLiquidityChange(b), RaydiumClmmLiquidityChange(i)) => merge_generic(b, i),
89 (RaydiumClmmConfigChange(b), RaydiumClmmConfigChange(i)) => merge_generic(b, i),
90 (RaydiumClmmCreatePersonalPosition(b), RaydiumClmmCreatePersonalPosition(i)) => {
91 merge_generic(b, i)
92 }
93 (RaydiumClmmLiquidityCalculate(b), RaydiumClmmLiquidityCalculate(i)) => merge_generic(b, i),
94 (RaydiumClmmOpenLimitOrder(b), RaydiumClmmOpenLimitOrder(i)) => merge_generic(b, i),
95 (RaydiumClmmIncreaseLimitOrder(b), RaydiumClmmIncreaseLimitOrder(i)) => merge_generic(b, i),
96 (RaydiumClmmDecreaseLimitOrder(b), RaydiumClmmDecreaseLimitOrder(i)) => merge_generic(b, i),
97 (RaydiumClmmSettleLimitOrder(b), RaydiumClmmSettleLimitOrder(i)) => merge_generic(b, i),
98 (RaydiumClmmUpdateRewardInfos(b), RaydiumClmmUpdateRewardInfos(i)) => merge_generic(b, i),
99 (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
100 (RaydiumClmmOpenPosition(b), RaydiumClmmOpenPosition(i)) => merge_generic(b, i),
101 (RaydiumClmmClosePosition(b), RaydiumClmmClosePosition(i)) => merge_generic(b, i),
102 (RaydiumClmmOpenPositionWithTokenExtNft(b), RaydiumClmmOpenPositionWithTokenExtNft(i)) => {
103 merge_generic(b, i)
104 }
105 (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
106
107 (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_generic(b, i),
109 (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
110 (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
111 (RaydiumCpmmInitialize(b), RaydiumCpmmInitialize(i)) => merge_generic(b, i),
112
113 (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => merge_generic(b, i),
115 (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
116 (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
117 (RaydiumAmmV4Initialize2(b), RaydiumAmmV4Initialize2(i)) => merge_generic(b, i),
118 (RaydiumAmmV4WithdrawPnl(b), RaydiumAmmV4WithdrawPnl(i)) => merge_generic(b, i),
119
120 (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => merge_generic(b, i),
122 (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => {
123 merge_generic(b, i)
124 }
125 (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => {
126 merge_generic(b, i)
127 }
128 (OrcaWhirlpoolPoolInitialized(b), OrcaWhirlpoolPoolInitialized(i)) => merge_generic(b, i),
129
130 (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => merge_generic(b, i),
132 (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => merge_generic(b, i),
133 (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => merge_generic(b, i),
134 (MeteoraPoolsBootstrapLiquidity(b), MeteoraPoolsBootstrapLiquidity(i)) => {
135 merge_generic(b, i)
136 }
137 (MeteoraPoolsPoolCreated(b), MeteoraPoolsPoolCreated(i)) => merge_generic(b, i),
138 (MeteoraPoolsSetPoolFees(b), MeteoraPoolsSetPoolFees(i)) => merge_generic(b, i),
139
140 (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
142 (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
143 (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
144 (MeteoraDammV2InitializePool(b), MeteoraDammV2InitializePool(i)) => merge_generic(b, i),
145 (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
146 (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
147
148 (MeteoraDlmmSwap(b), MeteoraDlmmSwap(i)) => merge_generic(b, i),
150 (MeteoraDlmmAddLiquidity(b), MeteoraDlmmAddLiquidity(i)) => merge_generic(b, i),
151 (MeteoraDlmmRemoveLiquidity(b), MeteoraDlmmRemoveLiquidity(i)) => merge_generic(b, i),
152 (MeteoraDlmmInitializePool(b), MeteoraDlmmInitializePool(i)) => {
153 merge_dlmm_initialize_pool(b, i)
154 }
155 (MeteoraDlmmInitializeBinArray(b), MeteoraDlmmInitializeBinArray(i)) => merge_generic(b, i),
156 (MeteoraDlmmCreatePosition(b), MeteoraDlmmCreatePosition(i)) => {
157 merge_dlmm_create_position(b, i)
158 }
159 (MeteoraDlmmClosePosition(b), MeteoraDlmmClosePosition(i)) => {
160 merge_dlmm_close_position(b, i)
161 }
162 (MeteoraDlmmClaimFee(b), MeteoraDlmmClaimFee(i)) => merge_generic(b, i),
163
164 (RaydiumLaunchlabTrade(b), RaydiumLaunchlabTrade(i)) => merge_generic(b, i),
166 (RaydiumLaunchlabPoolCreate(b), RaydiumLaunchlabPoolCreate(i)) => merge_generic(b, i),
167 (RaydiumLaunchlabMigrateAmm(b), RaydiumLaunchlabMigrateAmm(i)) => merge_generic(b, i),
168
169 (_, inner) => return Err(inner),
171 }
172
173 Ok(())
174}
175
176#[inline(always)]
183fn merge_generic<T>(base: &mut T, inner: T) {
184 *base = inner;
185}
186
187#[inline(always)]
188fn merge_dlmm_initialize_pool(
189 base: &mut MeteoraDlmmInitializePoolEvent,
190 inner: MeteoraDlmmInitializePoolEvent,
191) {
192 let creator = base.creator;
193 let active_bin_id = base.active_bin_id;
194 *base = inner;
195 base.creator = creator;
196 base.active_bin_id = active_bin_id;
197}
198
199#[inline(always)]
200fn merge_dlmm_create_position(
201 base: &mut MeteoraDlmmCreatePositionEvent,
202 inner: MeteoraDlmmCreatePositionEvent,
203) {
204 let lower_bin_id = base.lower_bin_id;
205 let width = base.width;
206 *base = inner;
207 base.lower_bin_id = lower_bin_id;
208 base.width = width;
209}
210
211#[inline(always)]
212fn merge_dlmm_close_position(
213 base: &mut MeteoraDlmmClosePositionEvent,
214 inner: MeteoraDlmmClosePositionEvent,
215) {
216 let pool = base.pool;
217 *base = inner;
218 base.pool = pool;
219}
220
221#[inline(always)]
226fn put_pk_if_set(to: &mut Pubkey, from: Pubkey) {
227 if from != Pubkey::default() {
228 *to = from;
229 }
230}
231
232#[inline(always)]
233fn put_pumpfun_quote_mint_if_set(to: &mut Pubkey, from: Pubkey) {
234 let from = normalize_pumpfun_quote_mint(from);
235 if from != Pubkey::default()
236 && (*to == Pubkey::default()
237 || is_pumpfun_solscan_sol_quote_mint(*to)
238 || !is_pumpfun_solscan_sol_quote_mint(from))
239 {
240 *to = from;
241 }
242}
243
244#[inline(always)]
245fn put_u64_if_nonzero(to: &mut u64, from: u64) {
246 if from != 0 {
247 *to = from;
248 }
249}
250
251#[inline(always)]
252fn put_i64_if_nonzero(to: &mut i64, from: i64) {
253 if from != 0 {
254 *to = from;
255 }
256}
257
258#[inline(always)]
268fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
269 let leg = inner.sol_amount != 0 || inner.token_amount != 0;
270
271 put_pk_if_set(&mut base.mint, inner.mint);
272 put_pk_if_set(&mut base.user, inner.user);
273 put_pk_if_set(&mut base.fee_recipient, inner.fee_recipient);
274 put_pk_if_set(&mut base.creator, inner.creator);
275
276 if leg {
277 base.sol_amount = inner.sol_amount;
278 base.token_amount = inner.token_amount;
279 base.is_buy = inner.is_buy;
280 base.timestamp = inner.timestamp;
281 base.virtual_sol_reserves = inner.virtual_sol_reserves;
282 base.virtual_token_reserves = inner.virtual_token_reserves;
283 base.real_sol_reserves = inner.real_sol_reserves;
284 base.real_token_reserves = inner.real_token_reserves;
285 base.fee_basis_points = inner.fee_basis_points;
286 base.fee = inner.fee;
287 base.creator_fee_basis_points = inner.creator_fee_basis_points;
288 base.creator_fee = inner.creator_fee;
289 base.track_volume |= inner.track_volume;
290 base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
291 base.total_claimed_tokens = inner.total_claimed_tokens;
292 base.current_sol_volume = inner.current_sol_volume;
293 base.last_update_timestamp = inner.last_update_timestamp;
294 if !inner.ix_name.is_empty() {
295 base.ix_name = inner.ix_name;
296 }
297 base.mayhem_mode |= inner.mayhem_mode;
298 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
299 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
300 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
301 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
302 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
303 base.shareholders = inner.shareholders;
304 }
305 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
306 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
307 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
308 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
309 base.is_cashback_coin |= inner.is_cashback_coin;
310 } else {
311 put_u64_if_nonzero(&mut base.fee, inner.fee);
312 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
313 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
314 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
315 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
316 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
317 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
318 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
319 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
320 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
321 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
322 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
323 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
324 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
325 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
326 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
327 base.shareholders = inner.shareholders;
328 }
329 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
330 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
331 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
332 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
333 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
334 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
335 if !inner.ix_name.is_empty() {
336 base.ix_name = inner.ix_name;
337 }
338 base.track_volume |= inner.track_volume;
339 base.mayhem_mode |= inner.mayhem_mode;
340 base.is_cashback_coin |= inner.is_cashback_coin;
341 }
342 put_u64_if_nonzero(&mut base.amount, inner.amount);
343 put_u64_if_nonzero(&mut base.max_sol_cost, inner.max_sol_cost);
344 put_u64_if_nonzero(&mut base.min_sol_output, inner.min_sol_output);
345 put_u64_if_nonzero(&mut base.spendable_sol_in, inner.spendable_sol_in);
346 put_u64_if_nonzero(&mut base.spendable_quote_in, inner.spendable_quote_in);
347 put_u64_if_nonzero(&mut base.min_tokens_out, inner.min_tokens_out);
348 put_pk_if_set(&mut base.global, inner.global);
349 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
350 put_pk_if_set(&mut base.bonding_curve_v2, inner.bonding_curve_v2);
351 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
352 put_pk_if_set(&mut base.associated_user, inner.associated_user);
353 put_pk_if_set(&mut base.system_program, inner.system_program);
354 put_pk_if_set(&mut base.token_program, inner.token_program);
355 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
356 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
357 put_pk_if_set(&mut base.creator_vault, inner.creator_vault);
358 put_pk_if_set(&mut base.associated_quote_fee_recipient, inner.associated_quote_fee_recipient);
359 put_pk_if_set(&mut base.buyback_fee_recipient, inner.buyback_fee_recipient);
360 put_pk_if_set(
361 &mut base.associated_quote_buyback_fee_recipient,
362 inner.associated_quote_buyback_fee_recipient,
363 );
364 put_pk_if_set(&mut base.associated_quote_bonding_curve, inner.associated_quote_bonding_curve);
365 put_pk_if_set(&mut base.associated_quote_user, inner.associated_quote_user);
366 put_pk_if_set(&mut base.associated_creator_vault, inner.associated_creator_vault);
367 put_pk_if_set(&mut base.sharing_config, inner.sharing_config);
368 put_pk_if_set(&mut base.event_authority, inner.event_authority);
369 put_pk_if_set(&mut base.program, inner.program);
370 put_pk_if_set(&mut base.global_volume_accumulator, inner.global_volume_accumulator);
371 put_pk_if_set(&mut base.user_volume_accumulator, inner.user_volume_accumulator);
372 put_pk_if_set(
373 &mut base.associated_user_volume_accumulator,
374 inner.associated_user_volume_accumulator,
375 );
376 put_pk_if_set(&mut base.fee_config, inner.fee_config);
377 put_pk_if_set(&mut base.fee_program, inner.fee_program);
378 if base.account.is_none() {
379 base.account = inner.account;
380 }
381
382 base.is_created_buy |= inner.is_created_buy;
383 }
385
386#[inline(always)]
388fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
389 base.name = inner.name;
391 base.symbol = inner.symbol;
392 base.uri = inner.uri;
393 base.mint = inner.mint;
394 base.bonding_curve = inner.bonding_curve;
395 base.user = inner.user;
396 base.creator = inner.creator;
397 base.timestamp = inner.timestamp;
398 base.virtual_token_reserves = inner.virtual_token_reserves;
399 base.virtual_sol_reserves = inner.virtual_sol_reserves;
400 base.real_token_reserves = inner.real_token_reserves;
401 base.token_total_supply = inner.token_total_supply;
402 base.token_program = inner.token_program;
403 base.is_mayhem_mode = inner.is_mayhem_mode;
404 base.is_cashback_enabled = inner.is_cashback_enabled;
405 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
406 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
407 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
408 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
409}
410
411#[inline(always)]
413fn merge_pumpfun_create_v2(base: &mut PumpFunCreateV2TokenEvent, inner: PumpFunCreateV2TokenEvent) {
414 fill_str_if_empty(&mut base.name, &inner.name);
415 fill_str_if_empty(&mut base.symbol, &inner.symbol);
416 fill_str_if_empty(&mut base.uri, &inner.uri);
417 put_pk_if_set(&mut base.mint, inner.mint);
418 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
419 put_pk_if_set(&mut base.user, inner.user);
420 put_pk_if_set(&mut base.creator, inner.creator);
421 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
422 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
423 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
424 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
425 put_u64_if_nonzero(&mut base.token_total_supply, inner.token_total_supply);
426 put_pk_if_set(&mut base.token_program, inner.token_program);
427 base.is_mayhem_mode |= inner.is_mayhem_mode;
428 base.is_cashback_enabled |= inner.is_cashback_enabled;
429 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
430 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
431 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
432 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
433 put_pk_if_set(&mut base.mint_authority, inner.mint_authority);
434 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
435 put_pk_if_set(&mut base.global, inner.global);
436 put_pk_if_set(&mut base.system_program, inner.system_program);
437 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
438 put_pk_if_set(&mut base.mayhem_program_id, inner.mayhem_program_id);
439 put_pk_if_set(&mut base.global_params, inner.global_params);
440 put_pk_if_set(&mut base.sol_vault, inner.sol_vault);
441 put_pk_if_set(&mut base.mayhem_state, inner.mayhem_state);
442 put_pk_if_set(&mut base.mayhem_token_vault, inner.mayhem_token_vault);
443 put_pk_if_set(&mut base.event_authority, inner.event_authority);
444 put_pk_if_set(&mut base.program, inner.program);
445 put_pk_if_set(&mut base.observed_fee_recipient, inner.observed_fee_recipient);
446}
447
448#[inline(always)]
450fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
451 base.user = inner.user;
453 base.mint = inner.mint;
454 base.mint_amount = inner.mint_amount;
455 base.sol_amount = inner.sol_amount;
456 base.pool_migration_fee = inner.pool_migration_fee;
457 base.bonding_curve = inner.bonding_curve;
458 base.timestamp = inner.timestamp;
459 base.pool = inner.pool;
460}
461
462#[inline(always)]
463fn merge_pumpswap_buy(base: &mut PumpSwapBuyEvent, inner: PumpSwapBuyEvent) {
464 let ix = std::mem::take(base);
465 *base = inner;
466 merge_pumpswap_buy_log_preferred(base, ix);
467}
468
469#[inline(always)]
470fn merge_pumpswap_sell(base: &mut PumpSwapSellEvent, inner: PumpSwapSellEvent) {
471 let ix = std::mem::take(base);
472 *base = inner;
473 merge_pumpswap_sell_log_preferred(base, ix);
474}
475
476#[inline(always)]
487pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
488 if base.metadata().signature != inner.metadata().signature {
490 return false;
491 }
492
493 match (base, inner) {
495 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
497 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
498 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
499 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
500 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
501 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
502 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
503 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
504 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
505 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
506
507 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
509 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
510
511 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
513
514 _ => false,
516 }
517}
518
519#[inline(always)]
524fn fill_pk(to: &mut Pubkey, from: Pubkey) {
525 if *to == Pubkey::default() && from != Pubkey::default() {
526 *to = from;
527 }
528}
529
530#[inline(always)]
531fn fill_pumpfun_quote_mint(to: &mut Pubkey, from: Pubkey) {
532 let from = normalize_pumpfun_quote_mint(from);
533 if (*to == Pubkey::default() || is_pumpfun_solscan_sol_quote_mint(*to))
534 && from != Pubkey::default()
535 {
536 *to = from;
537 }
538}
539
540#[inline(always)]
541fn fill_str_if_empty(to: &mut String, from: &str) {
542 if to.is_empty() && !from.is_empty() {
543 to.push_str(from);
544 }
545}
546
547#[inline]
550fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
551 fill_pk(&mut log.global, ix.global);
552 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
553 fill_pk(&mut log.bonding_curve_v2, ix.bonding_curve_v2);
554 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
555 fill_pk(&mut log.associated_user, ix.associated_user);
556 fill_pk(&mut log.system_program, ix.system_program);
557 fill_pk(&mut log.token_program, ix.token_program);
558 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
559 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
560 fill_pk(&mut log.creator_vault, ix.creator_vault);
561 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
562 fill_pk(&mut log.creator, ix.creator);
563 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
564 fill_pk(&mut log.associated_quote_fee_recipient, ix.associated_quote_fee_recipient);
565 fill_pk(&mut log.buyback_fee_recipient, ix.buyback_fee_recipient);
566 fill_pk(
567 &mut log.associated_quote_buyback_fee_recipient,
568 ix.associated_quote_buyback_fee_recipient,
569 );
570 fill_pk(&mut log.associated_quote_bonding_curve, ix.associated_quote_bonding_curve);
571 fill_pk(&mut log.associated_quote_user, ix.associated_quote_user);
572 fill_pk(&mut log.associated_creator_vault, ix.associated_creator_vault);
573 fill_pk(&mut log.sharing_config, ix.sharing_config);
574 fill_pk(&mut log.event_authority, ix.event_authority);
575 fill_pk(&mut log.program, ix.program);
576 fill_pk(&mut log.global_volume_accumulator, ix.global_volume_accumulator);
577 fill_pk(&mut log.user_volume_accumulator, ix.user_volume_accumulator);
578 fill_pk(&mut log.associated_user_volume_accumulator, ix.associated_user_volume_accumulator);
579 fill_pk(&mut log.fee_config, ix.fee_config);
580 fill_pk(&mut log.fee_program, ix.fee_program);
581 if log.account.is_none() {
582 log.account = ix.account;
583 }
584 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
585 log.ix_name = ix.ix_name;
586 }
587 put_u64_if_nonzero(&mut log.amount, ix.amount);
588 put_u64_if_nonzero(&mut log.max_sol_cost, ix.max_sol_cost);
589 put_u64_if_nonzero(&mut log.min_sol_output, ix.min_sol_output);
590 put_u64_if_nonzero(&mut log.spendable_sol_in, ix.spendable_sol_in);
591 put_u64_if_nonzero(&mut log.spendable_quote_in, ix.spendable_quote_in);
592 put_u64_if_nonzero(&mut log.min_tokens_out, ix.min_tokens_out);
593 put_u64_if_nonzero(&mut log.quote_amount, ix.quote_amount);
594 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
595 put_u64_if_nonzero(&mut log.real_quote_reserves, ix.real_quote_reserves);
596 if !log.is_created_buy && ix.is_created_buy {
597 log.is_created_buy = true;
598 }
599}
600
601#[inline]
602fn merge_pumpfun_create_log_preferred(
603 log: &mut PumpFunCreateTokenEvent,
604 ix: PumpFunCreateTokenEvent,
605) {
606 fill_str_if_empty(&mut log.name, &ix.name);
607 fill_str_if_empty(&mut log.symbol, &ix.symbol);
608 fill_str_if_empty(&mut log.uri, &ix.uri);
609 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
610 fill_pk(&mut log.user, ix.user);
611 fill_pk(&mut log.creator, ix.creator);
612 fill_pk(&mut log.token_program, ix.token_program);
613 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
614 fill_pk(&mut log.quote_vault, ix.quote_vault);
615 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
616 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
617 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
618 log.ix_name = ix.ix_name;
619 }
620 log.is_mayhem_mode |= ix.is_mayhem_mode;
621 log.is_cashback_enabled |= ix.is_cashback_enabled;
622}
623
624#[inline]
625fn merge_pumpfun_create_v2_into_create_log_preferred(
626 log: &mut PumpFunCreateTokenEvent,
627 ix: PumpFunCreateV2TokenEvent,
628) {
629 fill_str_if_empty(&mut log.name, &ix.name);
630 fill_str_if_empty(&mut log.symbol, &ix.symbol);
631 fill_str_if_empty(&mut log.uri, &ix.uri);
632 fill_pk(&mut log.mint, ix.mint);
633 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
634 fill_pk(&mut log.user, ix.user);
635 fill_pk(&mut log.creator, ix.creator);
636 if log.timestamp == 0 && ix.timestamp != 0 {
637 log.timestamp = ix.timestamp;
638 }
639 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
640 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
641 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
642 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
643 fill_pk(&mut log.token_program, ix.token_program);
644 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
645 fill_pk(&mut log.quote_vault, ix.quote_vault);
646 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
647 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
648 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
649 log.ix_name = ix.ix_name;
650 }
651 log.is_mayhem_mode |= ix.is_mayhem_mode;
652 log.is_cashback_enabled |= ix.is_cashback_enabled;
653}
654
655#[inline]
656fn merge_pumpfun_create_into_create_v2_log_preferred(
657 log: &mut PumpFunCreateV2TokenEvent,
658 ix: PumpFunCreateTokenEvent,
659) {
660 fill_str_if_empty(&mut log.name, &ix.name);
661 fill_str_if_empty(&mut log.symbol, &ix.symbol);
662 fill_str_if_empty(&mut log.uri, &ix.uri);
663 fill_pk(&mut log.mint, ix.mint);
664 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
665 fill_pk(&mut log.user, ix.user);
666 fill_pk(&mut log.creator, ix.creator);
667 if log.timestamp == 0 && ix.timestamp != 0 {
668 log.timestamp = ix.timestamp;
669 }
670 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
671 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
672 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
673 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
674 fill_pk(&mut log.token_program, ix.token_program);
675 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
676 fill_pk(&mut log.quote_vault, ix.quote_vault);
677 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
678 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
679 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
680 log.ix_name = ix.ix_name;
681 }
682 log.is_mayhem_mode |= ix.is_mayhem_mode;
683 log.is_cashback_enabled |= ix.is_cashback_enabled;
684}
685
686#[inline]
687fn merge_pumpfun_create_v2_log_preferred(
688 log: &mut PumpFunCreateV2TokenEvent,
689 ix: PumpFunCreateV2TokenEvent,
690) {
691 fill_str_if_empty(&mut log.name, &ix.name);
692 fill_str_if_empty(&mut log.symbol, &ix.symbol);
693 fill_str_if_empty(&mut log.uri, &ix.uri);
694 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
695 fill_pk(&mut log.user, ix.user);
696 fill_pk(&mut log.creator, ix.creator);
697 fill_pk(&mut log.token_program, ix.token_program);
698 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
699 fill_pk(&mut log.quote_vault, ix.quote_vault);
700 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
701 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
702 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
703 log.ix_name = ix.ix_name;
704 }
705 fill_pk(&mut log.mint_authority, ix.mint_authority);
706 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
707 fill_pk(&mut log.global, ix.global);
708 fill_pk(&mut log.system_program, ix.system_program);
709 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
710 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
711 fill_pk(&mut log.global_params, ix.global_params);
712 fill_pk(&mut log.sol_vault, ix.sol_vault);
713 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
714 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
715 fill_pk(&mut log.event_authority, ix.event_authority);
716 fill_pk(&mut log.program, ix.program);
717 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
718}
719
720#[inline]
721fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
722 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
723 fill_pk(&mut log.pool, ix.pool);
724 fill_pk(&mut log.user, ix.user);
725}
726
727#[inline]
728fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
729 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
730 log.ix_name = ix.ix_name;
731 }
732}
733
734#[inline]
735fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
736 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
737 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
738 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
739 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
740 fill_pk(&mut log.coin_creator, ix.coin_creator);
741 fill_pk(&mut log.base_mint, ix.base_mint);
742 fill_pk(&mut log.quote_mint, ix.quote_mint);
743 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
744 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
745 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
746 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
747 fill_pk(&mut log.base_token_program, ix.base_token_program);
748 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
749 fill_pk(&mut log.pool_v2, ix.pool_v2);
750 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
751 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
752 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
753 log.ix_name = ix.ix_name;
754 }
755}
756
757#[inline]
758fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
759 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
760 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
761 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
762 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
763 fill_pk(&mut log.coin_creator, ix.coin_creator);
764 fill_pk(&mut log.base_mint, ix.base_mint);
765 fill_pk(&mut log.quote_mint, ix.quote_mint);
766 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
767 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
768 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
769 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
770 fill_pk(&mut log.base_token_program, ix.base_token_program);
771 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
772 fill_pk(&mut log.pool_v2, ix.pool_v2);
773 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
774 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
775}
776
777#[inline]
778fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
779 fill_pk(&mut log.token_account_0, ix.token_account_0);
780 fill_pk(&mut log.token_account_1, ix.token_account_1);
781 fill_pk(&mut log.sender, ix.sender);
782}
783
784#[inline]
785fn merge_raydium_amm_v4_swap_log_preferred(
786 log: &mut RaydiumAmmV4SwapEvent,
787 ix: RaydiumAmmV4SwapEvent,
788) {
789 fill_pk(&mut log.token_program, ix.token_program);
790 fill_pk(&mut log.amm_authority, ix.amm_authority);
791 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
792 if let Some(ref o) = ix.amm_target_orders {
793 if log.amm_target_orders.is_none() {
794 log.amm_target_orders = Some(*o);
795 }
796 }
797 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
798 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
799 fill_pk(&mut log.serum_program, ix.serum_program);
800 fill_pk(&mut log.serum_market, ix.serum_market);
801 fill_pk(&mut log.serum_bids, ix.serum_bids);
802 fill_pk(&mut log.serum_asks, ix.serum_asks);
803 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
804 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
805 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
806 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
807 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
808 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
809 fill_pk(&mut log.user_source_owner, ix.user_source_owner);
810 fill_pk(&mut log.amm, ix.amm);
811}
812
813#[inline]
814fn merge_pumpswap_create_pool_log_preferred(
815 log: &mut PumpSwapCreatePoolEvent,
816 ix: PumpSwapCreatePoolEvent,
817) {
818 fill_pk(&mut log.creator, ix.creator);
819 fill_pk(&mut log.pool, ix.pool);
820 fill_pk(&mut log.lp_mint, ix.lp_mint);
821 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
822 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
823 fill_pk(&mut log.coin_creator, ix.coin_creator);
824 log.is_mayhem_mode |= ix.is_mayhem_mode;
825 log.is_cashback_coin |= ix.is_cashback_coin;
826}
827
828#[inline]
829fn merge_pumpswap_liquidity_added_log_preferred(
830 log: &mut PumpSwapLiquidityAdded,
831 ix: PumpSwapLiquidityAdded,
832) {
833 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
834 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
835 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
836}
837
838#[inline]
839fn merge_pumpswap_liquidity_removed_log_preferred(
840 log: &mut PumpSwapLiquidityRemoved,
841 ix: PumpSwapLiquidityRemoved,
842) {
843 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
844 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
845 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
846}
847
848#[inline]
849fn merge_raydium_launchlab_pool_create_log_preferred(
850 log: &mut RaydiumLaunchlabPoolCreateEvent,
851 ix: RaydiumLaunchlabPoolCreateEvent,
852) {
853 fill_pk(&mut log.creator, ix.creator);
854 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
855 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
856 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
857}
858
859#[inline]
860fn merge_raydium_launchlab_migrate_amm_log_preferred(
861 log: &mut RaydiumLaunchlabMigrateAmmEvent,
862 ix: RaydiumLaunchlabMigrateAmmEvent,
863) {
864 fill_pk(&mut log.old_pool, ix.old_pool);
865 fill_pk(&mut log.new_pool, ix.new_pool);
866 fill_pk(&mut log.user, ix.user);
867}
868
869#[inline]
871fn merge_raydium_launchlab_trade_log_preferred(
872 _log: &mut RaydiumLaunchlabTradeEvent,
873 _ix: RaydiumLaunchlabTradeEvent,
874) {
875}
876
877#[inline]
878fn merge_meteora_dlmm_swap_log_preferred(
879 _log: &mut MeteoraDlmmSwapEvent,
880 _ix: MeteoraDlmmSwapEvent,
881) {
882}
883
884pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
890 use DexEvent::*;
891 match log {
892 PumpFunTrade(l) => {
893 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
894 merge_pumpfun_trade_log_preferred(l, i);
895 }
896 }
897 PumpFunBuy(l) => {
898 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
899 merge_pumpfun_trade_log_preferred(l, i);
900 }
901 }
902 PumpFunSell(l) => {
903 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
904 merge_pumpfun_trade_log_preferred(l, i);
905 }
906 }
907 PumpFunBuyExactSolIn(l) => {
908 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
909 merge_pumpfun_trade_log_preferred(l, i);
910 }
911 }
912 PumpFunCreate(l) => match ix {
913 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_log_preferred(l, i),
914 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_into_create_log_preferred(l, i),
915 _ => {}
916 },
917 PumpFunCreateV2(l) => match ix {
918 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_into_create_v2_log_preferred(l, i),
919 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_log_preferred(l, i),
920 _ => {}
921 },
922 PumpFunMigrate(l) => {
923 if let DexEvent::PumpFunMigrate(i) = ix {
924 merge_pumpfun_migrate_log_preferred(l, i);
925 }
926 }
927 PumpSwapTrade(l) => {
928 if let PumpSwapTrade(i) = ix {
929 merge_pumpswap_trade_log_preferred(l, i);
930 }
931 }
932 PumpSwapBuy(l) => {
933 if let PumpSwapBuy(i) = ix {
934 merge_pumpswap_buy_log_preferred(l, i);
935 }
936 }
937 PumpSwapSell(l) => {
938 if let PumpSwapSell(i) = ix {
939 merge_pumpswap_sell_log_preferred(l, i);
940 }
941 }
942 RaydiumClmmSwap(l) => {
943 if let RaydiumClmmSwap(i) = ix {
944 merge_raydium_clmm_swap_log_preferred(l, i);
945 }
946 }
947 RaydiumAmmV4Swap(l) => {
948 if let RaydiumAmmV4Swap(i) = ix {
949 merge_raydium_amm_v4_swap_log_preferred(l, i);
950 }
951 }
952 RaydiumLaunchlabTrade(l) => {
953 if let RaydiumLaunchlabTrade(i) = ix {
954 merge_raydium_launchlab_trade_log_preferred(l, i);
955 }
956 }
957 RaydiumLaunchlabPoolCreate(l) => {
958 if let RaydiumLaunchlabPoolCreate(i) = ix {
959 merge_raydium_launchlab_pool_create_log_preferred(l, i);
960 }
961 }
962 RaydiumLaunchlabMigrateAmm(l) => {
963 if let RaydiumLaunchlabMigrateAmm(i) = ix {
964 merge_raydium_launchlab_migrate_amm_log_preferred(l, i);
965 }
966 }
967 PumpSwapCreatePool(l) => {
968 if let PumpSwapCreatePool(i) = ix {
969 merge_pumpswap_create_pool_log_preferred(l, i);
970 }
971 }
972 PumpSwapLiquidityAdded(l) => {
973 if let PumpSwapLiquidityAdded(i) = ix {
974 merge_pumpswap_liquidity_added_log_preferred(l, i);
975 }
976 }
977 PumpSwapLiquidityRemoved(l) => {
978 if let PumpSwapLiquidityRemoved(i) = ix {
979 merge_pumpswap_liquidity_removed_log_preferred(l, i);
980 }
981 }
982 MeteoraDlmmSwap(l) => {
983 if let MeteoraDlmmSwap(i) = ix {
984 merge_meteora_dlmm_swap_log_preferred(l, i);
985 }
986 }
987 _ => {}
988 }
989}
990
991#[inline]
992fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
993 match ix {
994 DexEvent::PumpFunTrade(t)
995 | DexEvent::PumpFunBuy(t)
996 | DexEvent::PumpFunSell(t)
997 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
998 _ => None,
999 }
1000}
1001
1002#[cfg(test)]
1003mod tests {
1004 use super::*;
1005 use solana_sdk::{pubkey::Pubkey, signature::Signature};
1006
1007 #[test]
1008 fn test_merge_pumpfun_trade() {
1009 let metadata = EventMetadata {
1010 signature: Signature::default(),
1011 slot: 100,
1012 tx_index: 1,
1013 block_time_us: 1000,
1014 grpc_recv_us: 2000,
1015 recent_blockhash: None,
1016 };
1017
1018 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1020 metadata: metadata.clone(),
1021 bonding_curve: Pubkey::new_unique(),
1022 associated_bonding_curve: Pubkey::new_unique(),
1023 ..Default::default()
1024 });
1025
1026 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1028 metadata: metadata.clone(),
1029 mint: Pubkey::new_unique(),
1030 sol_amount: 1000,
1031 token_amount: 2000,
1032 is_buy: true,
1033 user: Pubkey::new_unique(),
1034 ..Default::default()
1035 });
1036
1037 merge_events(&mut base, inner);
1039
1040 if let DexEvent::PumpFunTrade(trade) = base {
1042 assert_eq!(trade.sol_amount, 1000);
1043 assert_eq!(trade.token_amount, 2000);
1044 assert!(trade.is_buy);
1045 assert_ne!(trade.bonding_curve, Pubkey::default());
1047 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
1048 } else {
1049 panic!("Expected PumpFunTrade event");
1050 }
1051 }
1052
1053 #[test]
1054 fn merge_preserves_instruction_context_when_log_tail_is_absent() {
1055 let metadata = EventMetadata {
1056 signature: Signature::default(),
1057 slot: 100,
1058 tx_index: 1,
1059 block_time_us: 1000,
1060 grpc_recv_us: 2000,
1061 recent_blockhash: None,
1062 };
1063 let quote_mint = Pubkey::new_unique();
1064 let associated_quote_user = Pubkey::new_unique();
1065
1066 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1067 metadata: metadata.clone(),
1068 ix_name: "buy_exact_quote_in".to_string(),
1069 quote_mint,
1070 spendable_quote_in: 1_000,
1071 min_tokens_out: 2_000,
1072 associated_quote_user,
1073 ..Default::default()
1074 });
1075
1076 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1077 metadata,
1078 sol_amount: 1_000,
1079 token_amount: 2_000,
1080 is_buy: true,
1081 ..Default::default()
1082 });
1083
1084 merge_events(&mut base, inner);
1085
1086 if let DexEvent::PumpFunTrade(t) = base {
1087 assert_eq!(t.sol_amount, 1_000);
1088 assert_eq!(t.token_amount, 2_000);
1089 assert_eq!(t.ix_name, "buy_exact_quote_in");
1090 assert_eq!(t.quote_mint, quote_mint);
1091 assert_eq!(t.spendable_quote_in, 1_000);
1092 assert_eq!(t.min_tokens_out, 2_000);
1093 assert_eq!(t.associated_quote_user, associated_quote_user);
1094 } else {
1095 panic!("Expected PumpFunTrade event");
1096 }
1097 }
1098
1099 #[test]
1100 fn merge_replaces_sol_quote_sentinel_with_real_quote_mint() {
1101 let quote_mint = Pubkey::new_unique();
1102 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1103 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1104 ..Default::default()
1105 });
1106
1107 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent { quote_mint, ..Default::default() });
1108
1109 merge_events(&mut base, inner);
1110
1111 if let DexEvent::PumpFunTrade(t) = base {
1112 assert_eq!(t.quote_mint, quote_mint);
1113 } else {
1114 panic!("Expected PumpFunTrade event");
1115 }
1116 }
1117
1118 #[test]
1119 fn merge_replaces_sol_quote_sentinel_with_wsol_mint() {
1120 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1121 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1122 ..Default::default()
1123 });
1124
1125 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1126 quote_mint: PUMPFUN_WSOL_QUOTE_MINT,
1127 ..Default::default()
1128 });
1129
1130 merge_events(&mut base, inner);
1131
1132 if let DexEvent::PumpFunTrade(t) = base {
1133 assert_eq!(t.quote_mint, PUMPFUN_WSOL_QUOTE_MINT);
1134 } else {
1135 panic!("Expected PumpFunTrade event");
1136 }
1137 }
1138
1139 #[test]
1140 fn test_can_merge() {
1141 let metadata = EventMetadata {
1142 signature: Signature::default(),
1143 slot: 100,
1144 tx_index: 1,
1145 block_time_us: 1000,
1146 grpc_recv_us: 2000,
1147 recent_blockhash: None,
1148 };
1149
1150 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1151 metadata: metadata.clone(),
1152 ..Default::default()
1153 });
1154
1155 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1156 metadata: metadata.clone(),
1157 ..Default::default()
1158 });
1159
1160 assert!(can_merge(&base, &inner));
1162
1163 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1165 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
1166 ..Default::default()
1167 });
1168
1169 assert!(!can_merge(&base, &different_sig));
1170 }
1171
1172 #[test]
1173 fn dlmm_position_event_keeps_instruction_only_fields() {
1174 let pool = Pubkey::new_unique();
1175 let position = Pubkey::new_unique();
1176 let owner = Pubkey::new_unique();
1177 let mut base = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1178 metadata: EventMetadata::default(),
1179 pool,
1180 position,
1181 owner,
1182 lower_bin_id: -42,
1183 width: 70,
1184 });
1185 let inner = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1186 metadata: EventMetadata::default(),
1187 pool,
1188 position,
1189 owner,
1190 lower_bin_id: 0,
1191 width: 0,
1192 });
1193
1194 assert!(try_merge_events(&mut base, inner).is_ok());
1195 let DexEvent::MeteoraDlmmCreatePosition(event) = base else { panic!("position") };
1196 assert_eq!(event.lower_bin_id, -42);
1197 assert_eq!(event.width, 70);
1198 }
1199
1200 #[test]
1201 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
1202 let metadata = EventMetadata {
1203 signature: Signature::default(),
1204 slot: 1,
1205 tx_index: 0,
1206 block_time_us: 0,
1207 grpc_recv_us: 0,
1208 recent_blockhash: None,
1209 };
1210 let fr = Pubkey::new_unique();
1211 let log_t =
1212 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
1213 let mut ix_t = log_t.clone();
1214 ix_t.fee_recipient = fr;
1215 ix_t.sol_amount = 777;
1216 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1217 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1218 match log_ev {
1219 DexEvent::PumpFunTrade(t) => {
1220 assert_eq!(t.fee_recipient, fr);
1221 assert_eq!(t.sol_amount, 50);
1222 }
1223 _ => panic!("expected trade"),
1224 }
1225 }
1226
1227 #[test]
1228 fn grpc_merge_keeps_log_trade_fields() {
1229 let metadata = EventMetadata {
1230 signature: Signature::default(),
1231 slot: 1,
1232 tx_index: 0,
1233 block_time_us: 0,
1234 grpc_recv_us: 0,
1235 recent_blockhash: None,
1236 };
1237 let log_t = PumpFunTradeEvent {
1238 metadata: metadata.clone(),
1239 mayhem_mode: true,
1240 sol_amount: 100,
1241 ..Default::default()
1242 };
1243 let mut ix_t = log_t.clone();
1244 ix_t.mayhem_mode = false;
1245 ix_t.sol_amount = 999;
1246
1247 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1248 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1249 match log_ev {
1250 DexEvent::PumpFunTrade(t) => {
1251 assert!(t.mayhem_mode);
1252 assert_eq!(t.sol_amount, 100);
1253 }
1254 _ => panic!("variant preserved"),
1255 }
1256 }
1257}