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
249 base.is_created_buy |= inner.is_created_buy;
250 }
252
253#[inline(always)]
255fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
256 base.name = inner.name;
258 base.symbol = inner.symbol;
259 base.uri = inner.uri;
260 base.mint = inner.mint;
261 base.bonding_curve = inner.bonding_curve;
262 base.user = inner.user;
263 base.creator = inner.creator;
264 base.timestamp = inner.timestamp;
265 base.virtual_token_reserves = inner.virtual_token_reserves;
266 base.virtual_sol_reserves = inner.virtual_sol_reserves;
267 base.real_token_reserves = inner.real_token_reserves;
268 base.token_total_supply = inner.token_total_supply;
269 base.token_program = inner.token_program;
270 base.is_mayhem_mode = inner.is_mayhem_mode;
271}
272
273#[inline(always)]
275fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
276 base.user = inner.user;
278 base.mint = inner.mint;
279 base.mint_amount = inner.mint_amount;
280 base.sol_amount = inner.sol_amount;
281 base.pool_migration_fee = inner.pool_migration_fee;
282 base.bonding_curve = inner.bonding_curve;
283 base.timestamp = inner.timestamp;
284 base.pool = inner.pool;
285}
286
287#[inline(always)]
298pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
299 if base.metadata().signature != inner.metadata().signature {
301 return false;
302 }
303
304 match (base, inner) {
306 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
308 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
309 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
310 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
311 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
312 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
313 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
314 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
315 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
316 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
317
318 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
320 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
321
322 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
324
325 _ => false,
327 }
328}
329
330#[inline(always)]
335fn fill_pk(to: &mut Pubkey, from: Pubkey) {
336 if *to == Pubkey::default() && from != Pubkey::default() {
337 *to = from;
338 }
339}
340
341#[inline(always)]
342fn fill_str_if_empty(to: &mut String, from: &str) {
343 if to.is_empty() && !from.is_empty() {
344 to.push_str(from);
345 }
346}
347
348#[inline]
351fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
352 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
353 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
354 fill_pk(&mut log.token_program, ix.token_program);
355 fill_pk(&mut log.creator_vault, ix.creator_vault);
356 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
357 fill_pk(&mut log.creator, ix.creator);
358 if log.account.is_none() {
359 log.account = ix.account;
360 }
361 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
362 log.ix_name = ix.ix_name;
363 }
364 if !log.is_created_buy && ix.is_created_buy {
365 log.is_created_buy = true;
366 }
367}
368
369#[inline]
370fn merge_pumpfun_create_log_preferred(
371 log: &mut PumpFunCreateTokenEvent,
372 ix: PumpFunCreateTokenEvent,
373) {
374 fill_str_if_empty(&mut log.name, &ix.name);
375 fill_str_if_empty(&mut log.symbol, &ix.symbol);
376 fill_str_if_empty(&mut log.uri, &ix.uri);
377 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
378 fill_pk(&mut log.user, ix.user);
379 fill_pk(&mut log.creator, ix.creator);
380 fill_pk(&mut log.token_program, ix.token_program);
381}
382
383#[inline]
384fn merge_pumpfun_create_v2_log_preferred(
385 log: &mut PumpFunCreateV2TokenEvent,
386 ix: PumpFunCreateV2TokenEvent,
387) {
388 fill_str_if_empty(&mut log.name, &ix.name);
389 fill_str_if_empty(&mut log.symbol, &ix.symbol);
390 fill_str_if_empty(&mut log.uri, &ix.uri);
391 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
392 fill_pk(&mut log.user, ix.user);
393 fill_pk(&mut log.creator, ix.creator);
394 fill_pk(&mut log.token_program, ix.token_program);
395 fill_pk(&mut log.mint_authority, ix.mint_authority);
396 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
397 fill_pk(&mut log.global, ix.global);
398 fill_pk(&mut log.system_program, ix.system_program);
399 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
400 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
401 fill_pk(&mut log.global_params, ix.global_params);
402 fill_pk(&mut log.sol_vault, ix.sol_vault);
403 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
404 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
405 fill_pk(&mut log.event_authority, ix.event_authority);
406 fill_pk(&mut log.program, ix.program);
407 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
408}
409
410#[inline]
411fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
412 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
413 fill_pk(&mut log.pool, ix.pool);
414 fill_pk(&mut log.user, ix.user);
415}
416
417#[inline]
418fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
419 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
420 log.ix_name = ix.ix_name;
421 }
422}
423
424#[inline]
425fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
426 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
427 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
428 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
429 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
430 fill_pk(&mut log.coin_creator, ix.coin_creator);
431 fill_pk(&mut log.base_mint, ix.base_mint);
432 fill_pk(&mut log.quote_mint, ix.quote_mint);
433 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
434 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
435 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
436 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
437 fill_pk(&mut log.base_token_program, ix.base_token_program);
438 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
439 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
440 log.ix_name = ix.ix_name;
441 }
442}
443
444#[inline]
445fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
446 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
447 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
448 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
449 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
450 fill_pk(&mut log.coin_creator, ix.coin_creator);
451 fill_pk(&mut log.base_mint, ix.base_mint);
452 fill_pk(&mut log.quote_mint, ix.quote_mint);
453 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
454 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
455 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
456 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
457 fill_pk(&mut log.base_token_program, ix.base_token_program);
458 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
459}
460
461#[inline]
462fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
463 fill_pk(&mut log.token_account_0, ix.token_account_0);
464 fill_pk(&mut log.token_account_1, ix.token_account_1);
465 fill_pk(&mut log.sender, ix.sender);
466}
467
468#[inline]
469fn merge_raydium_amm_v4_swap_log_preferred(
470 log: &mut RaydiumAmmV4SwapEvent,
471 ix: RaydiumAmmV4SwapEvent,
472) {
473 fill_pk(&mut log.token_program, ix.token_program);
474 fill_pk(&mut log.amm_authority, ix.amm_authority);
475 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
476 if let Some(ref o) = ix.amm_target_orders {
477 if log.amm_target_orders.is_none() {
478 log.amm_target_orders = Some(*o);
479 }
480 }
481 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
482 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
483 fill_pk(&mut log.serum_program, ix.serum_program);
484 fill_pk(&mut log.serum_market, ix.serum_market);
485 fill_pk(&mut log.serum_bids, ix.serum_bids);
486 fill_pk(&mut log.serum_asks, ix.serum_asks);
487 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
488 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
489 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
490 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
491 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
492 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
493}
494
495#[inline]
496fn merge_pumpswap_create_pool_log_preferred(
497 log: &mut PumpSwapCreatePoolEvent,
498 ix: PumpSwapCreatePoolEvent,
499) {
500 fill_pk(&mut log.creator, ix.creator);
501 fill_pk(&mut log.pool, ix.pool);
502 fill_pk(&mut log.lp_mint, ix.lp_mint);
503 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
504 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
505 fill_pk(&mut log.coin_creator, ix.coin_creator);
506}
507
508#[inline]
509fn merge_pumpswap_liquidity_added_log_preferred(
510 log: &mut PumpSwapLiquidityAdded,
511 ix: PumpSwapLiquidityAdded,
512) {
513 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
514 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
515 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
516}
517
518#[inline]
519fn merge_pumpswap_liquidity_removed_log_preferred(
520 log: &mut PumpSwapLiquidityRemoved,
521 ix: PumpSwapLiquidityRemoved,
522) {
523 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
524 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
525 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
526}
527
528#[inline]
529fn merge_bonk_pool_create_log_preferred(log: &mut BonkPoolCreateEvent, ix: BonkPoolCreateEvent) {
530 fill_pk(&mut log.creator, ix.creator);
531 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
532 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
533 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
534}
535
536#[inline]
537fn merge_bonk_migrate_amm_log_preferred(log: &mut BonkMigrateAmmEvent, ix: BonkMigrateAmmEvent) {
538 fill_pk(&mut log.old_pool, ix.old_pool);
539 fill_pk(&mut log.new_pool, ix.new_pool);
540 fill_pk(&mut log.user, ix.user);
541}
542
543#[inline]
545fn merge_bonk_trade_log_preferred(_log: &mut BonkTradeEvent, _ix: BonkTradeEvent) {}
546
547#[inline]
548fn merge_meteora_dlmm_swap_log_preferred(
549 _log: &mut MeteoraDlmmSwapEvent,
550 _ix: MeteoraDlmmSwapEvent,
551) {
552}
553
554pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
560 use DexEvent::*;
561 match log {
562 PumpFunTrade(l) => {
563 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
564 merge_pumpfun_trade_log_preferred(l, i);
565 }
566 }
567 PumpFunBuy(l) => {
568 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
569 merge_pumpfun_trade_log_preferred(l, i);
570 }
571 }
572 PumpFunSell(l) => {
573 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
574 merge_pumpfun_trade_log_preferred(l, i);
575 }
576 }
577 PumpFunBuyExactSolIn(l) => {
578 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
579 merge_pumpfun_trade_log_preferred(l, i);
580 }
581 }
582 PumpFunCreate(l) => {
583 if let DexEvent::PumpFunCreate(i) = ix {
584 merge_pumpfun_create_log_preferred(l, i);
585 }
586 }
587 PumpFunCreateV2(l) => {
588 if let DexEvent::PumpFunCreateV2(i) = ix {
589 merge_pumpfun_create_v2_log_preferred(l, i);
590 }
591 }
592 PumpFunMigrate(l) => {
593 if let DexEvent::PumpFunMigrate(i) = ix {
594 merge_pumpfun_migrate_log_preferred(l, i);
595 }
596 }
597 PumpSwapTrade(l) => {
598 if let PumpSwapTrade(i) = ix {
599 merge_pumpswap_trade_log_preferred(l, i);
600 }
601 }
602 PumpSwapBuy(l) => {
603 if let PumpSwapBuy(i) = ix {
604 merge_pumpswap_buy_log_preferred(l, i);
605 }
606 }
607 PumpSwapSell(l) => {
608 if let PumpSwapSell(i) = ix {
609 merge_pumpswap_sell_log_preferred(l, i);
610 }
611 }
612 RaydiumClmmSwap(l) => {
613 if let RaydiumClmmSwap(i) = ix {
614 merge_raydium_clmm_swap_log_preferred(l, i);
615 }
616 }
617 RaydiumAmmV4Swap(l) => {
618 if let RaydiumAmmV4Swap(i) = ix {
619 merge_raydium_amm_v4_swap_log_preferred(l, i);
620 }
621 }
622 BonkTrade(l) => {
623 if let BonkTrade(i) = ix {
624 merge_bonk_trade_log_preferred(l, i);
625 }
626 }
627 BonkPoolCreate(l) => {
628 if let BonkPoolCreate(i) = ix {
629 merge_bonk_pool_create_log_preferred(l, i);
630 }
631 }
632 BonkMigrateAmm(l) => {
633 if let BonkMigrateAmm(i) = ix {
634 merge_bonk_migrate_amm_log_preferred(l, i);
635 }
636 }
637 PumpSwapCreatePool(l) => {
638 if let PumpSwapCreatePool(i) = ix {
639 merge_pumpswap_create_pool_log_preferred(l, i);
640 }
641 }
642 PumpSwapLiquidityAdded(l) => {
643 if let PumpSwapLiquidityAdded(i) = ix {
644 merge_pumpswap_liquidity_added_log_preferred(l, i);
645 }
646 }
647 PumpSwapLiquidityRemoved(l) => {
648 if let PumpSwapLiquidityRemoved(i) = ix {
649 merge_pumpswap_liquidity_removed_log_preferred(l, i);
650 }
651 }
652 MeteoraDlmmSwap(l) => {
653 if let MeteoraDlmmSwap(i) = ix {
654 merge_meteora_dlmm_swap_log_preferred(l, i);
655 }
656 }
657 _ => {}
658 }
659}
660
661#[inline]
662fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
663 match ix {
664 DexEvent::PumpFunTrade(t)
665 | DexEvent::PumpFunBuy(t)
666 | DexEvent::PumpFunSell(t)
667 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
668 _ => None,
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675 use solana_sdk::{pubkey::Pubkey, signature::Signature};
676
677 #[test]
678 fn test_merge_pumpfun_trade() {
679 let metadata = EventMetadata {
680 signature: Signature::default(),
681 slot: 100,
682 tx_index: 1,
683 block_time_us: 1000,
684 grpc_recv_us: 2000,
685 recent_blockhash: None,
686 };
687
688 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
690 metadata: metadata.clone(),
691 bonding_curve: Pubkey::new_unique(),
692 associated_bonding_curve: Pubkey::new_unique(),
693 ..Default::default()
694 });
695
696 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
698 metadata: metadata.clone(),
699 mint: Pubkey::new_unique(),
700 sol_amount: 1000,
701 token_amount: 2000,
702 is_buy: true,
703 user: Pubkey::new_unique(),
704 ..Default::default()
705 });
706
707 merge_events(&mut base, inner);
709
710 if let DexEvent::PumpFunTrade(trade) = base {
712 assert_eq!(trade.sol_amount, 1000);
713 assert_eq!(trade.token_amount, 2000);
714 assert!(trade.is_buy);
715 assert_ne!(trade.bonding_curve, Pubkey::default());
717 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
718 } else {
719 panic!("Expected PumpFunTrade event");
720 }
721 }
722
723 #[test]
724 fn test_can_merge() {
725 let metadata = EventMetadata {
726 signature: Signature::default(),
727 slot: 100,
728 tx_index: 1,
729 block_time_us: 1000,
730 grpc_recv_us: 2000,
731 recent_blockhash: None,
732 };
733
734 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
735 metadata: metadata.clone(),
736 ..Default::default()
737 });
738
739 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
740 metadata: metadata.clone(),
741 ..Default::default()
742 });
743
744 assert!(can_merge(&base, &inner));
746
747 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
749 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
750 ..Default::default()
751 });
752
753 assert!(!can_merge(&base, &different_sig));
754 }
755
756 #[test]
757 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
758 let metadata = EventMetadata {
759 signature: Signature::default(),
760 slot: 1,
761 tx_index: 0,
762 block_time_us: 0,
763 grpc_recv_us: 0,
764 recent_blockhash: None,
765 };
766 let fr = Pubkey::new_unique();
767 let log_t =
768 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
769 let mut ix_t = log_t.clone();
770 ix_t.fee_recipient = fr;
771 ix_t.sol_amount = 777;
772 let mut log_ev = DexEvent::PumpFunTrade(log_t);
773 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
774 match log_ev {
775 DexEvent::PumpFunTrade(t) => {
776 assert_eq!(t.fee_recipient, fr);
777 assert_eq!(t.sol_amount, 50);
778 }
779 _ => panic!("expected trade"),
780 }
781 }
782
783 #[test]
784 fn grpc_merge_keeps_log_trade_fields() {
785 let metadata = EventMetadata {
786 signature: Signature::default(),
787 slot: 1,
788 tx_index: 0,
789 block_time_us: 0,
790 grpc_recv_us: 0,
791 recent_blockhash: None,
792 };
793 let log_t = PumpFunTradeEvent {
794 metadata: metadata.clone(),
795 mayhem_mode: true,
796 sol_amount: 100,
797 ..Default::default()
798 };
799 let mut ix_t = log_t.clone();
800 ix_t.mayhem_mode = false;
801 ix_t.sol_amount = 999;
802
803 let mut log_ev = DexEvent::PumpFunTrade(log_t);
804 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
805 match log_ev {
806 DexEvent::PumpFunTrade(t) => {
807 assert!(t.mayhem_mode);
808 assert_eq!(t.sol_amount, 100);
809 }
810 _ => panic!("variant preserved"),
811 }
812 }
813}