1use solana_sdk::pubkey::Pubkey;
13
14use crate::core::events::*;
15
16#[inline(always)]
28pub fn merge_events(base: &mut DexEvent, inner: DexEvent) {
29 use DexEvent::*;
30
31 match (base, inner) {
32 (PumpFunTrade(b), PumpFunTrade(i))
34 | (PumpFunTrade(b), PumpFunBuy(i))
35 | (PumpFunTrade(b), PumpFunSell(i))
36 | (PumpFunTrade(b), PumpFunBuyExactSolIn(i))
37 | (PumpFunBuy(b), PumpFunTrade(i))
38 | (PumpFunBuy(b), PumpFunBuy(i))
39 | (PumpFunSell(b), PumpFunTrade(i))
40 | (PumpFunSell(b), PumpFunSell(i))
41 | (PumpFunBuyExactSolIn(b), PumpFunTrade(i))
42 | (PumpFunBuyExactSolIn(b), PumpFunBuyExactSolIn(i)) => merge_pumpfun_trade(b, i),
43
44 (PumpFunCreate(b), PumpFunCreate(i)) => merge_pumpfun_create(b, i),
45 (PumpFunCreateV2(b), PumpFunCreateV2(i)) => merge_generic(b, i),
46 (PumpFunMigrate(b), PumpFunMigrate(i)) => merge_pumpfun_migrate(b, i),
47
48 (PumpSwapBuy(b), PumpSwapBuy(i)) => merge_generic(b, i),
50 (PumpSwapSell(b), PumpSwapSell(i)) => merge_generic(b, i),
51 (PumpSwapCreatePool(b), PumpSwapCreatePool(i)) => merge_generic(b, i),
52 (PumpSwapLiquidityAdded(b), PumpSwapLiquidityAdded(i)) => merge_generic(b, i),
53 (PumpSwapLiquidityRemoved(b), PumpSwapLiquidityRemoved(i)) => merge_generic(b, i),
54
55 (RaydiumClmmSwap(b), RaydiumClmmSwap(i)) => merge_generic(b, i),
57 (RaydiumClmmIncreaseLiquidity(b), RaydiumClmmIncreaseLiquidity(i)) => merge_generic(b, i),
58 (RaydiumClmmDecreaseLiquidity(b), RaydiumClmmDecreaseLiquidity(i)) => merge_generic(b, i),
59 (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
60 (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
61
62 (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_generic(b, i),
64 (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
65 (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
66
67 (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => merge_generic(b, i),
69 (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
70 (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
71
72 (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => merge_generic(b, i),
74 (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => {
75 merge_generic(b, i)
76 }
77 (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => {
78 merge_generic(b, i)
79 }
80
81 (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => merge_generic(b, i),
83 (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => merge_generic(b, i),
84 (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => merge_generic(b, i),
85
86 (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
88 (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
89 (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
90 (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
91 (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
92
93 (BonkTrade(b), BonkTrade(i)) => merge_generic(b, i),
95
96 _ => {}
98 }
99}
100
101#[inline(always)]
108fn merge_generic<T>(base: &mut T, inner: T) {
109 *base = inner;
110}
111
112#[inline(always)]
117fn put_pk_if_set(to: &mut Pubkey, from: Pubkey) {
118 if from != Pubkey::default() {
119 *to = from;
120 }
121}
122
123#[inline(always)]
124fn put_u64_if_nonzero(to: &mut u64, from: u64) {
125 if from != 0 {
126 *to = from;
127 }
128}
129
130#[inline(always)]
131fn put_i64_if_nonzero(to: &mut i64, from: i64) {
132 if from != 0 {
133 *to = from;
134 }
135}
136
137#[inline(always)]
147fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
148 let leg = inner.sol_amount != 0 || inner.token_amount != 0;
149
150 put_pk_if_set(&mut base.mint, inner.mint);
151 put_pk_if_set(&mut base.user, inner.user);
152 put_pk_if_set(&mut base.fee_recipient, inner.fee_recipient);
153 put_pk_if_set(&mut base.creator, inner.creator);
154
155 if leg {
156 base.sol_amount = inner.sol_amount;
157 base.token_amount = inner.token_amount;
158 base.is_buy = inner.is_buy;
159 base.timestamp = inner.timestamp;
160 base.virtual_sol_reserves = inner.virtual_sol_reserves;
161 base.virtual_token_reserves = inner.virtual_token_reserves;
162 base.real_sol_reserves = inner.real_sol_reserves;
163 base.real_token_reserves = inner.real_token_reserves;
164 base.fee_basis_points = inner.fee_basis_points;
165 base.fee = inner.fee;
166 base.creator_fee_basis_points = inner.creator_fee_basis_points;
167 base.creator_fee = inner.creator_fee;
168 base.track_volume = inner.track_volume;
169 base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
170 base.total_claimed_tokens = inner.total_claimed_tokens;
171 base.current_sol_volume = inner.current_sol_volume;
172 base.last_update_timestamp = inner.last_update_timestamp;
173 base.ix_name = inner.ix_name;
174 base.mayhem_mode = inner.mayhem_mode;
175 base.cashback_fee_basis_points = inner.cashback_fee_basis_points;
176 base.cashback = inner.cashback;
177 base.is_cashback_coin = inner.is_cashback_coin;
178 } else {
179 put_u64_if_nonzero(&mut base.fee, inner.fee);
180 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
181 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
182 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
183 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
184 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
185 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
186 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
187 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
188 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
189 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
190 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
191 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
192 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
193 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
194 if !inner.ix_name.is_empty() {
195 base.ix_name = inner.ix_name;
196 }
197 base.track_volume |= inner.track_volume;
198 base.mayhem_mode |= inner.mayhem_mode;
199 base.is_cashback_coin |= inner.is_cashback_coin;
200 }
201
202 base.is_created_buy |= inner.is_created_buy;
203 }
205
206#[inline(always)]
208fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
209 base.name = inner.name;
211 base.symbol = inner.symbol;
212 base.uri = inner.uri;
213 base.mint = inner.mint;
214 base.bonding_curve = inner.bonding_curve;
215 base.user = inner.user;
216 base.creator = inner.creator;
217 base.timestamp = inner.timestamp;
218 base.virtual_token_reserves = inner.virtual_token_reserves;
219 base.virtual_sol_reserves = inner.virtual_sol_reserves;
220 base.real_token_reserves = inner.real_token_reserves;
221 base.token_total_supply = inner.token_total_supply;
222 base.token_program = inner.token_program;
223 base.is_mayhem_mode = inner.is_mayhem_mode;
224}
225
226#[inline(always)]
228fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
229 base.user = inner.user;
231 base.mint = inner.mint;
232 base.mint_amount = inner.mint_amount;
233 base.sol_amount = inner.sol_amount;
234 base.pool_migration_fee = inner.pool_migration_fee;
235 base.bonding_curve = inner.bonding_curve;
236 base.timestamp = inner.timestamp;
237 base.pool = inner.pool;
238}
239
240#[inline(always)]
251pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
252 if base.metadata().signature != inner.metadata().signature {
254 return false;
255 }
256
257 match (base, inner) {
259 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
261 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
262 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
263 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
264 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
265 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
266 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
267 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
268 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
269 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
270
271 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
273 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
274
275 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
277
278 _ => false,
280 }
281}
282
283#[inline(always)]
288fn fill_pk(to: &mut Pubkey, from: Pubkey) {
289 if *to == Pubkey::default() && from != Pubkey::default() {
290 *to = from;
291 }
292}
293
294#[inline(always)]
295fn fill_str_if_empty(to: &mut String, from: &str) {
296 if to.is_empty() && !from.is_empty() {
297 to.push_str(from);
298 }
299}
300
301#[inline]
304fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
305 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
306 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
307 fill_pk(&mut log.token_program, ix.token_program);
308 fill_pk(&mut log.creator_vault, ix.creator_vault);
309 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
310 fill_pk(&mut log.creator, ix.creator);
311 if log.account.is_none() {
312 log.account = ix.account;
313 }
314 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
315 log.ix_name = ix.ix_name;
316 }
317 if !log.is_created_buy && ix.is_created_buy {
318 log.is_created_buy = true;
319 }
320}
321
322#[inline]
323fn merge_pumpfun_create_log_preferred(log: &mut PumpFunCreateTokenEvent, ix: PumpFunCreateTokenEvent) {
324 fill_str_if_empty(&mut log.name, &ix.name);
325 fill_str_if_empty(&mut log.symbol, &ix.symbol);
326 fill_str_if_empty(&mut log.uri, &ix.uri);
327 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
328 fill_pk(&mut log.user, ix.user);
329 fill_pk(&mut log.creator, ix.creator);
330 fill_pk(&mut log.token_program, ix.token_program);
331}
332
333#[inline]
334fn merge_pumpfun_create_v2_log_preferred(
335 log: &mut PumpFunCreateV2TokenEvent,
336 ix: PumpFunCreateV2TokenEvent,
337) {
338 fill_str_if_empty(&mut log.name, &ix.name);
339 fill_str_if_empty(&mut log.symbol, &ix.symbol);
340 fill_str_if_empty(&mut log.uri, &ix.uri);
341 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
342 fill_pk(&mut log.user, ix.user);
343 fill_pk(&mut log.creator, ix.creator);
344 fill_pk(&mut log.token_program, ix.token_program);
345 fill_pk(&mut log.mint_authority, ix.mint_authority);
346 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
347 fill_pk(&mut log.global, ix.global);
348 fill_pk(&mut log.system_program, ix.system_program);
349 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
350 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
351 fill_pk(&mut log.global_params, ix.global_params);
352 fill_pk(&mut log.sol_vault, ix.sol_vault);
353 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
354 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
355 fill_pk(&mut log.event_authority, ix.event_authority);
356 fill_pk(&mut log.program, ix.program);
357 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
358}
359
360#[inline]
361fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
362 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
363 fill_pk(&mut log.pool, ix.pool);
364 fill_pk(&mut log.user, ix.user);
365}
366
367#[inline]
368fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
369 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
370 log.ix_name = ix.ix_name;
371 }
372}
373
374#[inline]
375fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
376 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
377 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
378 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
379 fill_pk(
380 &mut log.protocol_fee_recipient_token_account,
381 ix.protocol_fee_recipient_token_account,
382 );
383 fill_pk(&mut log.coin_creator, ix.coin_creator);
384 fill_pk(&mut log.base_mint, ix.base_mint);
385 fill_pk(&mut log.quote_mint, ix.quote_mint);
386 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
387 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
388 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
389 fill_pk(
390 &mut log.coin_creator_vault_authority,
391 ix.coin_creator_vault_authority,
392 );
393 fill_pk(&mut log.base_token_program, ix.base_token_program);
394 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
395 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
396 log.ix_name = ix.ix_name;
397 }
398}
399
400#[inline]
401fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
402 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
403 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
404 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
405 fill_pk(
406 &mut log.protocol_fee_recipient_token_account,
407 ix.protocol_fee_recipient_token_account,
408 );
409 fill_pk(&mut log.coin_creator, ix.coin_creator);
410 fill_pk(&mut log.base_mint, ix.base_mint);
411 fill_pk(&mut log.quote_mint, ix.quote_mint);
412 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
413 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
414 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
415 fill_pk(
416 &mut log.coin_creator_vault_authority,
417 ix.coin_creator_vault_authority,
418 );
419 fill_pk(&mut log.base_token_program, ix.base_token_program);
420 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
421}
422
423#[inline]
424fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
425 fill_pk(&mut log.token_account_0, ix.token_account_0);
426 fill_pk(&mut log.token_account_1, ix.token_account_1);
427 fill_pk(&mut log.sender, ix.sender);
428}
429
430#[inline]
431fn merge_raydium_amm_v4_swap_log_preferred(log: &mut RaydiumAmmV4SwapEvent, ix: RaydiumAmmV4SwapEvent) {
432 fill_pk(&mut log.token_program, ix.token_program);
433 fill_pk(&mut log.amm_authority, ix.amm_authority);
434 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
435 if let Some(ref o) = ix.amm_target_orders {
436 if log.amm_target_orders.is_none() {
437 log.amm_target_orders = Some(*o);
438 }
439 }
440 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
441 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
442 fill_pk(&mut log.serum_program, ix.serum_program);
443 fill_pk(&mut log.serum_market, ix.serum_market);
444 fill_pk(&mut log.serum_bids, ix.serum_bids);
445 fill_pk(&mut log.serum_asks, ix.serum_asks);
446 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
447 fill_pk(
448 &mut log.serum_coin_vault_account,
449 ix.serum_coin_vault_account,
450 );
451 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
452 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
453 fill_pk(
454 &mut log.user_source_token_account,
455 ix.user_source_token_account,
456 );
457 fill_pk(
458 &mut log.user_destination_token_account,
459 ix.user_destination_token_account,
460 );
461}
462
463#[inline]
464fn merge_pumpswap_create_pool_log_preferred(
465 log: &mut PumpSwapCreatePoolEvent,
466 ix: PumpSwapCreatePoolEvent,
467) {
468 fill_pk(&mut log.creator, ix.creator);
469 fill_pk(&mut log.pool, ix.pool);
470 fill_pk(&mut log.lp_mint, ix.lp_mint);
471 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
472 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
473 fill_pk(&mut log.coin_creator, ix.coin_creator);
474}
475
476#[inline]
477fn merge_pumpswap_liquidity_added_log_preferred(
478 log: &mut PumpSwapLiquidityAdded,
479 ix: PumpSwapLiquidityAdded,
480) {
481 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
482 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
483 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
484}
485
486#[inline]
487fn merge_pumpswap_liquidity_removed_log_preferred(
488 log: &mut PumpSwapLiquidityRemoved,
489 ix: PumpSwapLiquidityRemoved,
490) {
491 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
492 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
493 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
494}
495
496#[inline]
497fn merge_bonk_pool_create_log_preferred(log: &mut BonkPoolCreateEvent, ix: BonkPoolCreateEvent) {
498 fill_pk(&mut log.creator, ix.creator);
499 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
500 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
501 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
502}
503
504#[inline]
505fn merge_bonk_migrate_amm_log_preferred(log: &mut BonkMigrateAmmEvent, ix: BonkMigrateAmmEvent) {
506 fill_pk(&mut log.old_pool, ix.old_pool);
507 fill_pk(&mut log.new_pool, ix.new_pool);
508 fill_pk(&mut log.user, ix.user);
509}
510
511#[inline]
513fn merge_bonk_trade_log_preferred(_log: &mut BonkTradeEvent, _ix: BonkTradeEvent) {}
514
515#[inline]
516fn merge_meteora_dlmm_swap_log_preferred(_log: &mut MeteoraDlmmSwapEvent, _ix: MeteoraDlmmSwapEvent) {}
517
518pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
524 use DexEvent::*;
525 match log {
526 PumpFunTrade(l) => {
527 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
528 merge_pumpfun_trade_log_preferred(l, i);
529 }
530 }
531 PumpFunBuy(l) => {
532 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
533 merge_pumpfun_trade_log_preferred(l, i);
534 }
535 }
536 PumpFunSell(l) => {
537 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
538 merge_pumpfun_trade_log_preferred(l, i);
539 }
540 }
541 PumpFunBuyExactSolIn(l) => {
542 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
543 merge_pumpfun_trade_log_preferred(l, i);
544 }
545 }
546 PumpFunCreate(l) => {
547 if let DexEvent::PumpFunCreate(i) = ix {
548 merge_pumpfun_create_log_preferred(l, i);
549 }
550 }
551 PumpFunCreateV2(l) => {
552 if let DexEvent::PumpFunCreateV2(i) = ix {
553 merge_pumpfun_create_v2_log_preferred(l, i);
554 }
555 }
556 PumpFunMigrate(l) => {
557 if let DexEvent::PumpFunMigrate(i) = ix {
558 merge_pumpfun_migrate_log_preferred(l, i);
559 }
560 }
561 PumpSwapTrade(l) => {
562 if let PumpSwapTrade(i) = ix {
563 merge_pumpswap_trade_log_preferred(l, i);
564 }
565 }
566 PumpSwapBuy(l) => {
567 if let PumpSwapBuy(i) = ix {
568 merge_pumpswap_buy_log_preferred(l, i);
569 }
570 }
571 PumpSwapSell(l) => {
572 if let PumpSwapSell(i) = ix {
573 merge_pumpswap_sell_log_preferred(l, i);
574 }
575 }
576 RaydiumClmmSwap(l) => {
577 if let RaydiumClmmSwap(i) = ix {
578 merge_raydium_clmm_swap_log_preferred(l, i);
579 }
580 }
581 RaydiumAmmV4Swap(l) => {
582 if let RaydiumAmmV4Swap(i) = ix {
583 merge_raydium_amm_v4_swap_log_preferred(l, i);
584 }
585 }
586 BonkTrade(l) => {
587 if let BonkTrade(i) = ix {
588 merge_bonk_trade_log_preferred(l, i);
589 }
590 }
591 BonkPoolCreate(l) => {
592 if let BonkPoolCreate(i) = ix {
593 merge_bonk_pool_create_log_preferred(l, i);
594 }
595 }
596 BonkMigrateAmm(l) => {
597 if let BonkMigrateAmm(i) = ix {
598 merge_bonk_migrate_amm_log_preferred(l, i);
599 }
600 }
601 PumpSwapCreatePool(l) => {
602 if let PumpSwapCreatePool(i) = ix {
603 merge_pumpswap_create_pool_log_preferred(l, i);
604 }
605 }
606 PumpSwapLiquidityAdded(l) => {
607 if let PumpSwapLiquidityAdded(i) = ix {
608 merge_pumpswap_liquidity_added_log_preferred(l, i);
609 }
610 }
611 PumpSwapLiquidityRemoved(l) => {
612 if let PumpSwapLiquidityRemoved(i) = ix {
613 merge_pumpswap_liquidity_removed_log_preferred(l, i);
614 }
615 }
616 MeteoraDlmmSwap(l) => {
617 if let MeteoraDlmmSwap(i) = ix {
618 merge_meteora_dlmm_swap_log_preferred(l, i);
619 }
620 }
621 _ => {}
622 }
623}
624
625#[inline]
626fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
627 match ix {
628 DexEvent::PumpFunTrade(t)
629 | DexEvent::PumpFunBuy(t)
630 | DexEvent::PumpFunSell(t)
631 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
632 _ => None,
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use solana_sdk::{pubkey::Pubkey, signature::Signature};
640
641 #[test]
642 fn test_merge_pumpfun_trade() {
643 let metadata = EventMetadata {
644 signature: Signature::default(),
645 slot: 100,
646 tx_index: 1,
647 block_time_us: 1000,
648 grpc_recv_us: 2000,
649 recent_blockhash: None,
650 };
651
652 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
654 metadata: metadata.clone(),
655 bonding_curve: Pubkey::new_unique(),
656 associated_bonding_curve: Pubkey::new_unique(),
657 ..Default::default()
658 });
659
660 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
662 metadata: metadata.clone(),
663 mint: Pubkey::new_unique(),
664 sol_amount: 1000,
665 token_amount: 2000,
666 is_buy: true,
667 user: Pubkey::new_unique(),
668 ..Default::default()
669 });
670
671 merge_events(&mut base, inner);
673
674 if let DexEvent::PumpFunTrade(trade) = base {
676 assert_eq!(trade.sol_amount, 1000);
677 assert_eq!(trade.token_amount, 2000);
678 assert!(trade.is_buy);
679 assert_ne!(trade.bonding_curve, Pubkey::default());
681 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
682 } else {
683 panic!("Expected PumpFunTrade event");
684 }
685 }
686
687 #[test]
688 fn test_can_merge() {
689 let metadata = EventMetadata {
690 signature: Signature::default(),
691 slot: 100,
692 tx_index: 1,
693 block_time_us: 1000,
694 grpc_recv_us: 2000,
695 recent_blockhash: None,
696 };
697
698 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
699 metadata: metadata.clone(),
700 ..Default::default()
701 });
702
703 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
704 metadata: metadata.clone(),
705 ..Default::default()
706 });
707
708 assert!(can_merge(&base, &inner));
710
711 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
713 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
714 ..Default::default()
715 });
716
717 assert!(!can_merge(&base, &different_sig));
718 }
719
720 #[test]
721 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
722 let metadata = EventMetadata {
723 signature: Signature::default(),
724 slot: 1,
725 tx_index: 0,
726 block_time_us: 0,
727 grpc_recv_us: 0,
728 recent_blockhash: None,
729 };
730 let fr = Pubkey::new_unique();
731 let log_t = PumpFunTradeEvent {
732 metadata: metadata.clone(),
733 sol_amount: 50,
734 ..Default::default()
735 };
736 let mut ix_t = log_t.clone();
737 ix_t.fee_recipient = fr;
738 ix_t.sol_amount = 777;
739 let mut log_ev = DexEvent::PumpFunTrade(log_t);
740 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
741 match log_ev {
742 DexEvent::PumpFunTrade(t) => {
743 assert_eq!(t.fee_recipient, fr);
744 assert_eq!(t.sol_amount, 50);
745 }
746 _ => panic!("expected trade"),
747 }
748 }
749
750 #[test]
751 fn grpc_merge_keeps_log_trade_fields() {
752 let metadata = EventMetadata {
753 signature: Signature::default(),
754 slot: 1,
755 tx_index: 0,
756 block_time_us: 0,
757 grpc_recv_us: 0,
758 recent_blockhash: None,
759 };
760 let log_t = PumpFunTradeEvent {
761 metadata: metadata.clone(),
762 mayhem_mode: true,
763 sol_amount: 100,
764 ..Default::default()
765 };
766 let mut ix_t = log_t.clone();
767 ix_t.mayhem_mode = false;
768 ix_t.sol_amount = 999;
769
770 let mut log_ev = DexEvent::PumpFunTrade(log_t);
771 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
772 match log_ev {
773 DexEvent::PumpFunTrade(t) => {
774 assert!(t.mayhem_mode);
775 assert_eq!(t.sol_amount, 100);
776 }
777 _ => panic!("variant preserved"),
778 }
779 }
780}