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.payer, ix.payer);
854 fill_pk(&mut log.creator, ix.creator);
855 fill_pk(&mut log.global_config, ix.global_config);
856 fill_pk(&mut log.platform_config, ix.platform_config);
857 fill_pk(&mut log.base_mint, ix.base_mint);
858 fill_pk(&mut log.quote_mint, ix.quote_mint);
859 fill_pk(&mut log.base_vault, ix.base_vault);
860 fill_pk(&mut log.quote_vault, ix.quote_vault);
861 fill_pk(&mut log.base_token_program, ix.base_token_program);
862 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
863 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
864 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
865 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
866}
867
868#[inline]
869fn merge_raydium_launchlab_migrate_amm_log_preferred(
870 log: &mut RaydiumLaunchlabMigrateAmmEvent,
871 ix: RaydiumLaunchlabMigrateAmmEvent,
872) {
873 fill_pk(&mut log.old_pool, ix.old_pool);
874 fill_pk(&mut log.new_pool, ix.new_pool);
875 fill_pk(&mut log.user, ix.user);
876}
877
878#[inline]
879fn merge_raydium_launchlab_trade_log_preferred(
880 log: &mut RaydiumLaunchlabTradeEvent,
881 ix: RaydiumLaunchlabTradeEvent,
882) {
883 fill_pk(&mut log.user, ix.user);
884 fill_pk(&mut log.global_config, ix.global_config);
885 fill_pk(&mut log.platform_config, ix.platform_config);
886 fill_pk(&mut log.user_base_token, ix.user_base_token);
887 fill_pk(&mut log.user_quote_token, ix.user_quote_token);
888 fill_pk(&mut log.base_vault, ix.base_vault);
889 fill_pk(&mut log.quote_vault, ix.quote_vault);
890 fill_pk(&mut log.base_mint, ix.base_mint);
891 fill_pk(&mut log.quote_mint, ix.quote_mint);
892 fill_pk(&mut log.base_token_program, ix.base_token_program);
893 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
894}
895
896#[inline]
897fn merge_meteora_dlmm_swap_log_preferred(
898 _log: &mut MeteoraDlmmSwapEvent,
899 _ix: MeteoraDlmmSwapEvent,
900) {
901}
902
903pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
909 use DexEvent::*;
910 match log {
911 PumpFunTrade(l) => {
912 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
913 merge_pumpfun_trade_log_preferred(l, i);
914 }
915 }
916 PumpFunBuy(l) => {
917 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
918 merge_pumpfun_trade_log_preferred(l, i);
919 }
920 }
921 PumpFunSell(l) => {
922 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
923 merge_pumpfun_trade_log_preferred(l, i);
924 }
925 }
926 PumpFunBuyExactSolIn(l) => {
927 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
928 merge_pumpfun_trade_log_preferred(l, i);
929 }
930 }
931 PumpFunCreate(l) => match ix {
932 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_log_preferred(l, i),
933 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_into_create_log_preferred(l, i),
934 _ => {}
935 },
936 PumpFunCreateV2(l) => match ix {
937 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_into_create_v2_log_preferred(l, i),
938 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_log_preferred(l, i),
939 _ => {}
940 },
941 PumpFunMigrate(l) => {
942 if let DexEvent::PumpFunMigrate(i) = ix {
943 merge_pumpfun_migrate_log_preferred(l, i);
944 }
945 }
946 PumpSwapTrade(l) => {
947 if let PumpSwapTrade(i) = ix {
948 merge_pumpswap_trade_log_preferred(l, i);
949 }
950 }
951 PumpSwapBuy(l) => {
952 if let PumpSwapBuy(i) = ix {
953 merge_pumpswap_buy_log_preferred(l, i);
954 }
955 }
956 PumpSwapSell(l) => {
957 if let PumpSwapSell(i) = ix {
958 merge_pumpswap_sell_log_preferred(l, i);
959 }
960 }
961 RaydiumClmmSwap(l) => {
962 if let RaydiumClmmSwap(i) = ix {
963 merge_raydium_clmm_swap_log_preferred(l, i);
964 }
965 }
966 RaydiumAmmV4Swap(l) => {
967 if let RaydiumAmmV4Swap(i) = ix {
968 merge_raydium_amm_v4_swap_log_preferred(l, i);
969 }
970 }
971 RaydiumLaunchlabTrade(l) => {
972 if let RaydiumLaunchlabTrade(i) = ix {
973 merge_raydium_launchlab_trade_log_preferred(l, i);
974 }
975 }
976 RaydiumLaunchlabPoolCreate(l) => {
977 if let RaydiumLaunchlabPoolCreate(i) = ix {
978 merge_raydium_launchlab_pool_create_log_preferred(l, i);
979 }
980 }
981 RaydiumLaunchlabMigrateAmm(l) => {
982 if let RaydiumLaunchlabMigrateAmm(i) = ix {
983 merge_raydium_launchlab_migrate_amm_log_preferred(l, i);
984 }
985 }
986 PumpSwapCreatePool(l) => {
987 if let PumpSwapCreatePool(i) = ix {
988 merge_pumpswap_create_pool_log_preferred(l, i);
989 }
990 }
991 PumpSwapLiquidityAdded(l) => {
992 if let PumpSwapLiquidityAdded(i) = ix {
993 merge_pumpswap_liquidity_added_log_preferred(l, i);
994 }
995 }
996 PumpSwapLiquidityRemoved(l) => {
997 if let PumpSwapLiquidityRemoved(i) = ix {
998 merge_pumpswap_liquidity_removed_log_preferred(l, i);
999 }
1000 }
1001 MeteoraDlmmSwap(l) => {
1002 if let MeteoraDlmmSwap(i) = ix {
1003 merge_meteora_dlmm_swap_log_preferred(l, i);
1004 }
1005 }
1006 _ => {}
1007 }
1008}
1009
1010#[inline]
1011fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
1012 match ix {
1013 DexEvent::PumpFunTrade(t)
1014 | DexEvent::PumpFunBuy(t)
1015 | DexEvent::PumpFunSell(t)
1016 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
1017 _ => None,
1018 }
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use super::*;
1024 use solana_sdk::{pubkey::Pubkey, signature::Signature};
1025
1026 #[test]
1027 fn test_merge_pumpfun_trade() {
1028 let metadata = EventMetadata {
1029 signature: Signature::default(),
1030 slot: 100,
1031 tx_index: 1,
1032 block_time_us: 1000,
1033 grpc_recv_us: 2000,
1034 recent_blockhash: None,
1035 };
1036
1037 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1039 metadata: metadata.clone(),
1040 bonding_curve: Pubkey::new_unique(),
1041 associated_bonding_curve: Pubkey::new_unique(),
1042 ..Default::default()
1043 });
1044
1045 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1047 metadata: metadata.clone(),
1048 mint: Pubkey::new_unique(),
1049 sol_amount: 1000,
1050 token_amount: 2000,
1051 is_buy: true,
1052 user: Pubkey::new_unique(),
1053 ..Default::default()
1054 });
1055
1056 merge_events(&mut base, inner);
1058
1059 if let DexEvent::PumpFunTrade(trade) = base {
1061 assert_eq!(trade.sol_amount, 1000);
1062 assert_eq!(trade.token_amount, 2000);
1063 assert!(trade.is_buy);
1064 assert_ne!(trade.bonding_curve, Pubkey::default());
1066 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
1067 } else {
1068 panic!("Expected PumpFunTrade event");
1069 }
1070 }
1071
1072 #[test]
1073 fn merge_preserves_instruction_context_when_log_tail_is_absent() {
1074 let metadata = EventMetadata {
1075 signature: Signature::default(),
1076 slot: 100,
1077 tx_index: 1,
1078 block_time_us: 1000,
1079 grpc_recv_us: 2000,
1080 recent_blockhash: None,
1081 };
1082 let quote_mint = Pubkey::new_unique();
1083 let associated_quote_user = Pubkey::new_unique();
1084
1085 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1086 metadata: metadata.clone(),
1087 ix_name: "buy_exact_quote_in".to_string(),
1088 quote_mint,
1089 spendable_quote_in: 1_000,
1090 min_tokens_out: 2_000,
1091 associated_quote_user,
1092 ..Default::default()
1093 });
1094
1095 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1096 metadata,
1097 sol_amount: 1_000,
1098 token_amount: 2_000,
1099 is_buy: true,
1100 ..Default::default()
1101 });
1102
1103 merge_events(&mut base, inner);
1104
1105 if let DexEvent::PumpFunTrade(t) = base {
1106 assert_eq!(t.sol_amount, 1_000);
1107 assert_eq!(t.token_amount, 2_000);
1108 assert_eq!(t.ix_name, "buy_exact_quote_in");
1109 assert_eq!(t.quote_mint, quote_mint);
1110 assert_eq!(t.spendable_quote_in, 1_000);
1111 assert_eq!(t.min_tokens_out, 2_000);
1112 assert_eq!(t.associated_quote_user, associated_quote_user);
1113 } else {
1114 panic!("Expected PumpFunTrade event");
1115 }
1116 }
1117
1118 #[test]
1119 fn merge_replaces_sol_quote_sentinel_with_real_quote_mint() {
1120 let quote_mint = Pubkey::new_unique();
1121 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1122 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1123 ..Default::default()
1124 });
1125
1126 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent { quote_mint, ..Default::default() });
1127
1128 merge_events(&mut base, inner);
1129
1130 if let DexEvent::PumpFunTrade(t) = base {
1131 assert_eq!(t.quote_mint, quote_mint);
1132 } else {
1133 panic!("Expected PumpFunTrade event");
1134 }
1135 }
1136
1137 #[test]
1138 fn merge_replaces_sol_quote_sentinel_with_wsol_mint() {
1139 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1140 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1141 ..Default::default()
1142 });
1143
1144 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1145 quote_mint: PUMPFUN_WSOL_QUOTE_MINT,
1146 ..Default::default()
1147 });
1148
1149 merge_events(&mut base, inner);
1150
1151 if let DexEvent::PumpFunTrade(t) = base {
1152 assert_eq!(t.quote_mint, PUMPFUN_WSOL_QUOTE_MINT);
1153 } else {
1154 panic!("Expected PumpFunTrade event");
1155 }
1156 }
1157
1158 #[test]
1159 fn test_can_merge() {
1160 let metadata = EventMetadata {
1161 signature: Signature::default(),
1162 slot: 100,
1163 tx_index: 1,
1164 block_time_us: 1000,
1165 grpc_recv_us: 2000,
1166 recent_blockhash: None,
1167 };
1168
1169 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1170 metadata: metadata.clone(),
1171 ..Default::default()
1172 });
1173
1174 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1175 metadata: metadata.clone(),
1176 ..Default::default()
1177 });
1178
1179 assert!(can_merge(&base, &inner));
1181
1182 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1184 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
1185 ..Default::default()
1186 });
1187
1188 assert!(!can_merge(&base, &different_sig));
1189 }
1190
1191 #[test]
1192 fn dlmm_position_event_keeps_instruction_only_fields() {
1193 let pool = Pubkey::new_unique();
1194 let position = Pubkey::new_unique();
1195 let owner = Pubkey::new_unique();
1196 let mut base = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1197 metadata: EventMetadata::default(),
1198 pool,
1199 position,
1200 owner,
1201 lower_bin_id: -42,
1202 width: 70,
1203 });
1204 let inner = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1205 metadata: EventMetadata::default(),
1206 pool,
1207 position,
1208 owner,
1209 lower_bin_id: 0,
1210 width: 0,
1211 });
1212
1213 assert!(try_merge_events(&mut base, inner).is_ok());
1214 let DexEvent::MeteoraDlmmCreatePosition(event) = base else { panic!("position") };
1215 assert_eq!(event.lower_bin_id, -42);
1216 assert_eq!(event.width, 70);
1217 }
1218
1219 #[test]
1220 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
1221 let metadata = EventMetadata {
1222 signature: Signature::default(),
1223 slot: 1,
1224 tx_index: 0,
1225 block_time_us: 0,
1226 grpc_recv_us: 0,
1227 recent_blockhash: None,
1228 };
1229 let fr = Pubkey::new_unique();
1230 let log_t =
1231 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
1232 let mut ix_t = log_t.clone();
1233 ix_t.fee_recipient = fr;
1234 ix_t.sol_amount = 777;
1235 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1236 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1237 match log_ev {
1238 DexEvent::PumpFunTrade(t) => {
1239 assert_eq!(t.fee_recipient, fr);
1240 assert_eq!(t.sol_amount, 50);
1241 }
1242 _ => panic!("expected trade"),
1243 }
1244 }
1245
1246 #[test]
1247 fn grpc_merge_keeps_log_trade_fields() {
1248 let metadata = EventMetadata {
1249 signature: Signature::default(),
1250 slot: 1,
1251 tx_index: 0,
1252 block_time_us: 0,
1253 grpc_recv_us: 0,
1254 recent_blockhash: None,
1255 };
1256 let log_t = PumpFunTradeEvent {
1257 metadata: metadata.clone(),
1258 mayhem_mode: true,
1259 sol_amount: 100,
1260 ..Default::default()
1261 };
1262 let mut ix_t = log_t.clone();
1263 ix_t.mayhem_mode = false;
1264 ix_t.sol_amount = 999;
1265
1266 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1267 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1268 match log_ev {
1269 DexEvent::PumpFunTrade(t) => {
1270 assert!(t.mayhem_mode);
1271 assert_eq!(t.sol_amount, 100);
1272 }
1273 _ => panic!("variant preserved"),
1274 }
1275 }
1276}