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