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_pumpfun_create_v2(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 (RaydiumClmmLiquidityChange(b), RaydiumClmmLiquidityChange(i)) => merge_generic(b, i),
81 (RaydiumClmmConfigChange(b), RaydiumClmmConfigChange(i)) => merge_generic(b, i),
82 (RaydiumClmmCreatePersonalPosition(b), RaydiumClmmCreatePersonalPosition(i)) => {
83 merge_generic(b, i)
84 }
85 (RaydiumClmmLiquidityCalculate(b), RaydiumClmmLiquidityCalculate(i)) => merge_generic(b, i),
86 (RaydiumClmmOpenLimitOrder(b), RaydiumClmmOpenLimitOrder(i)) => merge_generic(b, i),
87 (RaydiumClmmIncreaseLimitOrder(b), RaydiumClmmIncreaseLimitOrder(i)) => merge_generic(b, i),
88 (RaydiumClmmDecreaseLimitOrder(b), RaydiumClmmDecreaseLimitOrder(i)) => merge_generic(b, i),
89 (RaydiumClmmSettleLimitOrder(b), RaydiumClmmSettleLimitOrder(i)) => merge_generic(b, i),
90 (RaydiumClmmUpdateRewardInfos(b), RaydiumClmmUpdateRewardInfos(i)) => merge_generic(b, i),
91 (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
92 (RaydiumClmmOpenPosition(b), RaydiumClmmOpenPosition(i)) => merge_generic(b, i),
93 (RaydiumClmmClosePosition(b), RaydiumClmmClosePosition(i)) => merge_generic(b, i),
94 (RaydiumClmmOpenPositionWithTokenExtNft(b), RaydiumClmmOpenPositionWithTokenExtNft(i)) => {
95 merge_generic(b, i)
96 }
97 (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
98
99 (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_generic(b, i),
101 (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
102 (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
103 (RaydiumCpmmInitialize(b), RaydiumCpmmInitialize(i)) => merge_generic(b, i),
104
105 (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => merge_generic(b, i),
107 (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
108 (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
109 (RaydiumAmmV4Initialize2(b), RaydiumAmmV4Initialize2(i)) => merge_generic(b, i),
110 (RaydiumAmmV4WithdrawPnl(b), RaydiumAmmV4WithdrawPnl(i)) => merge_generic(b, i),
111
112 (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => merge_generic(b, i),
114 (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => {
115 merge_generic(b, i)
116 }
117 (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => {
118 merge_generic(b, i)
119 }
120 (OrcaWhirlpoolPoolInitialized(b), OrcaWhirlpoolPoolInitialized(i)) => merge_generic(b, i),
121
122 (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => merge_generic(b, i),
124 (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => merge_generic(b, i),
125 (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => merge_generic(b, i),
126 (MeteoraPoolsBootstrapLiquidity(b), MeteoraPoolsBootstrapLiquidity(i)) => {
127 merge_generic(b, i)
128 }
129 (MeteoraPoolsPoolCreated(b), MeteoraPoolsPoolCreated(i)) => merge_generic(b, i),
130 (MeteoraPoolsSetPoolFees(b), MeteoraPoolsSetPoolFees(i)) => merge_generic(b, i),
131
132 (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
134 (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
135 (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
136 (MeteoraDammV2InitializePool(b), MeteoraDammV2InitializePool(i)) => merge_generic(b, i),
137 (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
138 (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
139
140 (MeteoraDlmmSwap(b), MeteoraDlmmSwap(i)) => merge_generic(b, i),
142 (MeteoraDlmmAddLiquidity(b), MeteoraDlmmAddLiquidity(i)) => merge_generic(b, i),
143 (MeteoraDlmmRemoveLiquidity(b), MeteoraDlmmRemoveLiquidity(i)) => merge_generic(b, i),
144 (MeteoraDlmmInitializePool(b), MeteoraDlmmInitializePool(i)) => merge_generic(b, i),
145 (MeteoraDlmmInitializeBinArray(b), MeteoraDlmmInitializeBinArray(i)) => merge_generic(b, i),
146 (MeteoraDlmmCreatePosition(b), MeteoraDlmmCreatePosition(i)) => merge_generic(b, i),
147 (MeteoraDlmmClosePosition(b), MeteoraDlmmClosePosition(i)) => merge_generic(b, i),
148 (MeteoraDlmmClaimFee(b), MeteoraDlmmClaimFee(i)) => merge_generic(b, i),
149
150 (RaydiumLaunchlabTrade(b), RaydiumLaunchlabTrade(i)) => merge_generic(b, i),
152 (RaydiumLaunchlabPoolCreate(b), RaydiumLaunchlabPoolCreate(i)) => merge_generic(b, i),
153 (RaydiumLaunchlabMigrateAmm(b), RaydiumLaunchlabMigrateAmm(i)) => merge_generic(b, i),
154
155 _ => {}
157 }
158}
159
160#[inline(always)]
167fn merge_generic<T>(base: &mut T, inner: T) {
168 *base = inner;
169}
170
171#[inline(always)]
176fn put_pk_if_set(to: &mut Pubkey, from: Pubkey) {
177 if from != Pubkey::default() {
178 *to = from;
179 }
180}
181
182#[inline(always)]
183fn put_pumpfun_quote_mint_if_set(to: &mut Pubkey, from: Pubkey) {
184 let from = normalize_pumpfun_quote_mint(from);
185 if from != Pubkey::default()
186 && (*to == Pubkey::default()
187 || is_pumpfun_solscan_sol_quote_mint(*to)
188 || !is_pumpfun_solscan_sol_quote_mint(from))
189 {
190 *to = from;
191 }
192}
193
194#[inline(always)]
195fn put_u64_if_nonzero(to: &mut u64, from: u64) {
196 if from != 0 {
197 *to = from;
198 }
199}
200
201#[inline(always)]
202fn put_i64_if_nonzero(to: &mut i64, from: i64) {
203 if from != 0 {
204 *to = from;
205 }
206}
207
208#[inline(always)]
218fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
219 let leg = inner.sol_amount != 0 || inner.token_amount != 0;
220
221 put_pk_if_set(&mut base.mint, inner.mint);
222 put_pk_if_set(&mut base.user, inner.user);
223 put_pk_if_set(&mut base.fee_recipient, inner.fee_recipient);
224 put_pk_if_set(&mut base.creator, inner.creator);
225
226 if leg {
227 base.sol_amount = inner.sol_amount;
228 base.token_amount = inner.token_amount;
229 base.is_buy = inner.is_buy;
230 base.timestamp = inner.timestamp;
231 base.virtual_sol_reserves = inner.virtual_sol_reserves;
232 base.virtual_token_reserves = inner.virtual_token_reserves;
233 base.real_sol_reserves = inner.real_sol_reserves;
234 base.real_token_reserves = inner.real_token_reserves;
235 base.fee_basis_points = inner.fee_basis_points;
236 base.fee = inner.fee;
237 base.creator_fee_basis_points = inner.creator_fee_basis_points;
238 base.creator_fee = inner.creator_fee;
239 base.track_volume |= inner.track_volume;
240 base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
241 base.total_claimed_tokens = inner.total_claimed_tokens;
242 base.current_sol_volume = inner.current_sol_volume;
243 base.last_update_timestamp = inner.last_update_timestamp;
244 if !inner.ix_name.is_empty() {
245 base.ix_name = inner.ix_name;
246 }
247 base.mayhem_mode |= inner.mayhem_mode;
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_pumpfun_quote_mint_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 base.is_cashback_coin |= inner.is_cashback_coin;
260 } else {
261 put_u64_if_nonzero(&mut base.fee, inner.fee);
262 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
263 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
264 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
265 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
266 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
267 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
268 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
269 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
270 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
271 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
272 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
273 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
274 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
275 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
276 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
277 base.shareholders = inner.shareholders;
278 }
279 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
280 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
281 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
282 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
283 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
284 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
285 if !inner.ix_name.is_empty() {
286 base.ix_name = inner.ix_name;
287 }
288 base.track_volume |= inner.track_volume;
289 base.mayhem_mode |= inner.mayhem_mode;
290 base.is_cashback_coin |= inner.is_cashback_coin;
291 }
292 put_u64_if_nonzero(&mut base.amount, inner.amount);
293 put_u64_if_nonzero(&mut base.max_sol_cost, inner.max_sol_cost);
294 put_u64_if_nonzero(&mut base.min_sol_output, inner.min_sol_output);
295 put_u64_if_nonzero(&mut base.spendable_sol_in, inner.spendable_sol_in);
296 put_u64_if_nonzero(&mut base.spendable_quote_in, inner.spendable_quote_in);
297 put_u64_if_nonzero(&mut base.min_tokens_out, inner.min_tokens_out);
298 put_pk_if_set(&mut base.global, inner.global);
299 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
300 put_pk_if_set(&mut base.bonding_curve_v2, inner.bonding_curve_v2);
301 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
302 put_pk_if_set(&mut base.associated_user, inner.associated_user);
303 put_pk_if_set(&mut base.system_program, inner.system_program);
304 put_pk_if_set(&mut base.token_program, inner.token_program);
305 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
306 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
307 put_pk_if_set(&mut base.creator_vault, inner.creator_vault);
308 put_pk_if_set(&mut base.associated_quote_fee_recipient, inner.associated_quote_fee_recipient);
309 put_pk_if_set(&mut base.buyback_fee_recipient, inner.buyback_fee_recipient);
310 put_pk_if_set(
311 &mut base.associated_quote_buyback_fee_recipient,
312 inner.associated_quote_buyback_fee_recipient,
313 );
314 put_pk_if_set(&mut base.associated_quote_bonding_curve, inner.associated_quote_bonding_curve);
315 put_pk_if_set(&mut base.associated_quote_user, inner.associated_quote_user);
316 put_pk_if_set(&mut base.associated_creator_vault, inner.associated_creator_vault);
317 put_pk_if_set(&mut base.sharing_config, inner.sharing_config);
318 put_pk_if_set(&mut base.event_authority, inner.event_authority);
319 put_pk_if_set(&mut base.program, inner.program);
320 put_pk_if_set(&mut base.global_volume_accumulator, inner.global_volume_accumulator);
321 put_pk_if_set(&mut base.user_volume_accumulator, inner.user_volume_accumulator);
322 put_pk_if_set(
323 &mut base.associated_user_volume_accumulator,
324 inner.associated_user_volume_accumulator,
325 );
326 put_pk_if_set(&mut base.fee_config, inner.fee_config);
327 put_pk_if_set(&mut base.fee_program, inner.fee_program);
328 if base.account.is_none() {
329 base.account = inner.account;
330 }
331
332 base.is_created_buy |= inner.is_created_buy;
333 }
335
336#[inline(always)]
338fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
339 base.name = inner.name;
341 base.symbol = inner.symbol;
342 base.uri = inner.uri;
343 base.mint = inner.mint;
344 base.bonding_curve = inner.bonding_curve;
345 base.user = inner.user;
346 base.creator = inner.creator;
347 base.timestamp = inner.timestamp;
348 base.virtual_token_reserves = inner.virtual_token_reserves;
349 base.virtual_sol_reserves = inner.virtual_sol_reserves;
350 base.real_token_reserves = inner.real_token_reserves;
351 base.token_total_supply = inner.token_total_supply;
352 base.token_program = inner.token_program;
353 base.is_mayhem_mode = inner.is_mayhem_mode;
354 base.is_cashback_enabled = inner.is_cashback_enabled;
355 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
356 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
357 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
358 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
359}
360
361#[inline(always)]
363fn merge_pumpfun_create_v2(base: &mut PumpFunCreateV2TokenEvent, inner: PumpFunCreateV2TokenEvent) {
364 fill_str_if_empty(&mut base.name, &inner.name);
365 fill_str_if_empty(&mut base.symbol, &inner.symbol);
366 fill_str_if_empty(&mut base.uri, &inner.uri);
367 put_pk_if_set(&mut base.mint, inner.mint);
368 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
369 put_pk_if_set(&mut base.user, inner.user);
370 put_pk_if_set(&mut base.creator, inner.creator);
371 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
372 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
373 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
374 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
375 put_u64_if_nonzero(&mut base.token_total_supply, inner.token_total_supply);
376 put_pk_if_set(&mut base.token_program, inner.token_program);
377 base.is_mayhem_mode |= inner.is_mayhem_mode;
378 base.is_cashback_enabled |= inner.is_cashback_enabled;
379 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
380 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
381 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
382 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
383 put_pk_if_set(&mut base.mint_authority, inner.mint_authority);
384 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
385 put_pk_if_set(&mut base.global, inner.global);
386 put_pk_if_set(&mut base.system_program, inner.system_program);
387 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
388 put_pk_if_set(&mut base.mayhem_program_id, inner.mayhem_program_id);
389 put_pk_if_set(&mut base.global_params, inner.global_params);
390 put_pk_if_set(&mut base.sol_vault, inner.sol_vault);
391 put_pk_if_set(&mut base.mayhem_state, inner.mayhem_state);
392 put_pk_if_set(&mut base.mayhem_token_vault, inner.mayhem_token_vault);
393 put_pk_if_set(&mut base.event_authority, inner.event_authority);
394 put_pk_if_set(&mut base.program, inner.program);
395 put_pk_if_set(&mut base.observed_fee_recipient, inner.observed_fee_recipient);
396}
397
398#[inline(always)]
400fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
401 base.user = inner.user;
403 base.mint = inner.mint;
404 base.mint_amount = inner.mint_amount;
405 base.sol_amount = inner.sol_amount;
406 base.pool_migration_fee = inner.pool_migration_fee;
407 base.bonding_curve = inner.bonding_curve;
408 base.timestamp = inner.timestamp;
409 base.pool = inner.pool;
410}
411
412#[inline(always)]
413fn merge_pumpswap_buy(base: &mut PumpSwapBuyEvent, inner: PumpSwapBuyEvent) {
414 let ix = std::mem::take(base);
415 *base = inner;
416 merge_pumpswap_buy_log_preferred(base, ix);
417}
418
419#[inline(always)]
420fn merge_pumpswap_sell(base: &mut PumpSwapSellEvent, inner: PumpSwapSellEvent) {
421 let ix = std::mem::take(base);
422 *base = inner;
423 merge_pumpswap_sell_log_preferred(base, ix);
424}
425
426#[inline(always)]
437pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
438 if base.metadata().signature != inner.metadata().signature {
440 return false;
441 }
442
443 match (base, inner) {
445 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
447 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
448 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
449 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
450 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
451 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
452 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
453 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
454 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
455 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
456
457 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
459 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
460
461 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
463
464 _ => false,
466 }
467}
468
469#[inline(always)]
474fn fill_pk(to: &mut Pubkey, from: Pubkey) {
475 if *to == Pubkey::default() && from != Pubkey::default() {
476 *to = from;
477 }
478}
479
480#[inline(always)]
481fn fill_pumpfun_quote_mint(to: &mut Pubkey, from: Pubkey) {
482 let from = normalize_pumpfun_quote_mint(from);
483 if (*to == Pubkey::default() || is_pumpfun_solscan_sol_quote_mint(*to))
484 && from != Pubkey::default()
485 {
486 *to = from;
487 }
488}
489
490#[inline(always)]
491fn fill_str_if_empty(to: &mut String, from: &str) {
492 if to.is_empty() && !from.is_empty() {
493 to.push_str(from);
494 }
495}
496
497#[inline]
500fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
501 fill_pk(&mut log.global, ix.global);
502 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
503 fill_pk(&mut log.bonding_curve_v2, ix.bonding_curve_v2);
504 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
505 fill_pk(&mut log.associated_user, ix.associated_user);
506 fill_pk(&mut log.system_program, ix.system_program);
507 fill_pk(&mut log.token_program, ix.token_program);
508 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
509 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
510 fill_pk(&mut log.creator_vault, ix.creator_vault);
511 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
512 fill_pk(&mut log.creator, ix.creator);
513 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
514 fill_pk(&mut log.associated_quote_fee_recipient, ix.associated_quote_fee_recipient);
515 fill_pk(&mut log.buyback_fee_recipient, ix.buyback_fee_recipient);
516 fill_pk(
517 &mut log.associated_quote_buyback_fee_recipient,
518 ix.associated_quote_buyback_fee_recipient,
519 );
520 fill_pk(&mut log.associated_quote_bonding_curve, ix.associated_quote_bonding_curve);
521 fill_pk(&mut log.associated_quote_user, ix.associated_quote_user);
522 fill_pk(&mut log.associated_creator_vault, ix.associated_creator_vault);
523 fill_pk(&mut log.sharing_config, ix.sharing_config);
524 fill_pk(&mut log.event_authority, ix.event_authority);
525 fill_pk(&mut log.program, ix.program);
526 fill_pk(&mut log.global_volume_accumulator, ix.global_volume_accumulator);
527 fill_pk(&mut log.user_volume_accumulator, ix.user_volume_accumulator);
528 fill_pk(&mut log.associated_user_volume_accumulator, ix.associated_user_volume_accumulator);
529 fill_pk(&mut log.fee_config, ix.fee_config);
530 fill_pk(&mut log.fee_program, ix.fee_program);
531 if log.account.is_none() {
532 log.account = ix.account;
533 }
534 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
535 log.ix_name = ix.ix_name;
536 }
537 put_u64_if_nonzero(&mut log.amount, ix.amount);
538 put_u64_if_nonzero(&mut log.max_sol_cost, ix.max_sol_cost);
539 put_u64_if_nonzero(&mut log.min_sol_output, ix.min_sol_output);
540 put_u64_if_nonzero(&mut log.spendable_sol_in, ix.spendable_sol_in);
541 put_u64_if_nonzero(&mut log.spendable_quote_in, ix.spendable_quote_in);
542 put_u64_if_nonzero(&mut log.min_tokens_out, ix.min_tokens_out);
543 put_u64_if_nonzero(&mut log.quote_amount, ix.quote_amount);
544 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
545 put_u64_if_nonzero(&mut log.real_quote_reserves, ix.real_quote_reserves);
546 if !log.is_created_buy && ix.is_created_buy {
547 log.is_created_buy = true;
548 }
549}
550
551#[inline]
552fn merge_pumpfun_create_log_preferred(
553 log: &mut PumpFunCreateTokenEvent,
554 ix: PumpFunCreateTokenEvent,
555) {
556 fill_str_if_empty(&mut log.name, &ix.name);
557 fill_str_if_empty(&mut log.symbol, &ix.symbol);
558 fill_str_if_empty(&mut log.uri, &ix.uri);
559 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
560 fill_pk(&mut log.user, ix.user);
561 fill_pk(&mut log.creator, ix.creator);
562 fill_pk(&mut log.token_program, ix.token_program);
563 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
564 fill_pk(&mut log.quote_vault, ix.quote_vault);
565 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
566 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
567 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
568 log.ix_name = ix.ix_name;
569 }
570 log.is_mayhem_mode |= ix.is_mayhem_mode;
571 log.is_cashback_enabled |= ix.is_cashback_enabled;
572}
573
574#[inline]
575fn merge_pumpfun_create_v2_into_create_log_preferred(
576 log: &mut PumpFunCreateTokenEvent,
577 ix: PumpFunCreateV2TokenEvent,
578) {
579 fill_str_if_empty(&mut log.name, &ix.name);
580 fill_str_if_empty(&mut log.symbol, &ix.symbol);
581 fill_str_if_empty(&mut log.uri, &ix.uri);
582 fill_pk(&mut log.mint, ix.mint);
583 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
584 fill_pk(&mut log.user, ix.user);
585 fill_pk(&mut log.creator, ix.creator);
586 if log.timestamp == 0 && ix.timestamp != 0 {
587 log.timestamp = ix.timestamp;
588 }
589 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
590 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
591 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
592 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
593 fill_pk(&mut log.token_program, ix.token_program);
594 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
595 fill_pk(&mut log.quote_vault, ix.quote_vault);
596 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
597 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
598 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
599 log.ix_name = ix.ix_name;
600 }
601 log.is_mayhem_mode |= ix.is_mayhem_mode;
602 log.is_cashback_enabled |= ix.is_cashback_enabled;
603}
604
605#[inline]
606fn merge_pumpfun_create_into_create_v2_log_preferred(
607 log: &mut PumpFunCreateV2TokenEvent,
608 ix: PumpFunCreateTokenEvent,
609) {
610 fill_str_if_empty(&mut log.name, &ix.name);
611 fill_str_if_empty(&mut log.symbol, &ix.symbol);
612 fill_str_if_empty(&mut log.uri, &ix.uri);
613 fill_pk(&mut log.mint, ix.mint);
614 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
615 fill_pk(&mut log.user, ix.user);
616 fill_pk(&mut log.creator, ix.creator);
617 if log.timestamp == 0 && ix.timestamp != 0 {
618 log.timestamp = ix.timestamp;
619 }
620 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
621 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
622 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
623 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
624 fill_pk(&mut log.token_program, ix.token_program);
625 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
626 fill_pk(&mut log.quote_vault, ix.quote_vault);
627 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
628 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
629 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
630 log.ix_name = ix.ix_name;
631 }
632 log.is_mayhem_mode |= ix.is_mayhem_mode;
633 log.is_cashback_enabled |= ix.is_cashback_enabled;
634}
635
636#[inline]
637fn merge_pumpfun_create_v2_log_preferred(
638 log: &mut PumpFunCreateV2TokenEvent,
639 ix: PumpFunCreateV2TokenEvent,
640) {
641 fill_str_if_empty(&mut log.name, &ix.name);
642 fill_str_if_empty(&mut log.symbol, &ix.symbol);
643 fill_str_if_empty(&mut log.uri, &ix.uri);
644 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
645 fill_pk(&mut log.user, ix.user);
646 fill_pk(&mut log.creator, ix.creator);
647 fill_pk(&mut log.token_program, ix.token_program);
648 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
649 fill_pk(&mut log.quote_vault, ix.quote_vault);
650 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
651 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
652 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
653 log.ix_name = ix.ix_name;
654 }
655 fill_pk(&mut log.mint_authority, ix.mint_authority);
656 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
657 fill_pk(&mut log.global, ix.global);
658 fill_pk(&mut log.system_program, ix.system_program);
659 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
660 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
661 fill_pk(&mut log.global_params, ix.global_params);
662 fill_pk(&mut log.sol_vault, ix.sol_vault);
663 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
664 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
665 fill_pk(&mut log.event_authority, ix.event_authority);
666 fill_pk(&mut log.program, ix.program);
667 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
668}
669
670#[inline]
671fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
672 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
673 fill_pk(&mut log.pool, ix.pool);
674 fill_pk(&mut log.user, ix.user);
675}
676
677#[inline]
678fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
679 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
680 log.ix_name = ix.ix_name;
681 }
682}
683
684#[inline]
685fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
686 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
687 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
688 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
689 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
690 fill_pk(&mut log.coin_creator, ix.coin_creator);
691 fill_pk(&mut log.base_mint, ix.base_mint);
692 fill_pk(&mut log.quote_mint, ix.quote_mint);
693 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
694 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
695 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
696 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
697 fill_pk(&mut log.base_token_program, ix.base_token_program);
698 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
699 fill_pk(&mut log.pool_v2, ix.pool_v2);
700 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
701 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
702 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
703 log.ix_name = ix.ix_name;
704 }
705}
706
707#[inline]
708fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
709 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
710 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
711 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
712 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
713 fill_pk(&mut log.coin_creator, ix.coin_creator);
714 fill_pk(&mut log.base_mint, ix.base_mint);
715 fill_pk(&mut log.quote_mint, ix.quote_mint);
716 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
717 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
718 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
719 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
720 fill_pk(&mut log.base_token_program, ix.base_token_program);
721 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
722 fill_pk(&mut log.pool_v2, ix.pool_v2);
723 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
724 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
725}
726
727#[inline]
728fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
729 fill_pk(&mut log.token_account_0, ix.token_account_0);
730 fill_pk(&mut log.token_account_1, ix.token_account_1);
731 fill_pk(&mut log.sender, ix.sender);
732}
733
734#[inline]
735fn merge_raydium_amm_v4_swap_log_preferred(
736 log: &mut RaydiumAmmV4SwapEvent,
737 ix: RaydiumAmmV4SwapEvent,
738) {
739 fill_pk(&mut log.token_program, ix.token_program);
740 fill_pk(&mut log.amm_authority, ix.amm_authority);
741 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
742 if let Some(ref o) = ix.amm_target_orders {
743 if log.amm_target_orders.is_none() {
744 log.amm_target_orders = Some(*o);
745 }
746 }
747 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
748 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
749 fill_pk(&mut log.serum_program, ix.serum_program);
750 fill_pk(&mut log.serum_market, ix.serum_market);
751 fill_pk(&mut log.serum_bids, ix.serum_bids);
752 fill_pk(&mut log.serum_asks, ix.serum_asks);
753 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
754 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
755 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
756 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
757 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
758 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
759}
760
761#[inline]
762fn merge_pumpswap_create_pool_log_preferred(
763 log: &mut PumpSwapCreatePoolEvent,
764 ix: PumpSwapCreatePoolEvent,
765) {
766 fill_pk(&mut log.creator, ix.creator);
767 fill_pk(&mut log.pool, ix.pool);
768 fill_pk(&mut log.lp_mint, ix.lp_mint);
769 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
770 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
771 fill_pk(&mut log.coin_creator, ix.coin_creator);
772 log.is_mayhem_mode |= ix.is_mayhem_mode;
773 log.is_cashback_coin |= ix.is_cashback_coin;
774}
775
776#[inline]
777fn merge_pumpswap_liquidity_added_log_preferred(
778 log: &mut PumpSwapLiquidityAdded,
779 ix: PumpSwapLiquidityAdded,
780) {
781 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
782 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
783 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
784}
785
786#[inline]
787fn merge_pumpswap_liquidity_removed_log_preferred(
788 log: &mut PumpSwapLiquidityRemoved,
789 ix: PumpSwapLiquidityRemoved,
790) {
791 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
792 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
793 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
794}
795
796#[inline]
797fn merge_raydium_launchlab_pool_create_log_preferred(
798 log: &mut RaydiumLaunchlabPoolCreateEvent,
799 ix: RaydiumLaunchlabPoolCreateEvent,
800) {
801 fill_pk(&mut log.creator, ix.creator);
802 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
803 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
804 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
805}
806
807#[inline]
808fn merge_raydium_launchlab_migrate_amm_log_preferred(
809 log: &mut RaydiumLaunchlabMigrateAmmEvent,
810 ix: RaydiumLaunchlabMigrateAmmEvent,
811) {
812 fill_pk(&mut log.old_pool, ix.old_pool);
813 fill_pk(&mut log.new_pool, ix.new_pool);
814 fill_pk(&mut log.user, ix.user);
815}
816
817#[inline]
819fn merge_raydium_launchlab_trade_log_preferred(
820 _log: &mut RaydiumLaunchlabTradeEvent,
821 _ix: RaydiumLaunchlabTradeEvent,
822) {
823}
824
825#[inline]
826fn merge_meteora_dlmm_swap_log_preferred(
827 _log: &mut MeteoraDlmmSwapEvent,
828 _ix: MeteoraDlmmSwapEvent,
829) {
830}
831
832pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
838 use DexEvent::*;
839 match log {
840 PumpFunTrade(l) => {
841 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
842 merge_pumpfun_trade_log_preferred(l, i);
843 }
844 }
845 PumpFunBuy(l) => {
846 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
847 merge_pumpfun_trade_log_preferred(l, i);
848 }
849 }
850 PumpFunSell(l) => {
851 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
852 merge_pumpfun_trade_log_preferred(l, i);
853 }
854 }
855 PumpFunBuyExactSolIn(l) => {
856 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
857 merge_pumpfun_trade_log_preferred(l, i);
858 }
859 }
860 PumpFunCreate(l) => match ix {
861 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_log_preferred(l, i),
862 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_into_create_log_preferred(l, i),
863 _ => {}
864 },
865 PumpFunCreateV2(l) => match ix {
866 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_into_create_v2_log_preferred(l, i),
867 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_log_preferred(l, i),
868 _ => {}
869 },
870 PumpFunMigrate(l) => {
871 if let DexEvent::PumpFunMigrate(i) = ix {
872 merge_pumpfun_migrate_log_preferred(l, i);
873 }
874 }
875 PumpSwapTrade(l) => {
876 if let PumpSwapTrade(i) = ix {
877 merge_pumpswap_trade_log_preferred(l, i);
878 }
879 }
880 PumpSwapBuy(l) => {
881 if let PumpSwapBuy(i) = ix {
882 merge_pumpswap_buy_log_preferred(l, i);
883 }
884 }
885 PumpSwapSell(l) => {
886 if let PumpSwapSell(i) = ix {
887 merge_pumpswap_sell_log_preferred(l, i);
888 }
889 }
890 RaydiumClmmSwap(l) => {
891 if let RaydiumClmmSwap(i) = ix {
892 merge_raydium_clmm_swap_log_preferred(l, i);
893 }
894 }
895 RaydiumAmmV4Swap(l) => {
896 if let RaydiumAmmV4Swap(i) = ix {
897 merge_raydium_amm_v4_swap_log_preferred(l, i);
898 }
899 }
900 RaydiumLaunchlabTrade(l) => {
901 if let RaydiumLaunchlabTrade(i) = ix {
902 merge_raydium_launchlab_trade_log_preferred(l, i);
903 }
904 }
905 RaydiumLaunchlabPoolCreate(l) => {
906 if let RaydiumLaunchlabPoolCreate(i) = ix {
907 merge_raydium_launchlab_pool_create_log_preferred(l, i);
908 }
909 }
910 RaydiumLaunchlabMigrateAmm(l) => {
911 if let RaydiumLaunchlabMigrateAmm(i) = ix {
912 merge_raydium_launchlab_migrate_amm_log_preferred(l, i);
913 }
914 }
915 PumpSwapCreatePool(l) => {
916 if let PumpSwapCreatePool(i) = ix {
917 merge_pumpswap_create_pool_log_preferred(l, i);
918 }
919 }
920 PumpSwapLiquidityAdded(l) => {
921 if let PumpSwapLiquidityAdded(i) = ix {
922 merge_pumpswap_liquidity_added_log_preferred(l, i);
923 }
924 }
925 PumpSwapLiquidityRemoved(l) => {
926 if let PumpSwapLiquidityRemoved(i) = ix {
927 merge_pumpswap_liquidity_removed_log_preferred(l, i);
928 }
929 }
930 MeteoraDlmmSwap(l) => {
931 if let MeteoraDlmmSwap(i) = ix {
932 merge_meteora_dlmm_swap_log_preferred(l, i);
933 }
934 }
935 _ => {}
936 }
937}
938
939#[inline]
940fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
941 match ix {
942 DexEvent::PumpFunTrade(t)
943 | DexEvent::PumpFunBuy(t)
944 | DexEvent::PumpFunSell(t)
945 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
946 _ => None,
947 }
948}
949
950#[cfg(test)]
951mod tests {
952 use super::*;
953 use solana_sdk::{pubkey::Pubkey, signature::Signature};
954
955 #[test]
956 fn test_merge_pumpfun_trade() {
957 let metadata = EventMetadata {
958 signature: Signature::default(),
959 slot: 100,
960 tx_index: 1,
961 block_time_us: 1000,
962 grpc_recv_us: 2000,
963 recent_blockhash: None,
964 };
965
966 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
968 metadata: metadata.clone(),
969 bonding_curve: Pubkey::new_unique(),
970 associated_bonding_curve: Pubkey::new_unique(),
971 ..Default::default()
972 });
973
974 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
976 metadata: metadata.clone(),
977 mint: Pubkey::new_unique(),
978 sol_amount: 1000,
979 token_amount: 2000,
980 is_buy: true,
981 user: Pubkey::new_unique(),
982 ..Default::default()
983 });
984
985 merge_events(&mut base, inner);
987
988 if let DexEvent::PumpFunTrade(trade) = base {
990 assert_eq!(trade.sol_amount, 1000);
991 assert_eq!(trade.token_amount, 2000);
992 assert!(trade.is_buy);
993 assert_ne!(trade.bonding_curve, Pubkey::default());
995 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
996 } else {
997 panic!("Expected PumpFunTrade event");
998 }
999 }
1000
1001 #[test]
1002 fn merge_preserves_instruction_context_when_log_tail_is_absent() {
1003 let metadata = EventMetadata {
1004 signature: Signature::default(),
1005 slot: 100,
1006 tx_index: 1,
1007 block_time_us: 1000,
1008 grpc_recv_us: 2000,
1009 recent_blockhash: None,
1010 };
1011 let quote_mint = Pubkey::new_unique();
1012 let associated_quote_user = Pubkey::new_unique();
1013
1014 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1015 metadata: metadata.clone(),
1016 ix_name: "buy_exact_quote_in".to_string(),
1017 quote_mint,
1018 spendable_quote_in: 1_000,
1019 min_tokens_out: 2_000,
1020 associated_quote_user,
1021 ..Default::default()
1022 });
1023
1024 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1025 metadata,
1026 sol_amount: 1_000,
1027 token_amount: 2_000,
1028 is_buy: true,
1029 ..Default::default()
1030 });
1031
1032 merge_events(&mut base, inner);
1033
1034 if let DexEvent::PumpFunTrade(t) = base {
1035 assert_eq!(t.sol_amount, 1_000);
1036 assert_eq!(t.token_amount, 2_000);
1037 assert_eq!(t.ix_name, "buy_exact_quote_in");
1038 assert_eq!(t.quote_mint, quote_mint);
1039 assert_eq!(t.spendable_quote_in, 1_000);
1040 assert_eq!(t.min_tokens_out, 2_000);
1041 assert_eq!(t.associated_quote_user, associated_quote_user);
1042 } else {
1043 panic!("Expected PumpFunTrade event");
1044 }
1045 }
1046
1047 #[test]
1048 fn merge_replaces_sol_quote_sentinel_with_real_quote_mint() {
1049 let quote_mint = Pubkey::new_unique();
1050 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1051 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1052 ..Default::default()
1053 });
1054
1055 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent { quote_mint, ..Default::default() });
1056
1057 merge_events(&mut base, inner);
1058
1059 if let DexEvent::PumpFunTrade(t) = base {
1060 assert_eq!(t.quote_mint, quote_mint);
1061 } else {
1062 panic!("Expected PumpFunTrade event");
1063 }
1064 }
1065
1066 #[test]
1067 fn merge_replaces_sol_quote_sentinel_with_wsol_mint() {
1068 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1069 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1070 ..Default::default()
1071 });
1072
1073 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1074 quote_mint: PUMPFUN_WSOL_QUOTE_MINT,
1075 ..Default::default()
1076 });
1077
1078 merge_events(&mut base, inner);
1079
1080 if let DexEvent::PumpFunTrade(t) = base {
1081 assert_eq!(t.quote_mint, PUMPFUN_WSOL_QUOTE_MINT);
1082 } else {
1083 panic!("Expected PumpFunTrade event");
1084 }
1085 }
1086
1087 #[test]
1088 fn test_can_merge() {
1089 let metadata = EventMetadata {
1090 signature: Signature::default(),
1091 slot: 100,
1092 tx_index: 1,
1093 block_time_us: 1000,
1094 grpc_recv_us: 2000,
1095 recent_blockhash: None,
1096 };
1097
1098 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1099 metadata: metadata.clone(),
1100 ..Default::default()
1101 });
1102
1103 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1104 metadata: metadata.clone(),
1105 ..Default::default()
1106 });
1107
1108 assert!(can_merge(&base, &inner));
1110
1111 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1113 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
1114 ..Default::default()
1115 });
1116
1117 assert!(!can_merge(&base, &different_sig));
1118 }
1119
1120 #[test]
1121 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
1122 let metadata = EventMetadata {
1123 signature: Signature::default(),
1124 slot: 1,
1125 tx_index: 0,
1126 block_time_us: 0,
1127 grpc_recv_us: 0,
1128 recent_blockhash: None,
1129 };
1130 let fr = Pubkey::new_unique();
1131 let log_t =
1132 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
1133 let mut ix_t = log_t.clone();
1134 ix_t.fee_recipient = fr;
1135 ix_t.sol_amount = 777;
1136 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1137 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1138 match log_ev {
1139 DexEvent::PumpFunTrade(t) => {
1140 assert_eq!(t.fee_recipient, fr);
1141 assert_eq!(t.sol_amount, 50);
1142 }
1143 _ => panic!("expected trade"),
1144 }
1145 }
1146
1147 #[test]
1148 fn grpc_merge_keeps_log_trade_fields() {
1149 let metadata = EventMetadata {
1150 signature: Signature::default(),
1151 slot: 1,
1152 tx_index: 0,
1153 block_time_us: 0,
1154 grpc_recv_us: 0,
1155 recent_blockhash: None,
1156 };
1157 let log_t = PumpFunTradeEvent {
1158 metadata: metadata.clone(),
1159 mayhem_mode: true,
1160 sol_amount: 100,
1161 ..Default::default()
1162 };
1163 let mut ix_t = log_t.clone();
1164 ix_t.mayhem_mode = false;
1165 ix_t.sol_amount = 999;
1166
1167 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1168 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1169 match log_ev {
1170 DexEvent::PumpFunTrade(t) => {
1171 assert!(t.mayhem_mode);
1172 assert_eq!(t.sol_amount, 100);
1173 }
1174 _ => panic!("variant preserved"),
1175 }
1176 }
1177
1178 #[test]
1179 fn grpc_merge_create_preserves_appended_quote_accounts() {
1180 let quote_mint = Pubkey::new_unique();
1181 let quote_vault = Pubkey::new_unique();
1182 let quote_token_program = Pubkey::new_unique();
1183 let mut log_ev = DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1184 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1185 ..Default::default()
1186 });
1187 let ix_ev = DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1188 quote_mint,
1189 quote_vault,
1190 quote_token_program,
1191 ix_name: "create_v2".to_string(),
1192 ..Default::default()
1193 });
1194
1195 merge_grpc_instruction_into_log(&mut log_ev, ix_ev);
1196
1197 match log_ev {
1198 DexEvent::PumpFunCreate(c) => {
1199 assert_eq!(c.quote_mint, quote_mint);
1200 assert_eq!(c.quote_vault, quote_vault);
1201 assert_eq!(c.quote_token_program, quote_token_program);
1202 assert_eq!(c.ix_name, "create_v2");
1203 }
1204 _ => panic!("expected create"),
1205 }
1206 }
1207}