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 (PumpFunMigrateBondingCurveCreator(b), PumpFunMigrateBondingCurveCreator(i)) => {
48 merge_generic(b, i)
49 }
50
51 (PumpFeesCreateFeeSharingConfig(b), PumpFeesCreateFeeSharingConfig(i)) => {
53 merge_generic(b, i)
54 }
55 (PumpFeesInitializeFeeConfig(b), PumpFeesInitializeFeeConfig(i)) => merge_generic(b, i),
56 (PumpFeesResetFeeSharingConfig(b), PumpFeesResetFeeSharingConfig(i)) => merge_generic(b, i),
57 (PumpFeesRevokeFeeSharingAuthority(b), PumpFeesRevokeFeeSharingAuthority(i)) => {
58 merge_generic(b, i)
59 }
60 (PumpFeesTransferFeeSharingAuthority(b), PumpFeesTransferFeeSharingAuthority(i)) => {
61 merge_generic(b, i)
62 }
63 (PumpFeesUpdateAdmin(b), PumpFeesUpdateAdmin(i)) => merge_generic(b, i),
64 (PumpFeesUpdateFeeConfig(b), PumpFeesUpdateFeeConfig(i)) => merge_generic(b, i),
65 (PumpFeesUpdateFeeShares(b), PumpFeesUpdateFeeShares(i)) => merge_generic(b, i),
66 (PumpFeesUpsertFeeTiers(b), PumpFeesUpsertFeeTiers(i)) => merge_generic(b, i),
67
68 (PumpSwapTrade(b), PumpSwapTrade(i)) => merge_generic(b, i),
70 (PumpSwapBuy(b), PumpSwapBuy(i)) => merge_generic(b, i),
71 (PumpSwapSell(b), PumpSwapSell(i)) => merge_generic(b, i),
72 (PumpSwapCreatePool(b), PumpSwapCreatePool(i)) => merge_generic(b, i),
73 (PumpSwapLiquidityAdded(b), PumpSwapLiquidityAdded(i)) => merge_generic(b, i),
74 (PumpSwapLiquidityRemoved(b), PumpSwapLiquidityRemoved(i)) => merge_generic(b, i),
75
76 (RaydiumClmmSwap(b), RaydiumClmmSwap(i)) => merge_generic(b, i),
78 (RaydiumClmmIncreaseLiquidity(b), RaydiumClmmIncreaseLiquidity(i)) => merge_generic(b, i),
79 (RaydiumClmmDecreaseLiquidity(b), RaydiumClmmDecreaseLiquidity(i)) => merge_generic(b, i),
80 (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
81 (RaydiumClmmOpenPosition(b), RaydiumClmmOpenPosition(i)) => merge_generic(b, i),
82 (RaydiumClmmClosePosition(b), RaydiumClmmClosePosition(i)) => merge_generic(b, i),
83 (RaydiumClmmOpenPositionWithTokenExtNft(b), RaydiumClmmOpenPositionWithTokenExtNft(i)) => {
84 merge_generic(b, i)
85 }
86 (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
87
88 (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_generic(b, i),
90 (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
91 (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
92 (RaydiumCpmmInitialize(b), RaydiumCpmmInitialize(i)) => merge_generic(b, i),
93
94 (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => merge_generic(b, i),
96 (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
97 (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
98 (RaydiumAmmV4Initialize2(b), RaydiumAmmV4Initialize2(i)) => merge_generic(b, i),
99 (RaydiumAmmV4WithdrawPnl(b), RaydiumAmmV4WithdrawPnl(i)) => merge_generic(b, i),
100
101 (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => merge_generic(b, i),
103 (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => {
104 merge_generic(b, i)
105 }
106 (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => {
107 merge_generic(b, i)
108 }
109 (OrcaWhirlpoolPoolInitialized(b), OrcaWhirlpoolPoolInitialized(i)) => merge_generic(b, i),
110
111 (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => merge_generic(b, i),
113 (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => merge_generic(b, i),
114 (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => merge_generic(b, i),
115 (MeteoraPoolsBootstrapLiquidity(b), MeteoraPoolsBootstrapLiquidity(i)) => {
116 merge_generic(b, i)
117 }
118 (MeteoraPoolsPoolCreated(b), MeteoraPoolsPoolCreated(i)) => merge_generic(b, i),
119 (MeteoraPoolsSetPoolFees(b), MeteoraPoolsSetPoolFees(i)) => merge_generic(b, i),
120
121 (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
123 (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
124 (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
125 (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
126 (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
127
128 (MeteoraDlmmSwap(b), MeteoraDlmmSwap(i)) => merge_generic(b, i),
130 (MeteoraDlmmAddLiquidity(b), MeteoraDlmmAddLiquidity(i)) => merge_generic(b, i),
131 (MeteoraDlmmRemoveLiquidity(b), MeteoraDlmmRemoveLiquidity(i)) => merge_generic(b, i),
132 (MeteoraDlmmInitializePool(b), MeteoraDlmmInitializePool(i)) => merge_generic(b, i),
133 (MeteoraDlmmInitializeBinArray(b), MeteoraDlmmInitializeBinArray(i)) => merge_generic(b, i),
134 (MeteoraDlmmCreatePosition(b), MeteoraDlmmCreatePosition(i)) => merge_generic(b, i),
135 (MeteoraDlmmClosePosition(b), MeteoraDlmmClosePosition(i)) => merge_generic(b, i),
136 (MeteoraDlmmClaimFee(b), MeteoraDlmmClaimFee(i)) => merge_generic(b, i),
137
138 (BonkTrade(b), BonkTrade(i)) => merge_generic(b, i),
140 (BonkPoolCreate(b), BonkPoolCreate(i)) => merge_generic(b, i),
141 (BonkMigrateAmm(b), BonkMigrateAmm(i)) => merge_generic(b, i),
142
143 _ => {}
145 }
146}
147
148#[inline(always)]
155fn merge_generic<T>(base: &mut T, inner: T) {
156 *base = inner;
157}
158
159#[inline(always)]
164fn put_pk_if_set(to: &mut Pubkey, from: Pubkey) {
165 if from != Pubkey::default() {
166 *to = from;
167 }
168}
169
170#[inline(always)]
171fn put_u64_if_nonzero(to: &mut u64, from: u64) {
172 if from != 0 {
173 *to = from;
174 }
175}
176
177#[inline(always)]
178fn put_i64_if_nonzero(to: &mut i64, from: i64) {
179 if from != 0 {
180 *to = from;
181 }
182}
183
184#[inline(always)]
194fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
195 let leg = inner.sol_amount != 0 || inner.token_amount != 0;
196
197 put_pk_if_set(&mut base.mint, inner.mint);
198 put_pk_if_set(&mut base.user, inner.user);
199 put_pk_if_set(&mut base.fee_recipient, inner.fee_recipient);
200 put_pk_if_set(&mut base.creator, inner.creator);
201
202 if leg {
203 base.sol_amount = inner.sol_amount;
204 base.token_amount = inner.token_amount;
205 base.is_buy = inner.is_buy;
206 base.timestamp = inner.timestamp;
207 base.virtual_sol_reserves = inner.virtual_sol_reserves;
208 base.virtual_token_reserves = inner.virtual_token_reserves;
209 base.real_sol_reserves = inner.real_sol_reserves;
210 base.real_token_reserves = inner.real_token_reserves;
211 base.fee_basis_points = inner.fee_basis_points;
212 base.fee = inner.fee;
213 base.creator_fee_basis_points = inner.creator_fee_basis_points;
214 base.creator_fee = inner.creator_fee;
215 base.track_volume |= inner.track_volume;
216 base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
217 base.total_claimed_tokens = inner.total_claimed_tokens;
218 base.current_sol_volume = inner.current_sol_volume;
219 base.last_update_timestamp = inner.last_update_timestamp;
220 base.ix_name = inner.ix_name;
221 base.mayhem_mode |= inner.mayhem_mode;
222 base.cashback_fee_basis_points = inner.cashback_fee_basis_points;
223 base.cashback = inner.cashback;
224 base.is_cashback_coin |= inner.is_cashback_coin;
225 } else {
226 put_u64_if_nonzero(&mut base.fee, inner.fee);
227 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
228 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
229 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
230 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
231 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
232 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
233 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
234 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
235 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
236 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
237 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
238 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
239 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
240 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
241 if !inner.ix_name.is_empty() {
242 base.ix_name = inner.ix_name;
243 }
244 base.track_volume |= inner.track_volume;
245 base.mayhem_mode |= inner.mayhem_mode;
246 base.is_cashback_coin |= inner.is_cashback_coin;
247 }
248 put_u64_if_nonzero(&mut base.amount, inner.amount);
249 put_u64_if_nonzero(&mut base.max_sol_cost, inner.max_sol_cost);
250 put_u64_if_nonzero(&mut base.min_sol_output, inner.min_sol_output);
251
252 base.is_created_buy |= inner.is_created_buy;
253 }
255
256#[inline(always)]
258fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
259 base.name = inner.name;
261 base.symbol = inner.symbol;
262 base.uri = inner.uri;
263 base.mint = inner.mint;
264 base.bonding_curve = inner.bonding_curve;
265 base.user = inner.user;
266 base.creator = inner.creator;
267 base.timestamp = inner.timestamp;
268 base.virtual_token_reserves = inner.virtual_token_reserves;
269 base.virtual_sol_reserves = inner.virtual_sol_reserves;
270 base.real_token_reserves = inner.real_token_reserves;
271 base.token_total_supply = inner.token_total_supply;
272 base.token_program = inner.token_program;
273 base.is_mayhem_mode = inner.is_mayhem_mode;
274}
275
276#[inline(always)]
278fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
279 base.user = inner.user;
281 base.mint = inner.mint;
282 base.mint_amount = inner.mint_amount;
283 base.sol_amount = inner.sol_amount;
284 base.pool_migration_fee = inner.pool_migration_fee;
285 base.bonding_curve = inner.bonding_curve;
286 base.timestamp = inner.timestamp;
287 base.pool = inner.pool;
288}
289
290#[inline(always)]
301pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
302 if base.metadata().signature != inner.metadata().signature {
304 return false;
305 }
306
307 match (base, inner) {
309 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
311 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
312 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
313 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
314 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
315 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
316 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
317 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
318 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
319 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
320
321 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
323 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
324
325 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
327
328 _ => false,
330 }
331}
332
333#[inline(always)]
338fn fill_pk(to: &mut Pubkey, from: Pubkey) {
339 if *to == Pubkey::default() && from != Pubkey::default() {
340 *to = from;
341 }
342}
343
344#[inline(always)]
345fn fill_str_if_empty(to: &mut String, from: &str) {
346 if to.is_empty() && !from.is_empty() {
347 to.push_str(from);
348 }
349}
350
351#[inline]
354fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
355 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
356 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
357 fill_pk(&mut log.token_program, ix.token_program);
358 fill_pk(&mut log.creator_vault, ix.creator_vault);
359 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
360 fill_pk(&mut log.creator, ix.creator);
361 if log.account.is_none() {
362 log.account = ix.account;
363 }
364 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
365 log.ix_name = ix.ix_name;
366 }
367 put_u64_if_nonzero(&mut log.amount, ix.amount);
368 put_u64_if_nonzero(&mut log.max_sol_cost, ix.max_sol_cost);
369 put_u64_if_nonzero(&mut log.min_sol_output, ix.min_sol_output);
370 if !log.is_created_buy && ix.is_created_buy {
371 log.is_created_buy = true;
372 }
373}
374
375#[inline]
376fn merge_pumpfun_create_log_preferred(
377 log: &mut PumpFunCreateTokenEvent,
378 ix: PumpFunCreateTokenEvent,
379) {
380 fill_str_if_empty(&mut log.name, &ix.name);
381 fill_str_if_empty(&mut log.symbol, &ix.symbol);
382 fill_str_if_empty(&mut log.uri, &ix.uri);
383 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
384 fill_pk(&mut log.user, ix.user);
385 fill_pk(&mut log.creator, ix.creator);
386 fill_pk(&mut log.token_program, ix.token_program);
387}
388
389#[inline]
390fn merge_pumpfun_create_v2_log_preferred(
391 log: &mut PumpFunCreateV2TokenEvent,
392 ix: PumpFunCreateV2TokenEvent,
393) {
394 fill_str_if_empty(&mut log.name, &ix.name);
395 fill_str_if_empty(&mut log.symbol, &ix.symbol);
396 fill_str_if_empty(&mut log.uri, &ix.uri);
397 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
398 fill_pk(&mut log.user, ix.user);
399 fill_pk(&mut log.creator, ix.creator);
400 fill_pk(&mut log.token_program, ix.token_program);
401 fill_pk(&mut log.mint_authority, ix.mint_authority);
402 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
403 fill_pk(&mut log.global, ix.global);
404 fill_pk(&mut log.system_program, ix.system_program);
405 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
406 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
407 fill_pk(&mut log.global_params, ix.global_params);
408 fill_pk(&mut log.sol_vault, ix.sol_vault);
409 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
410 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
411 fill_pk(&mut log.event_authority, ix.event_authority);
412 fill_pk(&mut log.program, ix.program);
413 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
414}
415
416#[inline]
417fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
418 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
419 fill_pk(&mut log.pool, ix.pool);
420 fill_pk(&mut log.user, ix.user);
421}
422
423#[inline]
424fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
425 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
426 log.ix_name = ix.ix_name;
427 }
428}
429
430#[inline]
431fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
432 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
433 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
434 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
435 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
436 fill_pk(&mut log.coin_creator, ix.coin_creator);
437 fill_pk(&mut log.base_mint, ix.base_mint);
438 fill_pk(&mut log.quote_mint, ix.quote_mint);
439 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
440 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
441 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
442 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
443 fill_pk(&mut log.base_token_program, ix.base_token_program);
444 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
445 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
446 log.ix_name = ix.ix_name;
447 }
448}
449
450#[inline]
451fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
452 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
453 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
454 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
455 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
456 fill_pk(&mut log.coin_creator, ix.coin_creator);
457 fill_pk(&mut log.base_mint, ix.base_mint);
458 fill_pk(&mut log.quote_mint, ix.quote_mint);
459 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
460 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
461 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
462 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
463 fill_pk(&mut log.base_token_program, ix.base_token_program);
464 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
465}
466
467#[inline]
468fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
469 fill_pk(&mut log.token_account_0, ix.token_account_0);
470 fill_pk(&mut log.token_account_1, ix.token_account_1);
471 fill_pk(&mut log.sender, ix.sender);
472}
473
474#[inline]
475fn merge_raydium_amm_v4_swap_log_preferred(
476 log: &mut RaydiumAmmV4SwapEvent,
477 ix: RaydiumAmmV4SwapEvent,
478) {
479 fill_pk(&mut log.token_program, ix.token_program);
480 fill_pk(&mut log.amm_authority, ix.amm_authority);
481 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
482 if let Some(ref o) = ix.amm_target_orders {
483 if log.amm_target_orders.is_none() {
484 log.amm_target_orders = Some(*o);
485 }
486 }
487 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
488 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
489 fill_pk(&mut log.serum_program, ix.serum_program);
490 fill_pk(&mut log.serum_market, ix.serum_market);
491 fill_pk(&mut log.serum_bids, ix.serum_bids);
492 fill_pk(&mut log.serum_asks, ix.serum_asks);
493 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
494 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
495 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
496 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
497 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
498 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
499}
500
501#[inline]
502fn merge_pumpswap_create_pool_log_preferred(
503 log: &mut PumpSwapCreatePoolEvent,
504 ix: PumpSwapCreatePoolEvent,
505) {
506 fill_pk(&mut log.creator, ix.creator);
507 fill_pk(&mut log.pool, ix.pool);
508 fill_pk(&mut log.lp_mint, ix.lp_mint);
509 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
510 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
511 fill_pk(&mut log.coin_creator, ix.coin_creator);
512}
513
514#[inline]
515fn merge_pumpswap_liquidity_added_log_preferred(
516 log: &mut PumpSwapLiquidityAdded,
517 ix: PumpSwapLiquidityAdded,
518) {
519 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
520 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
521 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
522}
523
524#[inline]
525fn merge_pumpswap_liquidity_removed_log_preferred(
526 log: &mut PumpSwapLiquidityRemoved,
527 ix: PumpSwapLiquidityRemoved,
528) {
529 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
530 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
531 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
532}
533
534#[inline]
535fn merge_bonk_pool_create_log_preferred(log: &mut BonkPoolCreateEvent, ix: BonkPoolCreateEvent) {
536 fill_pk(&mut log.creator, ix.creator);
537 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
538 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
539 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
540}
541
542#[inline]
543fn merge_bonk_migrate_amm_log_preferred(log: &mut BonkMigrateAmmEvent, ix: BonkMigrateAmmEvent) {
544 fill_pk(&mut log.old_pool, ix.old_pool);
545 fill_pk(&mut log.new_pool, ix.new_pool);
546 fill_pk(&mut log.user, ix.user);
547}
548
549#[inline]
551fn merge_bonk_trade_log_preferred(_log: &mut BonkTradeEvent, _ix: BonkTradeEvent) {}
552
553#[inline]
554fn merge_meteora_dlmm_swap_log_preferred(
555 _log: &mut MeteoraDlmmSwapEvent,
556 _ix: MeteoraDlmmSwapEvent,
557) {
558}
559
560pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
566 use DexEvent::*;
567 match log {
568 PumpFunTrade(l) => {
569 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
570 merge_pumpfun_trade_log_preferred(l, i);
571 }
572 }
573 PumpFunBuy(l) => {
574 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
575 merge_pumpfun_trade_log_preferred(l, i);
576 }
577 }
578 PumpFunSell(l) => {
579 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
580 merge_pumpfun_trade_log_preferred(l, i);
581 }
582 }
583 PumpFunBuyExactSolIn(l) => {
584 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
585 merge_pumpfun_trade_log_preferred(l, i);
586 }
587 }
588 PumpFunCreate(l) => {
589 if let DexEvent::PumpFunCreate(i) = ix {
590 merge_pumpfun_create_log_preferred(l, i);
591 }
592 }
593 PumpFunCreateV2(l) => {
594 if let DexEvent::PumpFunCreateV2(i) = ix {
595 merge_pumpfun_create_v2_log_preferred(l, i);
596 }
597 }
598 PumpFunMigrate(l) => {
599 if let DexEvent::PumpFunMigrate(i) = ix {
600 merge_pumpfun_migrate_log_preferred(l, i);
601 }
602 }
603 PumpSwapTrade(l) => {
604 if let PumpSwapTrade(i) = ix {
605 merge_pumpswap_trade_log_preferred(l, i);
606 }
607 }
608 PumpSwapBuy(l) => {
609 if let PumpSwapBuy(i) = ix {
610 merge_pumpswap_buy_log_preferred(l, i);
611 }
612 }
613 PumpSwapSell(l) => {
614 if let PumpSwapSell(i) = ix {
615 merge_pumpswap_sell_log_preferred(l, i);
616 }
617 }
618 RaydiumClmmSwap(l) => {
619 if let RaydiumClmmSwap(i) = ix {
620 merge_raydium_clmm_swap_log_preferred(l, i);
621 }
622 }
623 RaydiumAmmV4Swap(l) => {
624 if let RaydiumAmmV4Swap(i) = ix {
625 merge_raydium_amm_v4_swap_log_preferred(l, i);
626 }
627 }
628 BonkTrade(l) => {
629 if let BonkTrade(i) = ix {
630 merge_bonk_trade_log_preferred(l, i);
631 }
632 }
633 BonkPoolCreate(l) => {
634 if let BonkPoolCreate(i) = ix {
635 merge_bonk_pool_create_log_preferred(l, i);
636 }
637 }
638 BonkMigrateAmm(l) => {
639 if let BonkMigrateAmm(i) = ix {
640 merge_bonk_migrate_amm_log_preferred(l, i);
641 }
642 }
643 PumpSwapCreatePool(l) => {
644 if let PumpSwapCreatePool(i) = ix {
645 merge_pumpswap_create_pool_log_preferred(l, i);
646 }
647 }
648 PumpSwapLiquidityAdded(l) => {
649 if let PumpSwapLiquidityAdded(i) = ix {
650 merge_pumpswap_liquidity_added_log_preferred(l, i);
651 }
652 }
653 PumpSwapLiquidityRemoved(l) => {
654 if let PumpSwapLiquidityRemoved(i) = ix {
655 merge_pumpswap_liquidity_removed_log_preferred(l, i);
656 }
657 }
658 MeteoraDlmmSwap(l) => {
659 if let MeteoraDlmmSwap(i) = ix {
660 merge_meteora_dlmm_swap_log_preferred(l, i);
661 }
662 }
663 _ => {}
664 }
665}
666
667#[inline]
668fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
669 match ix {
670 DexEvent::PumpFunTrade(t)
671 | DexEvent::PumpFunBuy(t)
672 | DexEvent::PumpFunSell(t)
673 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
674 _ => None,
675 }
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681 use solana_sdk::{pubkey::Pubkey, signature::Signature};
682
683 #[test]
684 fn test_merge_pumpfun_trade() {
685 let metadata = EventMetadata {
686 signature: Signature::default(),
687 slot: 100,
688 tx_index: 1,
689 block_time_us: 1000,
690 grpc_recv_us: 2000,
691 recent_blockhash: None,
692 };
693
694 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
696 metadata: metadata.clone(),
697 bonding_curve: Pubkey::new_unique(),
698 associated_bonding_curve: Pubkey::new_unique(),
699 ..Default::default()
700 });
701
702 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
704 metadata: metadata.clone(),
705 mint: Pubkey::new_unique(),
706 sol_amount: 1000,
707 token_amount: 2000,
708 is_buy: true,
709 user: Pubkey::new_unique(),
710 ..Default::default()
711 });
712
713 merge_events(&mut base, inner);
715
716 if let DexEvent::PumpFunTrade(trade) = base {
718 assert_eq!(trade.sol_amount, 1000);
719 assert_eq!(trade.token_amount, 2000);
720 assert!(trade.is_buy);
721 assert_ne!(trade.bonding_curve, Pubkey::default());
723 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
724 } else {
725 panic!("Expected PumpFunTrade event");
726 }
727 }
728
729 #[test]
730 fn test_can_merge() {
731 let metadata = EventMetadata {
732 signature: Signature::default(),
733 slot: 100,
734 tx_index: 1,
735 block_time_us: 1000,
736 grpc_recv_us: 2000,
737 recent_blockhash: None,
738 };
739
740 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
741 metadata: metadata.clone(),
742 ..Default::default()
743 });
744
745 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
746 metadata: metadata.clone(),
747 ..Default::default()
748 });
749
750 assert!(can_merge(&base, &inner));
752
753 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
755 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
756 ..Default::default()
757 });
758
759 assert!(!can_merge(&base, &different_sig));
760 }
761
762 #[test]
763 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
764 let metadata = EventMetadata {
765 signature: Signature::default(),
766 slot: 1,
767 tx_index: 0,
768 block_time_us: 0,
769 grpc_recv_us: 0,
770 recent_blockhash: None,
771 };
772 let fr = Pubkey::new_unique();
773 let log_t =
774 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
775 let mut ix_t = log_t.clone();
776 ix_t.fee_recipient = fr;
777 ix_t.sol_amount = 777;
778 let mut log_ev = DexEvent::PumpFunTrade(log_t);
779 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
780 match log_ev {
781 DexEvent::PumpFunTrade(t) => {
782 assert_eq!(t.fee_recipient, fr);
783 assert_eq!(t.sol_amount, 50);
784 }
785 _ => panic!("expected trade"),
786 }
787 }
788
789 #[test]
790 fn grpc_merge_keeps_log_trade_fields() {
791 let metadata = EventMetadata {
792 signature: Signature::default(),
793 slot: 1,
794 tx_index: 0,
795 block_time_us: 0,
796 grpc_recv_us: 0,
797 recent_blockhash: None,
798 };
799 let log_t = PumpFunTradeEvent {
800 metadata: metadata.clone(),
801 mayhem_mode: true,
802 sol_amount: 100,
803 ..Default::default()
804 };
805 let mut ix_t = log_t.clone();
806 ix_t.mayhem_mode = false;
807 ix_t.sol_amount = 999;
808
809 let mut log_ev = DexEvent::PumpFunTrade(log_t);
810 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
811 match log_ev {
812 DexEvent::PumpFunTrade(t) => {
813 assert!(t.mayhem_mode);
814 assert_eq!(t.sol_amount, 100);
815 }
816 _ => panic!("variant preserved"),
817 }
818 }
819}