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_pumpswap_buy(b, i),
71 (PumpSwapSell(b), PumpSwapSell(i)) => merge_pumpswap_sell(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 if !inner.ix_name.is_empty() {
221 base.ix_name = inner.ix_name;
222 }
223 base.mayhem_mode |= inner.mayhem_mode;
224 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
225 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
226 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
227 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
228 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
229 base.shareholders = inner.shareholders;
230 }
231 put_pk_if_set(&mut base.quote_mint, inner.quote_mint);
232 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
233 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
234 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
235 base.is_cashback_coin |= inner.is_cashback_coin;
236 } else {
237 put_u64_if_nonzero(&mut base.fee, inner.fee);
238 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
239 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
240 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
241 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
242 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
243 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
244 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
245 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
246 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
247 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
248 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
249 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
250 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
251 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
252 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
253 base.shareholders = inner.shareholders;
254 }
255 put_pk_if_set(&mut base.quote_mint, inner.quote_mint);
256 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
257 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
258 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
259 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
260 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
261 if !inner.ix_name.is_empty() {
262 base.ix_name = inner.ix_name;
263 }
264 base.track_volume |= inner.track_volume;
265 base.mayhem_mode |= inner.mayhem_mode;
266 base.is_cashback_coin |= inner.is_cashback_coin;
267 }
268 put_u64_if_nonzero(&mut base.amount, inner.amount);
269 put_u64_if_nonzero(&mut base.max_sol_cost, inner.max_sol_cost);
270 put_u64_if_nonzero(&mut base.min_sol_output, inner.min_sol_output);
271 put_u64_if_nonzero(&mut base.spendable_sol_in, inner.spendable_sol_in);
272 put_u64_if_nonzero(&mut base.spendable_quote_in, inner.spendable_quote_in);
273 put_u64_if_nonzero(&mut base.min_tokens_out, inner.min_tokens_out);
274 put_pk_if_set(&mut base.global, inner.global);
275 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
276 put_pk_if_set(&mut base.bonding_curve_v2, inner.bonding_curve_v2);
277 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
278 put_pk_if_set(&mut base.associated_user, inner.associated_user);
279 put_pk_if_set(&mut base.system_program, inner.system_program);
280 put_pk_if_set(&mut base.token_program, inner.token_program);
281 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
282 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
283 put_pk_if_set(&mut base.creator_vault, inner.creator_vault);
284 put_pk_if_set(&mut base.associated_quote_fee_recipient, inner.associated_quote_fee_recipient);
285 put_pk_if_set(&mut base.buyback_fee_recipient, inner.buyback_fee_recipient);
286 put_pk_if_set(
287 &mut base.associated_quote_buyback_fee_recipient,
288 inner.associated_quote_buyback_fee_recipient,
289 );
290 put_pk_if_set(&mut base.associated_quote_bonding_curve, inner.associated_quote_bonding_curve);
291 put_pk_if_set(&mut base.associated_quote_user, inner.associated_quote_user);
292 put_pk_if_set(&mut base.associated_creator_vault, inner.associated_creator_vault);
293 put_pk_if_set(&mut base.sharing_config, inner.sharing_config);
294 put_pk_if_set(&mut base.event_authority, inner.event_authority);
295 put_pk_if_set(&mut base.program, inner.program);
296 put_pk_if_set(&mut base.global_volume_accumulator, inner.global_volume_accumulator);
297 put_pk_if_set(&mut base.user_volume_accumulator, inner.user_volume_accumulator);
298 put_pk_if_set(
299 &mut base.associated_user_volume_accumulator,
300 inner.associated_user_volume_accumulator,
301 );
302 put_pk_if_set(&mut base.fee_config, inner.fee_config);
303 put_pk_if_set(&mut base.fee_program, inner.fee_program);
304 if base.account.is_none() {
305 base.account = inner.account;
306 }
307
308 base.is_created_buy |= inner.is_created_buy;
309 }
311
312#[inline(always)]
314fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
315 base.name = inner.name;
317 base.symbol = inner.symbol;
318 base.uri = inner.uri;
319 base.mint = inner.mint;
320 base.bonding_curve = inner.bonding_curve;
321 base.user = inner.user;
322 base.creator = inner.creator;
323 base.timestamp = inner.timestamp;
324 base.virtual_token_reserves = inner.virtual_token_reserves;
325 base.virtual_sol_reserves = inner.virtual_sol_reserves;
326 base.real_token_reserves = inner.real_token_reserves;
327 base.token_total_supply = inner.token_total_supply;
328 base.token_program = inner.token_program;
329 base.is_mayhem_mode = inner.is_mayhem_mode;
330}
331
332#[inline(always)]
334fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
335 base.user = inner.user;
337 base.mint = inner.mint;
338 base.mint_amount = inner.mint_amount;
339 base.sol_amount = inner.sol_amount;
340 base.pool_migration_fee = inner.pool_migration_fee;
341 base.bonding_curve = inner.bonding_curve;
342 base.timestamp = inner.timestamp;
343 base.pool = inner.pool;
344}
345
346#[inline(always)]
347fn merge_pumpswap_buy(base: &mut PumpSwapBuyEvent, inner: PumpSwapBuyEvent) {
348 let ix = std::mem::take(base);
349 *base = inner;
350 merge_pumpswap_buy_log_preferred(base, ix);
351}
352
353#[inline(always)]
354fn merge_pumpswap_sell(base: &mut PumpSwapSellEvent, inner: PumpSwapSellEvent) {
355 let ix = std::mem::take(base);
356 *base = inner;
357 merge_pumpswap_sell_log_preferred(base, ix);
358}
359
360#[inline(always)]
371pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
372 if base.metadata().signature != inner.metadata().signature {
374 return false;
375 }
376
377 match (base, inner) {
379 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
381 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
382 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
383 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
384 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
385 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
386 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
387 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
388 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
389 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
390
391 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
393 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
394
395 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
397
398 _ => false,
400 }
401}
402
403#[inline(always)]
408fn fill_pk(to: &mut Pubkey, from: Pubkey) {
409 if *to == Pubkey::default() && from != Pubkey::default() {
410 *to = from;
411 }
412}
413
414#[inline(always)]
415fn fill_str_if_empty(to: &mut String, from: &str) {
416 if to.is_empty() && !from.is_empty() {
417 to.push_str(from);
418 }
419}
420
421#[inline]
424fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
425 fill_pk(&mut log.global, ix.global);
426 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
427 fill_pk(&mut log.bonding_curve_v2, ix.bonding_curve_v2);
428 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
429 fill_pk(&mut log.associated_user, ix.associated_user);
430 fill_pk(&mut log.system_program, ix.system_program);
431 fill_pk(&mut log.token_program, ix.token_program);
432 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
433 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
434 fill_pk(&mut log.creator_vault, ix.creator_vault);
435 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
436 fill_pk(&mut log.creator, ix.creator);
437 fill_pk(&mut log.quote_mint, ix.quote_mint);
438 fill_pk(&mut log.associated_quote_fee_recipient, ix.associated_quote_fee_recipient);
439 fill_pk(&mut log.buyback_fee_recipient, ix.buyback_fee_recipient);
440 fill_pk(
441 &mut log.associated_quote_buyback_fee_recipient,
442 ix.associated_quote_buyback_fee_recipient,
443 );
444 fill_pk(&mut log.associated_quote_bonding_curve, ix.associated_quote_bonding_curve);
445 fill_pk(&mut log.associated_quote_user, ix.associated_quote_user);
446 fill_pk(&mut log.associated_creator_vault, ix.associated_creator_vault);
447 fill_pk(&mut log.sharing_config, ix.sharing_config);
448 fill_pk(&mut log.event_authority, ix.event_authority);
449 fill_pk(&mut log.program, ix.program);
450 fill_pk(&mut log.global_volume_accumulator, ix.global_volume_accumulator);
451 fill_pk(&mut log.user_volume_accumulator, ix.user_volume_accumulator);
452 fill_pk(&mut log.associated_user_volume_accumulator, ix.associated_user_volume_accumulator);
453 fill_pk(&mut log.fee_config, ix.fee_config);
454 fill_pk(&mut log.fee_program, ix.fee_program);
455 if log.account.is_none() {
456 log.account = ix.account;
457 }
458 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
459 log.ix_name = ix.ix_name;
460 }
461 put_u64_if_nonzero(&mut log.amount, ix.amount);
462 put_u64_if_nonzero(&mut log.max_sol_cost, ix.max_sol_cost);
463 put_u64_if_nonzero(&mut log.min_sol_output, ix.min_sol_output);
464 put_u64_if_nonzero(&mut log.spendable_sol_in, ix.spendable_sol_in);
465 put_u64_if_nonzero(&mut log.spendable_quote_in, ix.spendable_quote_in);
466 put_u64_if_nonzero(&mut log.min_tokens_out, ix.min_tokens_out);
467 put_u64_if_nonzero(&mut log.quote_amount, ix.quote_amount);
468 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
469 put_u64_if_nonzero(&mut log.real_quote_reserves, ix.real_quote_reserves);
470 if !log.is_created_buy && ix.is_created_buy {
471 log.is_created_buy = true;
472 }
473}
474
475#[inline]
476fn merge_pumpfun_create_log_preferred(
477 log: &mut PumpFunCreateTokenEvent,
478 ix: PumpFunCreateTokenEvent,
479) {
480 fill_str_if_empty(&mut log.name, &ix.name);
481 fill_str_if_empty(&mut log.symbol, &ix.symbol);
482 fill_str_if_empty(&mut log.uri, &ix.uri);
483 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
484 fill_pk(&mut log.user, ix.user);
485 fill_pk(&mut log.creator, ix.creator);
486 fill_pk(&mut log.token_program, ix.token_program);
487}
488
489#[inline]
490fn merge_pumpfun_create_v2_log_preferred(
491 log: &mut PumpFunCreateV2TokenEvent,
492 ix: PumpFunCreateV2TokenEvent,
493) {
494 fill_str_if_empty(&mut log.name, &ix.name);
495 fill_str_if_empty(&mut log.symbol, &ix.symbol);
496 fill_str_if_empty(&mut log.uri, &ix.uri);
497 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
498 fill_pk(&mut log.user, ix.user);
499 fill_pk(&mut log.creator, ix.creator);
500 fill_pk(&mut log.token_program, ix.token_program);
501 fill_pk(&mut log.mint_authority, ix.mint_authority);
502 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
503 fill_pk(&mut log.global, ix.global);
504 fill_pk(&mut log.system_program, ix.system_program);
505 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
506 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
507 fill_pk(&mut log.global_params, ix.global_params);
508 fill_pk(&mut log.sol_vault, ix.sol_vault);
509 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
510 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
511 fill_pk(&mut log.event_authority, ix.event_authority);
512 fill_pk(&mut log.program, ix.program);
513 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
514}
515
516#[inline]
517fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
518 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
519 fill_pk(&mut log.pool, ix.pool);
520 fill_pk(&mut log.user, ix.user);
521}
522
523#[inline]
524fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
525 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
526 log.ix_name = ix.ix_name;
527 }
528}
529
530#[inline]
531fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
532 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
533 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
534 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
535 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
536 fill_pk(&mut log.coin_creator, ix.coin_creator);
537 fill_pk(&mut log.base_mint, ix.base_mint);
538 fill_pk(&mut log.quote_mint, ix.quote_mint);
539 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
540 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
541 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
542 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
543 fill_pk(&mut log.base_token_program, ix.base_token_program);
544 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
545 fill_pk(&mut log.pool_v2, ix.pool_v2);
546 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
547 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
548 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
549 log.ix_name = ix.ix_name;
550 }
551}
552
553#[inline]
554fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
555 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
556 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
557 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
558 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
559 fill_pk(&mut log.coin_creator, ix.coin_creator);
560 fill_pk(&mut log.base_mint, ix.base_mint);
561 fill_pk(&mut log.quote_mint, ix.quote_mint);
562 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
563 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
564 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
565 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
566 fill_pk(&mut log.base_token_program, ix.base_token_program);
567 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
568 fill_pk(&mut log.pool_v2, ix.pool_v2);
569 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
570 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
571}
572
573#[inline]
574fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
575 fill_pk(&mut log.token_account_0, ix.token_account_0);
576 fill_pk(&mut log.token_account_1, ix.token_account_1);
577 fill_pk(&mut log.sender, ix.sender);
578}
579
580#[inline]
581fn merge_raydium_amm_v4_swap_log_preferred(
582 log: &mut RaydiumAmmV4SwapEvent,
583 ix: RaydiumAmmV4SwapEvent,
584) {
585 fill_pk(&mut log.token_program, ix.token_program);
586 fill_pk(&mut log.amm_authority, ix.amm_authority);
587 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
588 if let Some(ref o) = ix.amm_target_orders {
589 if log.amm_target_orders.is_none() {
590 log.amm_target_orders = Some(*o);
591 }
592 }
593 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
594 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
595 fill_pk(&mut log.serum_program, ix.serum_program);
596 fill_pk(&mut log.serum_market, ix.serum_market);
597 fill_pk(&mut log.serum_bids, ix.serum_bids);
598 fill_pk(&mut log.serum_asks, ix.serum_asks);
599 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
600 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
601 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
602 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
603 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
604 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
605}
606
607#[inline]
608fn merge_pumpswap_create_pool_log_preferred(
609 log: &mut PumpSwapCreatePoolEvent,
610 ix: PumpSwapCreatePoolEvent,
611) {
612 fill_pk(&mut log.creator, ix.creator);
613 fill_pk(&mut log.pool, ix.pool);
614 fill_pk(&mut log.lp_mint, ix.lp_mint);
615 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
616 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
617 fill_pk(&mut log.coin_creator, ix.coin_creator);
618}
619
620#[inline]
621fn merge_pumpswap_liquidity_added_log_preferred(
622 log: &mut PumpSwapLiquidityAdded,
623 ix: PumpSwapLiquidityAdded,
624) {
625 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
626 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
627 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
628}
629
630#[inline]
631fn merge_pumpswap_liquidity_removed_log_preferred(
632 log: &mut PumpSwapLiquidityRemoved,
633 ix: PumpSwapLiquidityRemoved,
634) {
635 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
636 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
637 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
638}
639
640#[inline]
641fn merge_bonk_pool_create_log_preferred(log: &mut BonkPoolCreateEvent, ix: BonkPoolCreateEvent) {
642 fill_pk(&mut log.creator, ix.creator);
643 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
644 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
645 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
646}
647
648#[inline]
649fn merge_bonk_migrate_amm_log_preferred(log: &mut BonkMigrateAmmEvent, ix: BonkMigrateAmmEvent) {
650 fill_pk(&mut log.old_pool, ix.old_pool);
651 fill_pk(&mut log.new_pool, ix.new_pool);
652 fill_pk(&mut log.user, ix.user);
653}
654
655#[inline]
657fn merge_bonk_trade_log_preferred(_log: &mut BonkTradeEvent, _ix: BonkTradeEvent) {}
658
659#[inline]
660fn merge_meteora_dlmm_swap_log_preferred(
661 _log: &mut MeteoraDlmmSwapEvent,
662 _ix: MeteoraDlmmSwapEvent,
663) {
664}
665
666pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
672 use DexEvent::*;
673 match log {
674 PumpFunTrade(l) => {
675 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
676 merge_pumpfun_trade_log_preferred(l, i);
677 }
678 }
679 PumpFunBuy(l) => {
680 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
681 merge_pumpfun_trade_log_preferred(l, i);
682 }
683 }
684 PumpFunSell(l) => {
685 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
686 merge_pumpfun_trade_log_preferred(l, i);
687 }
688 }
689 PumpFunBuyExactSolIn(l) => {
690 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
691 merge_pumpfun_trade_log_preferred(l, i);
692 }
693 }
694 PumpFunCreate(l) => {
695 if let DexEvent::PumpFunCreate(i) = ix {
696 merge_pumpfun_create_log_preferred(l, i);
697 }
698 }
699 PumpFunCreateV2(l) => {
700 if let DexEvent::PumpFunCreateV2(i) = ix {
701 merge_pumpfun_create_v2_log_preferred(l, i);
702 }
703 }
704 PumpFunMigrate(l) => {
705 if let DexEvent::PumpFunMigrate(i) = ix {
706 merge_pumpfun_migrate_log_preferred(l, i);
707 }
708 }
709 PumpSwapTrade(l) => {
710 if let PumpSwapTrade(i) = ix {
711 merge_pumpswap_trade_log_preferred(l, i);
712 }
713 }
714 PumpSwapBuy(l) => {
715 if let PumpSwapBuy(i) = ix {
716 merge_pumpswap_buy_log_preferred(l, i);
717 }
718 }
719 PumpSwapSell(l) => {
720 if let PumpSwapSell(i) = ix {
721 merge_pumpswap_sell_log_preferred(l, i);
722 }
723 }
724 RaydiumClmmSwap(l) => {
725 if let RaydiumClmmSwap(i) = ix {
726 merge_raydium_clmm_swap_log_preferred(l, i);
727 }
728 }
729 RaydiumAmmV4Swap(l) => {
730 if let RaydiumAmmV4Swap(i) = ix {
731 merge_raydium_amm_v4_swap_log_preferred(l, i);
732 }
733 }
734 BonkTrade(l) => {
735 if let BonkTrade(i) = ix {
736 merge_bonk_trade_log_preferred(l, i);
737 }
738 }
739 BonkPoolCreate(l) => {
740 if let BonkPoolCreate(i) = ix {
741 merge_bonk_pool_create_log_preferred(l, i);
742 }
743 }
744 BonkMigrateAmm(l) => {
745 if let BonkMigrateAmm(i) = ix {
746 merge_bonk_migrate_amm_log_preferred(l, i);
747 }
748 }
749 PumpSwapCreatePool(l) => {
750 if let PumpSwapCreatePool(i) = ix {
751 merge_pumpswap_create_pool_log_preferred(l, i);
752 }
753 }
754 PumpSwapLiquidityAdded(l) => {
755 if let PumpSwapLiquidityAdded(i) = ix {
756 merge_pumpswap_liquidity_added_log_preferred(l, i);
757 }
758 }
759 PumpSwapLiquidityRemoved(l) => {
760 if let PumpSwapLiquidityRemoved(i) = ix {
761 merge_pumpswap_liquidity_removed_log_preferred(l, i);
762 }
763 }
764 MeteoraDlmmSwap(l) => {
765 if let MeteoraDlmmSwap(i) = ix {
766 merge_meteora_dlmm_swap_log_preferred(l, i);
767 }
768 }
769 _ => {}
770 }
771}
772
773#[inline]
774fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
775 match ix {
776 DexEvent::PumpFunTrade(t)
777 | DexEvent::PumpFunBuy(t)
778 | DexEvent::PumpFunSell(t)
779 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
780 _ => None,
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use super::*;
787 use solana_sdk::{pubkey::Pubkey, signature::Signature};
788
789 #[test]
790 fn test_merge_pumpfun_trade() {
791 let metadata = EventMetadata {
792 signature: Signature::default(),
793 slot: 100,
794 tx_index: 1,
795 block_time_us: 1000,
796 grpc_recv_us: 2000,
797 recent_blockhash: None,
798 };
799
800 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
802 metadata: metadata.clone(),
803 bonding_curve: Pubkey::new_unique(),
804 associated_bonding_curve: Pubkey::new_unique(),
805 ..Default::default()
806 });
807
808 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
810 metadata: metadata.clone(),
811 mint: Pubkey::new_unique(),
812 sol_amount: 1000,
813 token_amount: 2000,
814 is_buy: true,
815 user: Pubkey::new_unique(),
816 ..Default::default()
817 });
818
819 merge_events(&mut base, inner);
821
822 if let DexEvent::PumpFunTrade(trade) = base {
824 assert_eq!(trade.sol_amount, 1000);
825 assert_eq!(trade.token_amount, 2000);
826 assert!(trade.is_buy);
827 assert_ne!(trade.bonding_curve, Pubkey::default());
829 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
830 } else {
831 panic!("Expected PumpFunTrade event");
832 }
833 }
834
835 #[test]
836 fn merge_preserves_instruction_context_when_log_tail_is_absent() {
837 let metadata = EventMetadata {
838 signature: Signature::default(),
839 slot: 100,
840 tx_index: 1,
841 block_time_us: 1000,
842 grpc_recv_us: 2000,
843 recent_blockhash: None,
844 };
845 let quote_mint = Pubkey::new_unique();
846 let associated_quote_user = Pubkey::new_unique();
847
848 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
849 metadata: metadata.clone(),
850 ix_name: "buy_exact_quote_in_v2".to_string(),
851 quote_mint,
852 spendable_quote_in: 1_000,
853 min_tokens_out: 2_000,
854 associated_quote_user,
855 ..Default::default()
856 });
857
858 let inner = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
859 metadata,
860 sol_amount: 1_000,
861 token_amount: 2_000,
862 is_buy: true,
863 ..Default::default()
864 });
865
866 merge_events(&mut base, inner);
867
868 if let DexEvent::PumpFunTrade(t) = base {
869 assert_eq!(t.sol_amount, 1_000);
870 assert_eq!(t.token_amount, 2_000);
871 assert_eq!(t.ix_name, "buy_exact_quote_in_v2");
872 assert_eq!(t.quote_mint, quote_mint);
873 assert_eq!(t.spendable_quote_in, 1_000);
874 assert_eq!(t.min_tokens_out, 2_000);
875 assert_eq!(t.associated_quote_user, associated_quote_user);
876 } else {
877 panic!("Expected PumpFunTrade event");
878 }
879 }
880
881 #[test]
882 fn test_can_merge() {
883 let metadata = EventMetadata {
884 signature: Signature::default(),
885 slot: 100,
886 tx_index: 1,
887 block_time_us: 1000,
888 grpc_recv_us: 2000,
889 recent_blockhash: None,
890 };
891
892 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
893 metadata: metadata.clone(),
894 ..Default::default()
895 });
896
897 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
898 metadata: metadata.clone(),
899 ..Default::default()
900 });
901
902 assert!(can_merge(&base, &inner));
904
905 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
907 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
908 ..Default::default()
909 });
910
911 assert!(!can_merge(&base, &different_sig));
912 }
913
914 #[test]
915 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
916 let metadata = EventMetadata {
917 signature: Signature::default(),
918 slot: 1,
919 tx_index: 0,
920 block_time_us: 0,
921 grpc_recv_us: 0,
922 recent_blockhash: None,
923 };
924 let fr = Pubkey::new_unique();
925 let log_t =
926 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
927 let mut ix_t = log_t.clone();
928 ix_t.fee_recipient = fr;
929 ix_t.sol_amount = 777;
930 let mut log_ev = DexEvent::PumpFunTrade(log_t);
931 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
932 match log_ev {
933 DexEvent::PumpFunTrade(t) => {
934 assert_eq!(t.fee_recipient, fr);
935 assert_eq!(t.sol_amount, 50);
936 }
937 _ => panic!("expected trade"),
938 }
939 }
940
941 #[test]
942 fn grpc_merge_keeps_log_trade_fields() {
943 let metadata = EventMetadata {
944 signature: Signature::default(),
945 slot: 1,
946 tx_index: 0,
947 block_time_us: 0,
948 grpc_recv_us: 0,
949 recent_blockhash: None,
950 };
951 let log_t = PumpFunTradeEvent {
952 metadata: metadata.clone(),
953 mayhem_mode: true,
954 sol_amount: 100,
955 ..Default::default()
956 };
957 let mut ix_t = log_t.clone();
958 ix_t.mayhem_mode = false;
959 ix_t.sol_amount = 999;
960
961 let mut log_ev = DexEvent::PumpFunTrade(log_t);
962 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
963 match log_ev {
964 DexEvent::PumpFunTrade(t) => {
965 assert!(t.mayhem_mode);
966 assert_eq!(t.sol_amount, 100);
967 }
968 _ => panic!("variant preserved"),
969 }
970 }
971}