1use solana_sdk::pubkey::Pubkey;
13
14use crate::core::events::*;
15
16#[inline(always)]
28pub fn merge_events(base: &mut DexEvent, inner: DexEvent) {
29 let mut unmerged = None;
30 let _ = try_merge_events(base, inner, &mut unmerged);
31}
32
33#[inline(always)]
37pub fn try_merge_events(
38 base: &mut DexEvent,
39 inner: DexEvent,
40 unmerged: &mut Option<DexEvent>,
41) -> bool {
42 use DexEvent::*;
43
44 match (base, inner) {
45 (PumpFunTrade(b), PumpFunTrade(i))
47 | (PumpFunTrade(b), PumpFunBuy(i))
48 | (PumpFunTrade(b), PumpFunSell(i))
49 | (PumpFunTrade(b), PumpFunBuyExactSolIn(i))
50 | (PumpFunBuy(b), PumpFunTrade(i))
51 | (PumpFunBuy(b), PumpFunBuy(i))
52 | (PumpFunSell(b), PumpFunTrade(i))
53 | (PumpFunSell(b), PumpFunSell(i))
54 | (PumpFunBuyExactSolIn(b), PumpFunTrade(i))
55 | (PumpFunBuyExactSolIn(b), PumpFunBuyExactSolIn(i)) => merge_pumpfun_trade(b, i),
56
57 (PumpFunCreate(b), PumpFunCreate(i)) => merge_pumpfun_create(b, i),
58 (PumpFunCreateV2(b), PumpFunCreateV2(i)) => merge_pumpfun_create_v2(b, i),
59 (PumpFunMigrate(b), PumpFunMigrate(i)) => merge_pumpfun_migrate(b, i),
60 (PumpFunMigrateBondingCurveCreator(b), PumpFunMigrateBondingCurveCreator(i)) => {
61 merge_generic(b, i)
62 }
63
64 (PumpFeesCreateFeeSharingConfig(b), PumpFeesCreateFeeSharingConfig(i)) => {
66 merge_generic(b, i)
67 }
68 (PumpFeesInitializeFeeConfig(b), PumpFeesInitializeFeeConfig(i)) => merge_generic(b, i),
69 (PumpFeesResetFeeSharingConfig(b), PumpFeesResetFeeSharingConfig(i)) => merge_generic(b, i),
70 (PumpFeesRevokeFeeSharingAuthority(b), PumpFeesRevokeFeeSharingAuthority(i)) => {
71 merge_generic(b, i)
72 }
73 (PumpFeesTransferFeeSharingAuthority(b), PumpFeesTransferFeeSharingAuthority(i)) => {
74 merge_generic(b, i)
75 }
76 (PumpFeesUpdateAdmin(b), PumpFeesUpdateAdmin(i)) => merge_generic(b, i),
77 (PumpFeesUpdateFeeConfig(b), PumpFeesUpdateFeeConfig(i)) => merge_generic(b, i),
78 (PumpFeesUpdateFeeShares(b), PumpFeesUpdateFeeShares(i)) => merge_generic(b, i),
79 (PumpFeesUpsertFeeTiers(b), PumpFeesUpsertFeeTiers(i)) => merge_generic(b, i),
80
81 (PumpSwapTrade(b), PumpSwapTrade(i)) => merge_generic(b, i),
83 (PumpSwapBuy(b), PumpSwapBuy(i)) => merge_pumpswap_buy(b, i),
84 (PumpSwapSell(b), PumpSwapSell(i)) => merge_pumpswap_sell(b, i),
85 (PumpSwapCreatePool(b), PumpSwapCreatePool(i)) => merge_generic(b, i),
86 (PumpSwapLiquidityAdded(b), PumpSwapLiquidityAdded(i)) => merge_generic(b, i),
87 (PumpSwapLiquidityRemoved(b), PumpSwapLiquidityRemoved(i)) => merge_generic(b, i),
88
89 (RaydiumClmmSwap(b), RaydiumClmmSwap(i)) => merge_clmm_swap(b, i),
91 (RaydiumClmmIncreaseLiquidity(b), RaydiumClmmIncreaseLiquidity(i)) => merge_generic(b, i),
92 (RaydiumClmmDecreaseLiquidity(b), RaydiumClmmDecreaseLiquidity(i)) => merge_generic(b, i),
93 (RaydiumClmmLiquidityChange(b), RaydiumClmmLiquidityChange(i)) => merge_generic(b, i),
94 (RaydiumClmmConfigChange(b), RaydiumClmmConfigChange(i)) => merge_generic(b, i),
95 (RaydiumClmmCreatePersonalPosition(b), RaydiumClmmCreatePersonalPosition(i)) => {
96 merge_generic(b, i)
97 }
98 (RaydiumClmmLiquidityCalculate(b), RaydiumClmmLiquidityCalculate(i)) => merge_generic(b, i),
99 (RaydiumClmmOpenLimitOrder(b), RaydiumClmmOpenLimitOrder(i)) => merge_generic(b, i),
100 (RaydiumClmmIncreaseLimitOrder(b), RaydiumClmmIncreaseLimitOrder(i)) => merge_generic(b, i),
101 (RaydiumClmmDecreaseLimitOrder(b), RaydiumClmmDecreaseLimitOrder(i)) => merge_generic(b, i),
102 (RaydiumClmmSettleLimitOrder(b), RaydiumClmmSettleLimitOrder(i)) => merge_generic(b, i),
103 (RaydiumClmmUpdateRewardInfos(b), RaydiumClmmUpdateRewardInfos(i)) => merge_generic(b, i),
104 (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
105 (RaydiumClmmOpenPosition(b), RaydiumClmmOpenPosition(i)) => merge_generic(b, i),
106 (RaydiumClmmClosePosition(b), RaydiumClmmClosePosition(i)) => merge_generic(b, i),
107 (RaydiumClmmOpenPositionWithTokenExtNft(b), RaydiumClmmOpenPositionWithTokenExtNft(i)) => {
108 merge_generic(b, i)
109 }
110 (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
111
112 (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_cpmm_swap(b, i),
114 (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
115 (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
116 (RaydiumCpmmInitialize(b), RaydiumCpmmInitialize(i)) => merge_generic(b, i),
117
118 (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => {
120 let instruction = std::mem::replace(b, i);
121 merge_raydium_amm_v4_swap_log_preferred(b, instruction);
122 }
123 (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
124 (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
125 (RaydiumAmmV4Initialize2(b), RaydiumAmmV4Initialize2(i)) => merge_generic(b, i),
126 (RaydiumAmmV4WithdrawPnl(b), RaydiumAmmV4WithdrawPnl(i)) => merge_generic(b, i),
127
128 (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => {
130 let instruction = std::mem::replace(b, i);
131 merge_orca_swap_context(b, instruction);
132 }
133 (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => {
134 merge_generic(b, i)
135 }
136 (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => {
137 merge_generic(b, i)
138 }
139 (OrcaWhirlpoolPoolInitialized(b), OrcaWhirlpoolPoolInitialized(i)) => merge_generic(b, i),
140
141 (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => {
143 let instruction = std::mem::replace(b, i);
144 merge_meteora_pools_swap_context(b, instruction);
145 }
146 (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => {
147 let instruction = std::mem::replace(b, i);
148 merge_meteora_pools_add_context(b, instruction);
149 }
150 (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => {
151 let instruction = std::mem::replace(b, i);
152 merge_meteora_pools_remove_context(b, instruction);
153 }
154 (MeteoraPoolsBootstrapLiquidity(b), MeteoraPoolsBootstrapLiquidity(i)) => {
155 let instruction = std::mem::replace(b, i);
156 merge_meteora_pools_bootstrap_context(b, instruction);
157 }
158 (MeteoraPoolsPoolCreated(b), MeteoraPoolsPoolCreated(i)) => {
159 let instruction = std::mem::replace(b, i);
160 merge_meteora_pools_poolcreated_context(b, instruction);
161 }
162 (MeteoraPoolsSetPoolFees(b), MeteoraPoolsSetPoolFees(i)) => {
163 let instruction = std::mem::replace(b, i);
164 merge_meteora_pools_setpoolfees_context(b, instruction);
165 }
166
167 (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
169 (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
170 (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
171 (MeteoraDammV2InitializePool(b), MeteoraDammV2InitializePool(i)) => merge_generic(b, i),
172 (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
173 (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
174 (MeteoraDammV2UpdateDelegatePermission(b), MeteoraDammV2UpdateDelegatePermission(i)) => {
175 merge_generic(b, i)
176 }
177 (
178 MeteoraDammV2WithdrawDeadLiquidityReward(b),
179 MeteoraDammV2WithdrawDeadLiquidityReward(i),
180 ) => merge_generic(b, i),
181 (MeteoraDammV2CreateConfig(b), MeteoraDammV2CreateConfig(i)) => merge_generic(b, i),
182 (MeteoraDammV2CreateDynamicConfig(b), MeteoraDammV2CreateDynamicConfig(i)) => {
183 merge_generic(b, i)
184 }
185
186 (MeteoraDlmmSwap(b), MeteoraDlmmSwap(i)) => merge_dlmm_swap(b, i),
188 (MeteoraDlmmAddLiquidity(b), MeteoraDlmmAddLiquidity(i)) => merge_generic(b, i),
189 (MeteoraDlmmRemoveLiquidity(b), MeteoraDlmmRemoveLiquidity(i)) => merge_generic(b, i),
190 (MeteoraDlmmInitializePool(b), MeteoraDlmmInitializePool(i)) => {
191 merge_dlmm_initialize_pool(b, i)
192 }
193 (MeteoraDlmmInitializeBinArray(b), MeteoraDlmmInitializeBinArray(i)) => merge_generic(b, i),
194 (MeteoraDlmmCreatePosition(b), MeteoraDlmmCreatePosition(i)) => {
195 merge_dlmm_create_position(b, i)
196 }
197 (MeteoraDlmmClosePosition(b), MeteoraDlmmClosePosition(i)) => {
198 merge_dlmm_close_position(b, i)
199 }
200 (MeteoraDlmmClaimFee(b), MeteoraDlmmClaimFee(i)) => merge_generic(b, i),
201
202 (RaydiumLaunchlabTrade(b), RaydiumLaunchlabTrade(i)) => merge_generic(b, i),
204 (RaydiumLaunchlabPoolCreate(b), RaydiumLaunchlabPoolCreate(i)) => merge_generic(b, i),
205 (RaydiumLaunchlabMigrateAmm(b), RaydiumLaunchlabMigrateAmm(i)) => merge_generic(b, i),
206
207 (_, event) => {
209 *unmerged = Some(event);
210 return false;
211 }
212 }
213
214 true
215}
216
217#[inline(always)]
224fn merge_cpmm_swap(base: &mut RaydiumCpmmSwapEvent, inner: RaydiumCpmmSwapEvent) {
225 let instruction = std::mem::replace(base, inner);
226 merge_raydium_cpmm_swap_log_preferred(base, instruction);
227}
228
229#[inline]
230fn merge_raydium_cpmm_swap_log_preferred(log: &mut RaydiumCpmmSwapEvent, ix: RaydiumCpmmSwapEvent) {
231 if !ix.ix_name.is_empty() {
232 log.ix_name = ix.ix_name;
233 log.amount_in = ix.amount_in;
234 log.minimum_amount_out = ix.minimum_amount_out;
235 log.max_amount_in = ix.max_amount_in;
236 log.amount_out = ix.amount_out;
237 }
238 fill_pk(&mut log.payer, ix.payer);
239 fill_pk(&mut log.authority, ix.authority);
240 fill_pk(&mut log.input_token_account, ix.input_token_account);
241 fill_pk(&mut log.output_token_account, ix.output_token_account);
242 fill_pk(&mut log.amm_config, ix.amm_config);
243 fill_pk(&mut log.input_vault, ix.input_vault);
244 fill_pk(&mut log.output_vault, ix.output_vault);
245 fill_pk(&mut log.input_token_program, ix.input_token_program);
246 fill_pk(&mut log.output_token_program, ix.output_token_program);
247 fill_pk(&mut log.input_token_mint, ix.input_token_mint);
248 fill_pk(&mut log.output_token_mint, ix.output_token_mint);
249 fill_pk(&mut log.observation_state, ix.observation_state);
250}
251
252#[inline(always)]
253fn merge_clmm_swap(base: &mut RaydiumClmmSwapEvent, inner: RaydiumClmmSwapEvent) {
254 let instruction = std::mem::replace(base, inner);
255 merge_raydium_clmm_swap_log_preferred(base, instruction);
256}
257
258#[inline(always)]
259fn merge_generic<T>(base: &mut T, inner: T) {
260 *base = inner;
261}
262
263#[inline(always)]
264fn merge_dlmm_swap(base: &mut MeteoraDlmmSwapEvent, inner: MeteoraDlmmSwapEvent) {
265 let min_amount_out =
266 if inner.min_amount_out != 0 { inner.min_amount_out } else { base.min_amount_out };
267 let user_token_in = if inner.user_token_in != Pubkey::default() {
268 inner.user_token_in
269 } else {
270 base.user_token_in
271 };
272 let user_token_out = if inner.user_token_out != Pubkey::default() {
273 inner.user_token_out
274 } else {
275 base.user_token_out
276 };
277 *base = inner;
278 base.min_amount_out = min_amount_out;
279 base.user_token_in = user_token_in;
280 base.user_token_out = user_token_out;
281}
282
283#[inline(always)]
284fn merge_dlmm_initialize_pool(
285 base: &mut MeteoraDlmmInitializePoolEvent,
286 inner: MeteoraDlmmInitializePoolEvent,
287) {
288 let creator = base.creator;
289 let active_bin_id = base.active_bin_id;
290 *base = inner;
291 base.creator = creator;
292 base.active_bin_id = active_bin_id;
293}
294
295#[inline(always)]
296fn merge_dlmm_create_position(
297 base: &mut MeteoraDlmmCreatePositionEvent,
298 inner: MeteoraDlmmCreatePositionEvent,
299) {
300 let lower_bin_id = base.lower_bin_id;
301 let width = base.width;
302 *base = inner;
303 base.lower_bin_id = lower_bin_id;
304 base.width = width;
305}
306
307#[inline(always)]
308fn merge_dlmm_close_position(
309 base: &mut MeteoraDlmmClosePositionEvent,
310 inner: MeteoraDlmmClosePositionEvent,
311) {
312 let pool = base.pool;
313 *base = inner;
314 base.pool = pool;
315}
316
317#[inline(always)]
322fn put_pk_if_set(to: &mut Pubkey, from: Pubkey) {
323 if from != Pubkey::default() {
324 *to = from;
325 }
326}
327
328#[inline(always)]
329fn put_pumpfun_quote_mint_if_set(to: &mut Pubkey, from: Pubkey) {
330 let from = normalize_pumpfun_quote_mint(from);
331 if from != Pubkey::default()
332 && (*to == Pubkey::default()
333 || is_pumpfun_solscan_sol_quote_mint(*to)
334 || !is_pumpfun_solscan_sol_quote_mint(from))
335 {
336 *to = from;
337 }
338}
339
340#[inline(always)]
341fn put_u64_if_nonzero(to: &mut u64, from: u64) {
342 if from != 0 {
343 *to = from;
344 }
345}
346
347#[inline(always)]
348fn put_i64_if_nonzero(to: &mut i64, from: i64) {
349 if from != 0 {
350 *to = from;
351 }
352}
353
354#[inline(always)]
364fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
365 let leg = inner.sol_amount != 0 || inner.token_amount != 0;
366
367 put_pk_if_set(&mut base.mint, inner.mint);
368 put_pk_if_set(&mut base.user, inner.user);
369 put_pk_if_set(&mut base.fee_recipient, inner.fee_recipient);
370 put_pk_if_set(&mut base.creator, inner.creator);
371
372 if leg {
373 base.sol_amount = inner.sol_amount;
374 base.token_amount = inner.token_amount;
375 base.is_buy = inner.is_buy;
376 base.timestamp = inner.timestamp;
377 base.virtual_sol_reserves = inner.virtual_sol_reserves;
378 base.virtual_token_reserves = inner.virtual_token_reserves;
379 base.real_sol_reserves = inner.real_sol_reserves;
380 base.real_token_reserves = inner.real_token_reserves;
381 base.fee_basis_points = inner.fee_basis_points;
382 base.fee = inner.fee;
383 base.creator_fee_basis_points = inner.creator_fee_basis_points;
384 base.creator_fee = inner.creator_fee;
385 base.track_volume |= inner.track_volume;
386 base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
387 base.total_claimed_tokens = inner.total_claimed_tokens;
388 base.current_sol_volume = inner.current_sol_volume;
389 base.last_update_timestamp = inner.last_update_timestamp;
390 if !inner.ix_name.is_empty() {
391 base.ix_name = inner.ix_name;
392 }
393 base.mayhem_mode |= inner.mayhem_mode;
394 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
395 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
396 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
397 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
398 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
399 base.shareholders = inner.shareholders;
400 }
401 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
402 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
403 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
404 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
405 base.is_cashback_coin |= inner.is_cashback_coin;
406 base.holder_rewards_bps = inner.holder_rewards_bps;
407 base.holder_rewards = inner.holder_rewards;
408 } else {
409 put_u64_if_nonzero(&mut base.fee, inner.fee);
410 put_u64_if_nonzero(&mut base.creator_fee, inner.creator_fee);
411 put_u64_if_nonzero(&mut base.fee_basis_points, inner.fee_basis_points);
412 put_u64_if_nonzero(&mut base.creator_fee_basis_points, inner.creator_fee_basis_points);
413 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
414 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
415 put_u64_if_nonzero(&mut base.real_sol_reserves, inner.real_sol_reserves);
416 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
417 put_u64_if_nonzero(&mut base.total_unclaimed_tokens, inner.total_unclaimed_tokens);
418 put_u64_if_nonzero(&mut base.total_claimed_tokens, inner.total_claimed_tokens);
419 put_u64_if_nonzero(&mut base.current_sol_volume, inner.current_sol_volume);
420 put_u64_if_nonzero(&mut base.cashback_fee_basis_points, inner.cashback_fee_basis_points);
421 put_u64_if_nonzero(&mut base.cashback, inner.cashback);
422 put_u64_if_nonzero(&mut base.buyback_fee_basis_points, inner.buyback_fee_basis_points);
423 put_u64_if_nonzero(&mut base.buyback_fee, inner.buyback_fee);
424 if base.shareholders.is_empty() && !inner.shareholders.is_empty() {
425 base.shareholders = inner.shareholders;
426 }
427 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
428 put_u64_if_nonzero(&mut base.quote_amount, inner.quote_amount);
429 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
430 put_u64_if_nonzero(&mut base.real_quote_reserves, inner.real_quote_reserves);
431 put_u64_if_nonzero(&mut base.holder_rewards_bps, inner.holder_rewards_bps);
432 put_u64_if_nonzero(&mut base.holder_rewards, inner.holder_rewards);
433 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
434 put_i64_if_nonzero(&mut base.last_update_timestamp, inner.last_update_timestamp);
435 if !inner.ix_name.is_empty() {
436 base.ix_name = inner.ix_name;
437 }
438 base.track_volume |= inner.track_volume;
439 base.mayhem_mode |= inner.mayhem_mode;
440 base.is_cashback_coin |= inner.is_cashback_coin;
441 }
442 put_u64_if_nonzero(&mut base.amount, inner.amount);
443 put_u64_if_nonzero(&mut base.max_sol_cost, inner.max_sol_cost);
444 put_u64_if_nonzero(&mut base.min_sol_output, inner.min_sol_output);
445 put_u64_if_nonzero(&mut base.spendable_sol_in, inner.spendable_sol_in);
446 put_u64_if_nonzero(&mut base.spendable_quote_in, inner.spendable_quote_in);
447 put_u64_if_nonzero(&mut base.min_tokens_out, inner.min_tokens_out);
448 put_pk_if_set(&mut base.global, inner.global);
449 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
450 put_pk_if_set(&mut base.bonding_curve_v2, inner.bonding_curve_v2);
451 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
452 put_pk_if_set(&mut base.associated_user, inner.associated_user);
453 put_pk_if_set(&mut base.system_program, inner.system_program);
454 put_pk_if_set(&mut base.token_program, inner.token_program);
455 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
456 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
457 put_pk_if_set(&mut base.creator_vault, inner.creator_vault);
458 put_pk_if_set(&mut base.associated_quote_fee_recipient, inner.associated_quote_fee_recipient);
459 put_pk_if_set(&mut base.buyback_fee_recipient, inner.buyback_fee_recipient);
460 put_pk_if_set(
461 &mut base.associated_quote_buyback_fee_recipient,
462 inner.associated_quote_buyback_fee_recipient,
463 );
464 put_pk_if_set(&mut base.associated_quote_bonding_curve, inner.associated_quote_bonding_curve);
465 put_pk_if_set(&mut base.associated_quote_user, inner.associated_quote_user);
466 put_pk_if_set(&mut base.associated_creator_vault, inner.associated_creator_vault);
467 put_pk_if_set(&mut base.sharing_config, inner.sharing_config);
468 put_pk_if_set(&mut base.event_authority, inner.event_authority);
469 put_pk_if_set(&mut base.program, inner.program);
470 put_pk_if_set(&mut base.global_volume_accumulator, inner.global_volume_accumulator);
471 put_pk_if_set(&mut base.user_volume_accumulator, inner.user_volume_accumulator);
472 put_pk_if_set(
473 &mut base.associated_user_volume_accumulator,
474 inner.associated_user_volume_accumulator,
475 );
476 put_pk_if_set(&mut base.fee_config, inner.fee_config);
477 put_pk_if_set(&mut base.fee_program, inner.fee_program);
478 if base.account.is_none() {
479 base.account = inner.account;
480 }
481
482 base.is_created_buy |= inner.is_created_buy;
483 }
485
486#[inline(always)]
488fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
489 base.name = inner.name;
491 base.symbol = inner.symbol;
492 base.uri = inner.uri;
493 base.mint = inner.mint;
494 base.bonding_curve = inner.bonding_curve;
495 base.user = inner.user;
496 base.creator = inner.creator;
497 base.timestamp = inner.timestamp;
498 base.virtual_token_reserves = inner.virtual_token_reserves;
499 base.virtual_sol_reserves = inner.virtual_sol_reserves;
500 base.real_token_reserves = inner.real_token_reserves;
501 base.token_total_supply = inner.token_total_supply;
502 base.token_program = inner.token_program;
503 base.is_mayhem_mode = inner.is_mayhem_mode;
504 base.is_cashback_enabled = inner.is_cashback_enabled;
505 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
506 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
507 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
508 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
509 put_u64_if_nonzero(&mut base.creator_fee_bps, inner.creator_fee_bps);
510 base.is_holder_reward |= inner.is_holder_reward;
511}
512
513#[inline(always)]
515fn merge_pumpfun_create_v2(base: &mut PumpFunCreateV2TokenEvent, inner: PumpFunCreateV2TokenEvent) {
516 fill_str_if_empty(&mut base.name, &inner.name);
517 fill_str_if_empty(&mut base.symbol, &inner.symbol);
518 fill_str_if_empty(&mut base.uri, &inner.uri);
519 put_pk_if_set(&mut base.mint, inner.mint);
520 put_pk_if_set(&mut base.bonding_curve, inner.bonding_curve);
521 put_pk_if_set(&mut base.user, inner.user);
522 put_pk_if_set(&mut base.creator, inner.creator);
523 put_i64_if_nonzero(&mut base.timestamp, inner.timestamp);
524 put_u64_if_nonzero(&mut base.virtual_token_reserves, inner.virtual_token_reserves);
525 put_u64_if_nonzero(&mut base.virtual_sol_reserves, inner.virtual_sol_reserves);
526 put_u64_if_nonzero(&mut base.real_token_reserves, inner.real_token_reserves);
527 put_u64_if_nonzero(&mut base.token_total_supply, inner.token_total_supply);
528 put_pk_if_set(&mut base.token_program, inner.token_program);
529 base.is_mayhem_mode |= inner.is_mayhem_mode;
530 base.is_cashback_enabled |= inner.is_cashback_enabled;
531 put_pumpfun_quote_mint_if_set(&mut base.quote_mint, inner.quote_mint);
532 put_pk_if_set(&mut base.quote_vault, inner.quote_vault);
533 put_pk_if_set(&mut base.quote_token_program, inner.quote_token_program);
534 put_u64_if_nonzero(&mut base.virtual_quote_reserves, inner.virtual_quote_reserves);
535 put_pk_if_set(&mut base.mint_authority, inner.mint_authority);
536 put_pk_if_set(&mut base.associated_bonding_curve, inner.associated_bonding_curve);
537 put_pk_if_set(&mut base.global, inner.global);
538 put_pk_if_set(&mut base.system_program, inner.system_program);
539 put_pk_if_set(&mut base.associated_token_program, inner.associated_token_program);
540 put_pk_if_set(&mut base.mayhem_program_id, inner.mayhem_program_id);
541 put_pk_if_set(&mut base.global_params, inner.global_params);
542 put_pk_if_set(&mut base.sol_vault, inner.sol_vault);
543 put_pk_if_set(&mut base.mayhem_state, inner.mayhem_state);
544 put_pk_if_set(&mut base.mayhem_token_vault, inner.mayhem_token_vault);
545 put_pk_if_set(&mut base.event_authority, inner.event_authority);
546 put_pk_if_set(&mut base.program, inner.program);
547 put_pk_if_set(&mut base.observed_fee_recipient, inner.observed_fee_recipient);
548 put_u64_if_nonzero(&mut base.creator_fee_bps, inner.creator_fee_bps);
549 base.is_holder_reward |= inner.is_holder_reward;
550}
551
552#[inline(always)]
554fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
555 base.user = inner.user;
557 base.mint = inner.mint;
558 base.mint_amount = inner.mint_amount;
559 base.sol_amount = inner.sol_amount;
560 base.pool_migration_fee = inner.pool_migration_fee;
561 base.bonding_curve = inner.bonding_curve;
562 base.timestamp = inner.timestamp;
563 base.pool = inner.pool;
564}
565
566#[inline(always)]
567fn merge_pumpswap_buy(base: &mut PumpSwapBuyEvent, inner: PumpSwapBuyEvent) {
568 let ix = std::mem::take(base);
569 *base = inner;
570 merge_pumpswap_buy_log_preferred(base, ix);
571}
572
573#[inline(always)]
574fn merge_pumpswap_sell(base: &mut PumpSwapSellEvent, inner: PumpSwapSellEvent) {
575 let ix = std::mem::take(base);
576 *base = inner;
577 merge_pumpswap_sell_log_preferred(base, ix);
578}
579
580#[inline(always)]
591pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
592 if base.metadata().signature != inner.metadata().signature {
594 return false;
595 }
596
597 match (base, inner) {
599 (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
601 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
602 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
603 | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
604 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
605 | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
606 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
607 | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
608 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
609 | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
610
611 (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
613 (DexEvent::PumpFunCreateV2(_), DexEvent::PumpFunCreateV2(_)) => true,
614
615 (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
617
618 _ => false,
620 }
621}
622
623#[inline(always)]
628fn fill_pk(to: &mut Pubkey, from: Pubkey) {
629 if *to == Pubkey::default() && from != Pubkey::default() {
630 *to = from;
631 }
632}
633
634#[inline(always)]
635fn fill_pumpfun_quote_mint(to: &mut Pubkey, from: Pubkey) {
636 let from = normalize_pumpfun_quote_mint(from);
637 if (*to == Pubkey::default() || is_pumpfun_solscan_sol_quote_mint(*to))
638 && from != Pubkey::default()
639 {
640 *to = from;
641 }
642}
643
644#[inline(always)]
645fn fill_str_if_empty(to: &mut String, from: &str) {
646 if to.is_empty() && !from.is_empty() {
647 to.push_str(from);
648 }
649}
650
651#[inline]
654fn merge_pumpfun_trade_log_preferred(log: &mut PumpFunTradeEvent, ix: PumpFunTradeEvent) {
655 fill_pk(&mut log.global, ix.global);
656 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
657 fill_pk(&mut log.bonding_curve_v2, ix.bonding_curve_v2);
658 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
659 fill_pk(&mut log.associated_user, ix.associated_user);
660 fill_pk(&mut log.system_program, ix.system_program);
661 fill_pk(&mut log.token_program, ix.token_program);
662 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
663 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
664 fill_pk(&mut log.creator_vault, ix.creator_vault);
665 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
666 fill_pk(&mut log.creator, ix.creator);
667 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
668 fill_pk(&mut log.associated_quote_fee_recipient, ix.associated_quote_fee_recipient);
669 fill_pk(&mut log.buyback_fee_recipient, ix.buyback_fee_recipient);
670 fill_pk(
671 &mut log.associated_quote_buyback_fee_recipient,
672 ix.associated_quote_buyback_fee_recipient,
673 );
674 fill_pk(&mut log.associated_quote_bonding_curve, ix.associated_quote_bonding_curve);
675 fill_pk(&mut log.associated_quote_user, ix.associated_quote_user);
676 fill_pk(&mut log.associated_creator_vault, ix.associated_creator_vault);
677 fill_pk(&mut log.sharing_config, ix.sharing_config);
678 fill_pk(&mut log.event_authority, ix.event_authority);
679 fill_pk(&mut log.program, ix.program);
680 fill_pk(&mut log.global_volume_accumulator, ix.global_volume_accumulator);
681 fill_pk(&mut log.user_volume_accumulator, ix.user_volume_accumulator);
682 fill_pk(&mut log.associated_user_volume_accumulator, ix.associated_user_volume_accumulator);
683 fill_pk(&mut log.fee_config, ix.fee_config);
684 fill_pk(&mut log.fee_program, ix.fee_program);
685 if log.account.is_none() {
686 log.account = ix.account;
687 }
688 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
689 log.ix_name = ix.ix_name;
690 }
691 put_u64_if_nonzero(&mut log.amount, ix.amount);
692 put_u64_if_nonzero(&mut log.max_sol_cost, ix.max_sol_cost);
693 put_u64_if_nonzero(&mut log.min_sol_output, ix.min_sol_output);
694 put_u64_if_nonzero(&mut log.spendable_sol_in, ix.spendable_sol_in);
695 put_u64_if_nonzero(&mut log.spendable_quote_in, ix.spendable_quote_in);
696 put_u64_if_nonzero(&mut log.min_tokens_out, ix.min_tokens_out);
697 put_u64_if_nonzero(&mut log.quote_amount, ix.quote_amount);
698 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
699 put_u64_if_nonzero(&mut log.real_quote_reserves, ix.real_quote_reserves);
700 if !log.is_created_buy && ix.is_created_buy {
701 log.is_created_buy = true;
702 }
703}
704
705#[inline]
706fn merge_pumpfun_create_log_preferred(
707 log: &mut PumpFunCreateTokenEvent,
708 ix: PumpFunCreateTokenEvent,
709) {
710 fill_str_if_empty(&mut log.name, &ix.name);
711 fill_str_if_empty(&mut log.symbol, &ix.symbol);
712 fill_str_if_empty(&mut log.uri, &ix.uri);
713 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
714 fill_pk(&mut log.user, ix.user);
715 fill_pk(&mut log.creator, ix.creator);
716 fill_pk(&mut log.token_program, ix.token_program);
717 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
718 fill_pk(&mut log.quote_vault, ix.quote_vault);
719 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
720 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
721 if log.creator_fee_bps == 0 {
722 log.creator_fee_bps = ix.creator_fee_bps;
723 }
724 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
725 log.ix_name = ix.ix_name;
726 }
727 log.is_mayhem_mode |= ix.is_mayhem_mode;
728 log.is_cashback_enabled |= ix.is_cashback_enabled;
729 log.is_holder_reward |= ix.is_holder_reward;
730}
731
732#[inline]
733fn merge_pumpfun_create_v2_into_create_log_preferred(
734 log: &mut PumpFunCreateTokenEvent,
735 ix: PumpFunCreateV2TokenEvent,
736) {
737 fill_str_if_empty(&mut log.name, &ix.name);
738 fill_str_if_empty(&mut log.symbol, &ix.symbol);
739 fill_str_if_empty(&mut log.uri, &ix.uri);
740 fill_pk(&mut log.mint, ix.mint);
741 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
742 fill_pk(&mut log.user, ix.user);
743 fill_pk(&mut log.creator, ix.creator);
744 if log.timestamp == 0 && ix.timestamp != 0 {
745 log.timestamp = ix.timestamp;
746 }
747 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
748 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
749 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
750 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
751 fill_pk(&mut log.token_program, ix.token_program);
752 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
753 fill_pk(&mut log.quote_vault, ix.quote_vault);
754 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
755 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
756 if log.creator_fee_bps == 0 {
757 log.creator_fee_bps = ix.creator_fee_bps;
758 }
759 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
760 log.ix_name = ix.ix_name;
761 }
762 log.is_mayhem_mode |= ix.is_mayhem_mode;
763 log.is_cashback_enabled |= ix.is_cashback_enabled;
764 log.is_holder_reward |= ix.is_holder_reward;
765}
766
767#[inline]
768fn merge_pumpfun_create_into_create_v2_log_preferred(
769 log: &mut PumpFunCreateV2TokenEvent,
770 ix: PumpFunCreateTokenEvent,
771) {
772 fill_str_if_empty(&mut log.name, &ix.name);
773 fill_str_if_empty(&mut log.symbol, &ix.symbol);
774 fill_str_if_empty(&mut log.uri, &ix.uri);
775 fill_pk(&mut log.mint, ix.mint);
776 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
777 fill_pk(&mut log.user, ix.user);
778 fill_pk(&mut log.creator, ix.creator);
779 if log.timestamp == 0 && ix.timestamp != 0 {
780 log.timestamp = ix.timestamp;
781 }
782 put_u64_if_nonzero(&mut log.virtual_token_reserves, ix.virtual_token_reserves);
783 put_u64_if_nonzero(&mut log.virtual_sol_reserves, ix.virtual_sol_reserves);
784 put_u64_if_nonzero(&mut log.real_token_reserves, ix.real_token_reserves);
785 put_u64_if_nonzero(&mut log.token_total_supply, ix.token_total_supply);
786 fill_pk(&mut log.token_program, ix.token_program);
787 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
788 fill_pk(&mut log.quote_vault, ix.quote_vault);
789 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
790 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
791 if log.creator_fee_bps == 0 {
792 log.creator_fee_bps = ix.creator_fee_bps;
793 }
794 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
795 log.ix_name = ix.ix_name;
796 }
797 log.is_mayhem_mode |= ix.is_mayhem_mode;
798 log.is_cashback_enabled |= ix.is_cashback_enabled;
799 log.is_holder_reward |= ix.is_holder_reward;
800}
801
802#[inline]
803fn merge_pumpfun_create_v2_log_preferred(
804 log: &mut PumpFunCreateV2TokenEvent,
805 ix: PumpFunCreateV2TokenEvent,
806) {
807 fill_str_if_empty(&mut log.name, &ix.name);
808 fill_str_if_empty(&mut log.symbol, &ix.symbol);
809 fill_str_if_empty(&mut log.uri, &ix.uri);
810 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
811 fill_pk(&mut log.user, ix.user);
812 fill_pk(&mut log.creator, ix.creator);
813 fill_pk(&mut log.token_program, ix.token_program);
814 fill_pumpfun_quote_mint(&mut log.quote_mint, ix.quote_mint);
815 fill_pk(&mut log.quote_vault, ix.quote_vault);
816 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
817 put_u64_if_nonzero(&mut log.virtual_quote_reserves, ix.virtual_quote_reserves);
818 if log.creator_fee_bps == 0 {
819 log.creator_fee_bps = ix.creator_fee_bps;
820 }
821 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
822 log.ix_name = ix.ix_name;
823 }
824 fill_pk(&mut log.mint_authority, ix.mint_authority);
825 fill_pk(&mut log.associated_bonding_curve, ix.associated_bonding_curve);
826 fill_pk(&mut log.global, ix.global);
827 fill_pk(&mut log.system_program, ix.system_program);
828 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
829 fill_pk(&mut log.mayhem_program_id, ix.mayhem_program_id);
830 fill_pk(&mut log.global_params, ix.global_params);
831 fill_pk(&mut log.sol_vault, ix.sol_vault);
832 fill_pk(&mut log.mayhem_state, ix.mayhem_state);
833 fill_pk(&mut log.mayhem_token_vault, ix.mayhem_token_vault);
834 fill_pk(&mut log.event_authority, ix.event_authority);
835 fill_pk(&mut log.program, ix.program);
836 fill_pk(&mut log.observed_fee_recipient, ix.observed_fee_recipient);
837 log.is_holder_reward |= ix.is_holder_reward;
838}
839
840#[inline]
841fn merge_pumpfun_migrate_log_preferred(log: &mut PumpFunMigrateEvent, ix: PumpFunMigrateEvent) {
842 fill_pk(&mut log.bonding_curve, ix.bonding_curve);
843 fill_pk(&mut log.pool, ix.pool);
844 fill_pk(&mut log.user, ix.user);
845}
846
847#[inline]
848fn merge_pumpswap_trade_log_preferred(log: &mut PumpSwapTradeEvent, ix: PumpSwapTradeEvent) {
849 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
850 log.ix_name = ix.ix_name;
851 }
852}
853
854#[inline]
855fn merge_pumpswap_buy_log_preferred(log: &mut PumpSwapBuyEvent, ix: PumpSwapBuyEvent) {
856 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
857 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
858 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
859 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
860 fill_pk(&mut log.coin_creator, ix.coin_creator);
861 fill_pk(&mut log.base_mint, ix.base_mint);
862 fill_pk(&mut log.quote_mint, ix.quote_mint);
863 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
864 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
865 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
866 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
867 fill_pk(&mut log.base_token_program, ix.base_token_program);
868 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
869 fill_pk(&mut log.pool_v2, ix.pool_v2);
870 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
871 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
872 if log.ix_name.is_empty() && !ix.ix_name.is_empty() {
873 log.ix_name = ix.ix_name;
874 }
875}
876
877#[inline]
878fn merge_pumpswap_sell_log_preferred(log: &mut PumpSwapSellEvent, ix: PumpSwapSellEvent) {
879 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
880 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
881 fill_pk(&mut log.protocol_fee_recipient, ix.protocol_fee_recipient);
882 fill_pk(&mut log.protocol_fee_recipient_token_account, ix.protocol_fee_recipient_token_account);
883 fill_pk(&mut log.coin_creator, ix.coin_creator);
884 fill_pk(&mut log.base_mint, ix.base_mint);
885 fill_pk(&mut log.quote_mint, ix.quote_mint);
886 fill_pk(&mut log.pool_base_token_account, ix.pool_base_token_account);
887 fill_pk(&mut log.pool_quote_token_account, ix.pool_quote_token_account);
888 fill_pk(&mut log.coin_creator_vault_ata, ix.coin_creator_vault_ata);
889 fill_pk(&mut log.coin_creator_vault_authority, ix.coin_creator_vault_authority);
890 fill_pk(&mut log.base_token_program, ix.base_token_program);
891 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
892 fill_pk(&mut log.pool_v2, ix.pool_v2);
893 fill_pk(&mut log.fee_recipient, ix.fee_recipient);
894 fill_pk(&mut log.fee_recipient_quote_token_account, ix.fee_recipient_quote_token_account);
895}
896
897#[inline]
898fn merge_raydium_clmm_swap_log_preferred(log: &mut RaydiumClmmSwapEvent, ix: RaydiumClmmSwapEvent) {
899 if !ix.ix_name.is_empty() {
900 log.ix_name = ix.ix_name;
901 log.amount = ix.amount;
902 log.other_amount_threshold = ix.other_amount_threshold;
903 log.sqrt_price_limit_x64 = ix.sqrt_price_limit_x64;
904 log.is_base_input = ix.is_base_input;
905 }
906 fill_pk(&mut log.token_account_0, ix.token_account_0);
907 fill_pk(&mut log.token_account_1, ix.token_account_1);
908 fill_pk(&mut log.input_token_account, ix.input_token_account);
909 fill_pk(&mut log.output_token_account, ix.output_token_account);
910 fill_pk(&mut log.sender, ix.sender);
911 fill_pk(&mut log.amm_config, ix.amm_config);
912 fill_pk(&mut log.input_vault, ix.input_vault);
913 fill_pk(&mut log.output_vault, ix.output_vault);
914 fill_pk(&mut log.observation_state, ix.observation_state);
915 fill_pk(&mut log.input_mint, ix.input_mint);
916 fill_pk(&mut log.output_mint, ix.output_mint);
917 if log.tick_array_bitmap_extension.is_none() {
918 log.tick_array_bitmap_extension = ix.tick_array_bitmap_extension;
919 }
920 if log.tick_arrays.is_empty() && !ix.tick_arrays.is_empty() {
921 log.tick_arrays = ix.tick_arrays;
922 }
923}
924
925#[inline]
926fn merge_raydium_amm_v4_swap_log_preferred(
927 log: &mut RaydiumAmmV4SwapEvent,
928 ix: RaydiumAmmV4SwapEvent,
929) {
930 if !ix.ix_name.is_empty() {
931 log.ix_name = ix.ix_name;
932 log.instruction_amount_in = ix.instruction_amount_in;
933 log.instruction_amount_out = ix.instruction_amount_out;
934 log.minimum_amount_out = ix.minimum_amount_out;
935 log.max_amount_in = ix.max_amount_in;
936 }
937 fill_pk(&mut log.token_program, ix.token_program);
938 fill_pk(&mut log.amm_authority, ix.amm_authority);
939 fill_pk(&mut log.amm_open_orders, ix.amm_open_orders);
940 if let Some(ref o) = ix.amm_target_orders {
941 if log.amm_target_orders.is_none() {
942 log.amm_target_orders = Some(*o);
943 }
944 }
945 fill_pk(&mut log.pool_coin_token_account, ix.pool_coin_token_account);
946 fill_pk(&mut log.pool_pc_token_account, ix.pool_pc_token_account);
947 fill_pk(&mut log.serum_program, ix.serum_program);
948 fill_pk(&mut log.serum_market, ix.serum_market);
949 fill_pk(&mut log.serum_bids, ix.serum_bids);
950 fill_pk(&mut log.serum_asks, ix.serum_asks);
951 fill_pk(&mut log.serum_event_queue, ix.serum_event_queue);
952 fill_pk(&mut log.serum_coin_vault_account, ix.serum_coin_vault_account);
953 fill_pk(&mut log.serum_pc_vault_account, ix.serum_pc_vault_account);
954 fill_pk(&mut log.serum_vault_signer, ix.serum_vault_signer);
955 fill_pk(&mut log.user_source_token_account, ix.user_source_token_account);
956 fill_pk(&mut log.user_destination_token_account, ix.user_destination_token_account);
957 fill_pk(&mut log.user_source_owner, ix.user_source_owner);
958 fill_pk(&mut log.amm, ix.amm);
959}
960
961#[inline]
962fn merge_pumpswap_create_pool_log_preferred(
963 log: &mut PumpSwapCreatePoolEvent,
964 ix: PumpSwapCreatePoolEvent,
965) {
966 fill_pk(&mut log.creator, ix.creator);
967 fill_pk(&mut log.pool, ix.pool);
968 fill_pk(&mut log.lp_mint, ix.lp_mint);
969 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
970 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
971 fill_pk(&mut log.coin_creator, ix.coin_creator);
972 log.is_mayhem_mode |= ix.is_mayhem_mode;
973 log.is_cashback_coin |= ix.is_cashback_coin;
974 if log.creator_fee_bps == 0 {
975 log.creator_fee_bps = ix.creator_fee_bps;
976 }
977 log.can_edit_creator_fee |= ix.can_edit_creator_fee;
978 log.is_holder_reward |= ix.is_holder_reward;
979}
980
981#[inline]
982fn merge_pumpswap_liquidity_added_log_preferred(
983 log: &mut PumpSwapLiquidityAdded,
984 ix: PumpSwapLiquidityAdded,
985) {
986 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
987 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
988 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
989}
990
991#[inline]
992fn merge_pumpswap_liquidity_removed_log_preferred(
993 log: &mut PumpSwapLiquidityRemoved,
994 ix: PumpSwapLiquidityRemoved,
995) {
996 fill_pk(&mut log.user_base_token_account, ix.user_base_token_account);
997 fill_pk(&mut log.user_quote_token_account, ix.user_quote_token_account);
998 fill_pk(&mut log.user_pool_token_account, ix.user_pool_token_account);
999}
1000
1001#[inline]
1002fn merge_raydium_launchlab_pool_create_log_preferred(
1003 log: &mut RaydiumLaunchlabPoolCreateEvent,
1004 ix: RaydiumLaunchlabPoolCreateEvent,
1005) {
1006 fill_pk(&mut log.payer, ix.payer);
1007 fill_pk(&mut log.creator, ix.creator);
1008 fill_pk(&mut log.global_config, ix.global_config);
1009 fill_pk(&mut log.platform_config, ix.platform_config);
1010 fill_pk(&mut log.base_mint, ix.base_mint);
1011 fill_pk(&mut log.quote_mint, ix.quote_mint);
1012 fill_pk(&mut log.base_vault, ix.base_vault);
1013 fill_pk(&mut log.quote_vault, ix.quote_vault);
1014 fill_pk(&mut log.base_token_program, ix.base_token_program);
1015 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
1016 fill_str_if_empty(&mut log.base_mint_param.name, &ix.base_mint_param.name);
1017 fill_str_if_empty(&mut log.base_mint_param.symbol, &ix.base_mint_param.symbol);
1018 fill_str_if_empty(&mut log.base_mint_param.uri, &ix.base_mint_param.uri);
1019}
1020
1021#[inline]
1022fn merge_raydium_launchlab_migrate_amm_log_preferred(
1023 log: &mut RaydiumLaunchlabMigrateAmmEvent,
1024 ix: RaydiumLaunchlabMigrateAmmEvent,
1025) {
1026 fill_pk(&mut log.old_pool, ix.old_pool);
1027 fill_pk(&mut log.new_pool, ix.new_pool);
1028 fill_pk(&mut log.user, ix.user);
1029}
1030
1031#[inline]
1032fn merge_raydium_launchlab_trade_log_preferred(
1033 log: &mut RaydiumLaunchlabTradeEvent,
1034 ix: RaydiumLaunchlabTradeEvent,
1035) {
1036 fill_pk(&mut log.user, ix.user);
1037 fill_pk(&mut log.global_config, ix.global_config);
1038 fill_pk(&mut log.platform_config, ix.platform_config);
1039 fill_pk(&mut log.user_base_token, ix.user_base_token);
1040 fill_pk(&mut log.user_quote_token, ix.user_quote_token);
1041 fill_pk(&mut log.base_vault, ix.base_vault);
1042 fill_pk(&mut log.quote_vault, ix.quote_vault);
1043 fill_pk(&mut log.base_mint, ix.base_mint);
1044 fill_pk(&mut log.quote_mint, ix.quote_mint);
1045 fill_pk(&mut log.base_token_program, ix.base_token_program);
1046 fill_pk(&mut log.quote_token_program, ix.quote_token_program);
1047 fill_pk(&mut log.system_program, ix.system_program);
1048 fill_pk(&mut log.platform_associated_account, ix.platform_associated_account);
1049 fill_pk(&mut log.creator_associated_account, ix.creator_associated_account);
1050}
1051
1052#[inline]
1053fn merge_meteora_dlmm_swap_log_preferred(log: &mut MeteoraDlmmSwapEvent, ix: MeteoraDlmmSwapEvent) {
1054 fill_pk(&mut log.user_token_in, ix.user_token_in);
1055 fill_pk(&mut log.user_token_out, ix.user_token_out);
1056 if log.min_amount_out == 0 {
1057 log.min_amount_out = ix.min_amount_out;
1058 }
1059}
1060
1061fn merge_meteora_pools_add_context(
1067 log: &mut MeteoraPoolsAddLiquidityEvent,
1068 ix: MeteoraPoolsAddLiquidityEvent,
1069) {
1070 if !ix.ix_name.is_empty() {
1071 log.ix_name = ix.ix_name;
1072 log.pool_token_amount = ix.pool_token_amount;
1073 log.maximum_token_a_amount = ix.maximum_token_a_amount;
1074 log.maximum_token_b_amount = ix.maximum_token_b_amount;
1075 log.minimum_pool_token_amount = ix.minimum_pool_token_amount;
1076 log.token_a_in_amount = ix.token_a_in_amount;
1077 log.token_b_in_amount = ix.token_b_in_amount;
1078 }
1079 fill_pk(&mut log.pool, ix.pool);
1080 fill_pk(&mut log.lp_mint, ix.lp_mint);
1081 fill_pk(&mut log.user_pool_lp, ix.user_pool_lp);
1082 fill_pk(&mut log.a_vault_lp, ix.a_vault_lp);
1083 fill_pk(&mut log.b_vault_lp, ix.b_vault_lp);
1084 fill_pk(&mut log.a_vault, ix.a_vault);
1085 fill_pk(&mut log.b_vault, ix.b_vault);
1086 fill_pk(&mut log.a_vault_lp_mint, ix.a_vault_lp_mint);
1087 fill_pk(&mut log.b_vault_lp_mint, ix.b_vault_lp_mint);
1088 fill_pk(&mut log.a_token_vault, ix.a_token_vault);
1089 fill_pk(&mut log.b_token_vault, ix.b_token_vault);
1090 fill_pk(&mut log.user_a_token, ix.user_a_token);
1091 fill_pk(&mut log.user_b_token, ix.user_b_token);
1092 fill_pk(&mut log.user, ix.user);
1093 fill_pk(&mut log.vault_program, ix.vault_program);
1094 fill_pk(&mut log.token_program, ix.token_program);
1095}
1096
1097fn merge_meteora_pools_bootstrap_context(
1098 log: &mut MeteoraPoolsBootstrapLiquidityEvent,
1099 ix: MeteoraPoolsBootstrapLiquidityEvent,
1100) {
1101 if !ix.ix_name.is_empty() {
1102 log.ix_name = ix.ix_name;
1103 log.token_a_in_amount = ix.token_a_in_amount;
1104 log.token_b_in_amount = ix.token_b_in_amount;
1105 }
1106 fill_pk(&mut log.pool, ix.pool);
1107 fill_pk(&mut log.lp_mint, ix.lp_mint);
1108 fill_pk(&mut log.user_pool_lp, ix.user_pool_lp);
1109 fill_pk(&mut log.a_vault_lp, ix.a_vault_lp);
1110 fill_pk(&mut log.b_vault_lp, ix.b_vault_lp);
1111 fill_pk(&mut log.a_vault, ix.a_vault);
1112 fill_pk(&mut log.b_vault, ix.b_vault);
1113 fill_pk(&mut log.a_vault_lp_mint, ix.a_vault_lp_mint);
1114 fill_pk(&mut log.b_vault_lp_mint, ix.b_vault_lp_mint);
1115 fill_pk(&mut log.a_token_vault, ix.a_token_vault);
1116 fill_pk(&mut log.b_token_vault, ix.b_token_vault);
1117 fill_pk(&mut log.user_a_token, ix.user_a_token);
1118 fill_pk(&mut log.user_b_token, ix.user_b_token);
1119 fill_pk(&mut log.user, ix.user);
1120 fill_pk(&mut log.vault_program, ix.vault_program);
1121 fill_pk(&mut log.token_program, ix.token_program);
1122}
1123
1124fn merge_meteora_pools_poolcreated_context(
1125 log: &mut MeteoraPoolsPoolCreatedEvent,
1126 ix: MeteoraPoolsPoolCreatedEvent,
1127) {
1128 if !ix.ix_name.is_empty() {
1129 log.ix_name = ix.ix_name;
1130 log.token_a_in_amount = ix.token_a_in_amount;
1131 log.token_b_in_amount = ix.token_b_in_amount;
1132 log.activation_point = ix.activation_point;
1133 log.stable_curve = ix.stable_curve;
1134 log.trade_fee_bps = ix.trade_fee_bps;
1135 log.customizable_params = ix.customizable_params;
1136 }
1137 fill_pk(&mut log.pool, ix.pool);
1138 fill_pk(&mut log.config, ix.config);
1139 fill_pk(&mut log.lp_mint, ix.lp_mint);
1140 fill_pk(&mut log.token_a_mint, ix.token_a_mint);
1141 fill_pk(&mut log.token_b_mint, ix.token_b_mint);
1142 fill_pk(&mut log.a_vault, ix.a_vault);
1143 fill_pk(&mut log.b_vault, ix.b_vault);
1144 fill_pk(&mut log.a_token_vault, ix.a_token_vault);
1145 fill_pk(&mut log.b_token_vault, ix.b_token_vault);
1146 fill_pk(&mut log.a_vault_lp_mint, ix.a_vault_lp_mint);
1147 fill_pk(&mut log.b_vault_lp_mint, ix.b_vault_lp_mint);
1148 fill_pk(&mut log.a_vault_lp, ix.a_vault_lp);
1149 fill_pk(&mut log.b_vault_lp, ix.b_vault_lp);
1150 fill_pk(&mut log.payer_token_a, ix.payer_token_a);
1151 fill_pk(&mut log.payer_token_b, ix.payer_token_b);
1152 fill_pk(&mut log.payer_pool_lp, ix.payer_pool_lp);
1153 fill_pk(&mut log.protocol_token_a_fee, ix.protocol_token_a_fee);
1154 fill_pk(&mut log.protocol_token_b_fee, ix.protocol_token_b_fee);
1155 fill_pk(&mut log.payer, ix.payer);
1156 fill_pk(&mut log.rent, ix.rent);
1157 fill_pk(&mut log.mint_metadata, ix.mint_metadata);
1158 fill_pk(&mut log.metadata_program, ix.metadata_program);
1159 fill_pk(&mut log.vault_program, ix.vault_program);
1160 fill_pk(&mut log.token_program, ix.token_program);
1161 fill_pk(&mut log.associated_token_program, ix.associated_token_program);
1162 fill_pk(&mut log.system_program, ix.system_program);
1163 fill_pk(&mut log.admin_token_a, ix.admin_token_a);
1164 fill_pk(&mut log.admin_token_b, ix.admin_token_b);
1165 fill_pk(&mut log.admin_pool_lp, ix.admin_pool_lp);
1166 fill_pk(&mut log.admin, ix.admin);
1167 fill_pk(&mut log.fee_owner, ix.fee_owner);
1168}
1169
1170fn merge_meteora_pools_setpoolfees_context(
1171 log: &mut MeteoraPoolsSetPoolFeesEvent,
1172 ix: MeteoraPoolsSetPoolFeesEvent,
1173) {
1174 if !ix.ix_name.is_empty() {
1175 log.ix_name = ix.ix_name;
1176 log.new_partner_fee_numerator = ix.new_partner_fee_numerator;
1177 }
1178 fill_pk(&mut log.pool, ix.pool);
1179 fill_pk(&mut log.fee_operator, ix.fee_operator);
1180}
1181
1182fn merge_meteora_pools_remove_context(
1183 log: &mut MeteoraPoolsRemoveLiquidityEvent,
1184 ix: MeteoraPoolsRemoveLiquidityEvent,
1185) {
1186 if !ix.ix_name.is_empty() {
1187 log.ix_name = ix.ix_name;
1188 log.pool_token_amount = ix.pool_token_amount;
1189 log.minimum_out_amount = ix.minimum_out_amount;
1190 log.minimum_a_token_out = ix.minimum_a_token_out;
1191 log.minimum_b_token_out = ix.minimum_b_token_out;
1192 }
1193 fill_pk(&mut log.pool, ix.pool);
1194 fill_pk(&mut log.lp_mint, ix.lp_mint);
1195 fill_pk(&mut log.user_pool_lp, ix.user_pool_lp);
1196 fill_pk(&mut log.a_vault_lp, ix.a_vault_lp);
1197 fill_pk(&mut log.b_vault_lp, ix.b_vault_lp);
1198 fill_pk(&mut log.a_vault, ix.a_vault);
1199 fill_pk(&mut log.b_vault, ix.b_vault);
1200 fill_pk(&mut log.a_vault_lp_mint, ix.a_vault_lp_mint);
1201 fill_pk(&mut log.b_vault_lp_mint, ix.b_vault_lp_mint);
1202 fill_pk(&mut log.a_token_vault, ix.a_token_vault);
1203 fill_pk(&mut log.b_token_vault, ix.b_token_vault);
1204 fill_pk(&mut log.user_a_token, ix.user_a_token);
1205 fill_pk(&mut log.user_b_token, ix.user_b_token);
1206 fill_pk(&mut log.user, ix.user);
1207 fill_pk(&mut log.vault_program, ix.vault_program);
1208 fill_pk(&mut log.token_program, ix.token_program);
1209 fill_pk(&mut log.user_destination_token, ix.user_destination_token);
1210}
1211
1212fn merge_meteora_pools_swap_context(log: &mut MeteoraPoolsSwapEvent, ix: MeteoraPoolsSwapEvent) {
1213 if !ix.ix_name.is_empty() {
1214 log.ix_name = ix.ix_name;
1215 log.amount_in = ix.amount_in;
1216 log.minimum_out_amount = ix.minimum_out_amount;
1217 }
1218 fill_pk(&mut log.pool, ix.pool);
1219 fill_pk(&mut log.user_source_token, ix.user_source_token);
1220 fill_pk(&mut log.user_destination_token, ix.user_destination_token);
1221 fill_pk(&mut log.a_vault, ix.a_vault);
1222 fill_pk(&mut log.b_vault, ix.b_vault);
1223 fill_pk(&mut log.a_token_vault, ix.a_token_vault);
1224 fill_pk(&mut log.b_token_vault, ix.b_token_vault);
1225 fill_pk(&mut log.a_vault_lp_mint, ix.a_vault_lp_mint);
1226 fill_pk(&mut log.b_vault_lp_mint, ix.b_vault_lp_mint);
1227 fill_pk(&mut log.a_vault_lp, ix.a_vault_lp);
1228 fill_pk(&mut log.b_vault_lp, ix.b_vault_lp);
1229 fill_pk(&mut log.protocol_token_fee, ix.protocol_token_fee);
1230 fill_pk(&mut log.user, ix.user);
1231 fill_pk(&mut log.vault_program, ix.vault_program);
1232 fill_pk(&mut log.token_program, ix.token_program);
1233}
1234
1235fn merge_orca_swap_context(log: &mut OrcaWhirlpoolSwapEvent, ix: OrcaWhirlpoolSwapEvent) {
1236 if !ix.ix_name.is_empty() {
1237 log.ix_name = ix.ix_name;
1238 log.amount = ix.amount;
1239 log.other_amount_threshold = ix.other_amount_threshold;
1240 log.sqrt_price_limit = ix.sqrt_price_limit;
1241 log.amount_specified_is_input = ix.amount_specified_is_input;
1242 }
1243 fill_pk(&mut log.token_authority, ix.token_authority);
1244 fill_pk(&mut log.token_owner_account_a, ix.token_owner_account_a);
1245 fill_pk(&mut log.token_owner_account_b, ix.token_owner_account_b);
1246 fill_pk(&mut log.token_program_a, ix.token_program_a);
1247 fill_pk(&mut log.token_program_b, ix.token_program_b);
1248 fill_pk(&mut log.token_mint_a, ix.token_mint_a);
1249 fill_pk(&mut log.token_mint_b, ix.token_mint_b);
1250 fill_pk(&mut log.token_vault_a, ix.token_vault_a);
1251 fill_pk(&mut log.token_vault_b, ix.token_vault_b);
1252 fill_pk(&mut log.tick_array_0, ix.tick_array_0);
1253 fill_pk(&mut log.tick_array_1, ix.tick_array_1);
1254 fill_pk(&mut log.tick_array_2, ix.tick_array_2);
1255 fill_pk(&mut log.oracle, ix.oracle);
1256}
1257
1258pub fn merge_grpc_instruction_into_log(log: &mut DexEvent, ix: DexEvent) {
1259 use DexEvent::*;
1260 match log {
1261 PumpFunTrade(l) => {
1262 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
1263 merge_pumpfun_trade_log_preferred(l, i);
1264 }
1265 }
1266 PumpFunBuy(l) => {
1267 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
1268 merge_pumpfun_trade_log_preferred(l, i);
1269 }
1270 }
1271 PumpFunSell(l) => {
1272 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
1273 merge_pumpfun_trade_log_preferred(l, i);
1274 }
1275 }
1276 PumpFunBuyExactSolIn(l) => {
1277 if let Some(i) = pumpfun_trade_from_ix_variant(ix) {
1278 merge_pumpfun_trade_log_preferred(l, i);
1279 }
1280 }
1281 PumpFunCreate(l) => match ix {
1282 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_log_preferred(l, i),
1283 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_into_create_log_preferred(l, i),
1284 _ => {}
1285 },
1286 PumpFunCreateV2(l) => match ix {
1287 DexEvent::PumpFunCreate(i) => merge_pumpfun_create_into_create_v2_log_preferred(l, i),
1288 DexEvent::PumpFunCreateV2(i) => merge_pumpfun_create_v2_log_preferred(l, i),
1289 _ => {}
1290 },
1291 PumpFunMigrate(l) => {
1292 if let DexEvent::PumpFunMigrate(i) = ix {
1293 merge_pumpfun_migrate_log_preferred(l, i);
1294 }
1295 }
1296 PumpSwapTrade(l) => {
1297 if let PumpSwapTrade(i) = ix {
1298 merge_pumpswap_trade_log_preferred(l, i);
1299 }
1300 }
1301 PumpSwapBuy(l) => {
1302 if let PumpSwapBuy(i) = ix {
1303 merge_pumpswap_buy_log_preferred(l, i);
1304 }
1305 }
1306 PumpSwapSell(l) => {
1307 if let PumpSwapSell(i) = ix {
1308 merge_pumpswap_sell_log_preferred(l, i);
1309 }
1310 }
1311 RaydiumCpmmSwap(l) => {
1312 if let RaydiumCpmmSwap(i) = ix {
1313 merge_raydium_cpmm_swap_log_preferred(l, i);
1314 }
1315 }
1316 MeteoraPoolsAddLiquidity(l) => {
1317 if let MeteoraPoolsAddLiquidity(i) = ix {
1318 merge_meteora_pools_add_context(l, i);
1319 }
1320 }
1321 MeteoraPoolsRemoveLiquidity(l) => {
1322 if let MeteoraPoolsRemoveLiquidity(i) = ix {
1323 merge_meteora_pools_remove_context(l, i);
1324 }
1325 }
1326 MeteoraPoolsBootstrapLiquidity(l) => {
1327 if let MeteoraPoolsBootstrapLiquidity(i) = ix {
1328 merge_meteora_pools_bootstrap_context(l, i);
1329 }
1330 }
1331 MeteoraPoolsPoolCreated(l) => {
1332 if let MeteoraPoolsPoolCreated(i) = ix {
1333 merge_meteora_pools_poolcreated_context(l, i);
1334 }
1335 }
1336 MeteoraPoolsSetPoolFees(l) => {
1337 if let MeteoraPoolsSetPoolFees(i) = ix {
1338 merge_meteora_pools_setpoolfees_context(l, i);
1339 }
1340 }
1341 MeteoraPoolsSwap(l) => {
1342 if let MeteoraPoolsSwap(i) = ix {
1343 merge_meteora_pools_swap_context(l, i);
1344 }
1345 }
1346 OrcaWhirlpoolSwap(l) => {
1347 if let OrcaWhirlpoolSwap(i) = ix {
1348 merge_orca_swap_context(l, i);
1349 }
1350 }
1351 RaydiumClmmSwap(l) => {
1352 if let RaydiumClmmSwap(i) = ix {
1353 merge_raydium_clmm_swap_log_preferred(l, i);
1354 }
1355 }
1356 RaydiumAmmV4Swap(l) => {
1357 if let RaydiumAmmV4Swap(i) = ix {
1358 merge_raydium_amm_v4_swap_log_preferred(l, i);
1359 }
1360 }
1361 RaydiumLaunchlabTrade(l) => {
1362 if let RaydiumLaunchlabTrade(i) = ix {
1363 merge_raydium_launchlab_trade_log_preferred(l, i);
1364 }
1365 }
1366 RaydiumLaunchlabPoolCreate(l) => {
1367 if let RaydiumLaunchlabPoolCreate(i) = ix {
1368 merge_raydium_launchlab_pool_create_log_preferred(l, i);
1369 }
1370 }
1371 RaydiumLaunchlabMigrateAmm(l) => {
1372 if let RaydiumLaunchlabMigrateAmm(i) = ix {
1373 merge_raydium_launchlab_migrate_amm_log_preferred(l, i);
1374 }
1375 }
1376 PumpSwapCreatePool(l) => {
1377 if let PumpSwapCreatePool(i) = ix {
1378 merge_pumpswap_create_pool_log_preferred(l, i);
1379 }
1380 }
1381 PumpSwapLiquidityAdded(l) => {
1382 if let PumpSwapLiquidityAdded(i) = ix {
1383 merge_pumpswap_liquidity_added_log_preferred(l, i);
1384 }
1385 }
1386 PumpSwapLiquidityRemoved(l) => {
1387 if let PumpSwapLiquidityRemoved(i) = ix {
1388 merge_pumpswap_liquidity_removed_log_preferred(l, i);
1389 }
1390 }
1391 MeteoraDlmmSwap(l) => {
1392 if let MeteoraDlmmSwap(i) = ix {
1393 merge_meteora_dlmm_swap_log_preferred(l, i);
1394 }
1395 }
1396 _ => {}
1397 }
1398}
1399
1400#[inline]
1401fn pumpfun_trade_from_ix_variant(ix: DexEvent) -> Option<PumpFunTradeEvent> {
1402 match ix {
1403 DexEvent::PumpFunTrade(t)
1404 | DexEvent::PumpFunBuy(t)
1405 | DexEvent::PumpFunSell(t)
1406 | DexEvent::PumpFunBuyExactSolIn(t) => Some(t),
1407 _ => None,
1408 }
1409}
1410
1411#[cfg(test)]
1412mod tests {
1413
1414 #[test]
1415 fn clmm_merge_retains_limits_and_mode_without_overwriting_executed_price_or_direction() {
1416 use crate::core::events::{DexEvent, RaydiumClmmSwapEvent};
1417 for input_mode in [false, true] {
1418 let instruction = RaydiumClmmSwapEvent {
1419 ix_name: "swap_v2".to_string(),
1420 amount: 0,
1421 other_amount_threshold: u64::MAX,
1422 sqrt_price_limit_x64: 0,
1423 is_base_input: input_mode,
1424 input_mint: solana_sdk::pubkey::Pubkey::new_unique(),
1425 tick_arrays: vec![solana_sdk::pubkey::Pubkey::new_unique()],
1426 ..Default::default()
1427 };
1428 let log = RaydiumClmmSwapEvent {
1429 amount_0: 456,
1430 amount_1: 123,
1431 sqrt_price_x64: 789,
1432 zero_for_one: !input_mode,
1433 tick: -7,
1434 liquidity: 1011,
1435 amount: 99,
1436 sqrt_price_limit_x64: 99,
1437 ..Default::default()
1438 };
1439 for log_first in [false, true] {
1440 let mut merged = DexEvent::RaydiumClmmSwap(if log_first {
1441 log.clone()
1442 } else {
1443 instruction.clone()
1444 });
1445 if log_first {
1446 super::merge_grpc_instruction_into_log(
1447 &mut merged,
1448 DexEvent::RaydiumClmmSwap(instruction.clone()),
1449 );
1450 } else {
1451 super::merge_events(&mut merged, DexEvent::RaydiumClmmSwap(log.clone()));
1452 }
1453 let DexEvent::RaydiumClmmSwap(e) = merged else { panic!("swap") };
1454 assert_eq!(
1455 (e.amount_0, e.amount_1, e.sqrt_price_x64, e.tick, e.liquidity),
1456 (456, 123, 789, -7, 1011)
1457 );
1458 assert_eq!(e.zero_for_one, !input_mode);
1459 assert_eq!(
1460 (e.amount, e.other_amount_threshold, e.sqrt_price_limit_x64),
1461 (0, u64::MAX, 0)
1462 );
1463 assert_eq!(e.is_base_input, input_mode);
1464 assert_eq!(e.ix_name, "swap_v2");
1465 assert_eq!(e.input_mint, instruction.input_mint);
1466 assert_eq!(e.tick_arrays, instruction.tick_arrays);
1467 }
1468 }
1469 }
1470
1471 #[test]
1472 fn cpmm_merge_preserves_zero_limits_and_execution_values_in_both_paths() {
1473 use crate::core::events::{DexEvent, RaydiumCpmmSwapEvent};
1474 for exact_input in [false, true] {
1475 let payer = solana_sdk::pubkey::Pubkey::new_unique();
1476 let input_account = solana_sdk::pubkey::Pubkey::new_unique();
1477 let instruction = RaydiumCpmmSwapEvent {
1478 ix_name: if exact_input { "swap_base_input" } else { "swap_base_output" }
1479 .to_string(),
1480 base_input: exact_input,
1481 amount_in: 0,
1482 minimum_amount_out: if exact_input { u64::MAX } else { 0 },
1483 max_amount_in: if exact_input { 0 } else { u64::MAX },
1484 amount_out: 0,
1485 payer,
1486 input_token_account: input_account,
1487 ..Default::default()
1488 };
1489 let log = RaydiumCpmmSwapEvent {
1490 base_input: exact_input,
1491 input_amount: 456,
1492 output_amount: 123,
1493 input_transfer_fee: 7,
1494 amount_in: 99,
1495 amount_out: 99,
1496 ..Default::default()
1497 };
1498 for log_first in [false, true] {
1499 let mut merged = if log_first {
1500 DexEvent::RaydiumCpmmSwap(log.clone())
1501 } else {
1502 DexEvent::RaydiumCpmmSwap(instruction.clone())
1503 };
1504 if log_first {
1505 super::merge_grpc_instruction_into_log(
1506 &mut merged,
1507 DexEvent::RaydiumCpmmSwap(instruction.clone()),
1508 );
1509 } else {
1510 super::merge_events(&mut merged, DexEvent::RaydiumCpmmSwap(log.clone()));
1511 }
1512 let DexEvent::RaydiumCpmmSwap(e) = merged else { panic!("swap") };
1513 assert_eq!((e.input_amount, e.output_amount, e.input_transfer_fee), (456, 123, 7));
1514 assert_eq!(e.ix_name, instruction.ix_name);
1515 assert_eq!(
1516 (e.amount_in, e.minimum_amount_out, e.max_amount_in, e.amount_out),
1517 (
1518 instruction.amount_in,
1519 instruction.minimum_amount_out,
1520 instruction.max_amount_in,
1521 0
1522 )
1523 );
1524 assert_eq!(e.payer, payer);
1525 assert_eq!(e.input_token_account, input_account);
1526 assert_eq!(e.base_input, exact_input);
1527 }
1528 }
1529 }
1530 use super::*;
1531
1532 #[test]
1533 fn incompatible_merge_leaves_both_events_unchanged() {
1534 let mut base = DexEvent::Error("base".to_string());
1535 let inner = DexEvent::Error("inner".to_string());
1536 let mut unmerged = None;
1537
1538 assert!(!try_merge_events(&mut base, inner, &mut unmerged));
1539 assert!(matches!(base, DexEvent::Error(ref message) if message == "base"));
1540 assert!(matches!(unmerged, Some(DexEvent::Error(ref message)) if message == "inner"));
1541 }
1542 use solana_sdk::{pubkey::Pubkey, signature::Signature};
1543
1544 fn dlmm_swap(min_amount_out: u64, amount_out: u64) -> MeteoraDlmmSwapEvent {
1545 MeteoraDlmmSwapEvent {
1546 metadata: EventMetadata::default(),
1547 token_x_mint: Pubkey::default(),
1548 token_y_mint: Pubkey::default(),
1549 user_token_in: Pubkey::new_unique(),
1550 user_token_out: Pubkey::new_unique(),
1551 min_amount_out,
1552 pool: Pubkey::new_unique(),
1553 from: Pubkey::new_unique(),
1554 start_bin_id: 0,
1555 end_bin_id: 0,
1556 amount_in: 200,
1557 amount_out,
1558 swap_for_y: true,
1559 fee: 1,
1560 protocol_fee: 0,
1561 fee_bps: 25,
1562 host_fee: 0,
1563 ..Default::default()
1564 }
1565 }
1566
1567 #[test]
1568 fn dlmm_event_merge_keeps_instruction_threshold_and_executed_output() {
1569 let base_event = dlmm_swap(100, 0);
1570 let expected_user_token_in = base_event.user_token_in;
1571 let expected_user_token_out = base_event.user_token_out;
1572 let mut inner_event = dlmm_swap(0, 125);
1573 inner_event.user_token_in = Pubkey::default();
1574 inner_event.user_token_out = Pubkey::default();
1575 let mut base = DexEvent::MeteoraDlmmSwap(base_event);
1576 let inner = DexEvent::MeteoraDlmmSwap(inner_event);
1577
1578 assert!(try_merge_events(&mut base, inner, &mut None));
1579 let DexEvent::MeteoraDlmmSwap(event) = base else { panic!("swap") };
1580 assert_eq!(event.min_amount_out, 100);
1581 assert_eq!(event.amount_out, 125);
1582 assert_eq!(event.user_token_in, expected_user_token_in);
1583 assert_eq!(event.user_token_out, expected_user_token_out);
1584 }
1585
1586 #[test]
1587 fn grpc_dlmm_merge_keeps_log_output_and_adds_instruction_threshold() {
1588 let mut log_event = dlmm_swap(0, 125);
1589 log_event.user_token_in = Pubkey::default();
1590 log_event.user_token_out = Pubkey::default();
1591 let instruction_event = dlmm_swap(100, 0);
1592 let expected_user_token_in = instruction_event.user_token_in;
1593 let expected_user_token_out = instruction_event.user_token_out;
1594 let mut log = DexEvent::MeteoraDlmmSwap(log_event);
1595 let instruction = DexEvent::MeteoraDlmmSwap(instruction_event);
1596
1597 merge_grpc_instruction_into_log(&mut log, instruction);
1598 let DexEvent::MeteoraDlmmSwap(event) = log else { panic!("swap") };
1599 assert_eq!(event.min_amount_out, 100);
1600 assert_eq!(event.amount_out, 125);
1601 assert_eq!(event.user_token_in, expected_user_token_in);
1602 assert_eq!(event.user_token_out, expected_user_token_out);
1603 }
1604
1605 #[test]
1606 fn test_merge_pumpfun_trade() {
1607 let metadata = EventMetadata {
1608 signature: Signature::default(),
1609 slot: 100,
1610 tx_index: 1,
1611 block_time_us: 1000,
1612 grpc_recv_us: 2000,
1613 recent_blockhash: None,
1614 };
1615
1616 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1618 metadata: metadata.clone(),
1619 bonding_curve: Pubkey::new_unique(),
1620 associated_bonding_curve: Pubkey::new_unique(),
1621 ..Default::default()
1622 });
1623
1624 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1626 metadata: metadata.clone(),
1627 mint: Pubkey::new_unique(),
1628 sol_amount: 1000,
1629 token_amount: 2000,
1630 is_buy: true,
1631 user: Pubkey::new_unique(),
1632 holder_rewards_bps: 300,
1633 holder_rewards: 400,
1634 ..Default::default()
1635 });
1636
1637 merge_events(&mut base, inner);
1639
1640 if let DexEvent::PumpFunTrade(trade) = base {
1642 assert_eq!(trade.sol_amount, 1000);
1643 assert_eq!(trade.token_amount, 2000);
1644 assert!(trade.is_buy);
1645 assert_eq!(trade.holder_rewards_bps, 300);
1646 assert_eq!(trade.holder_rewards, 400);
1647 assert_ne!(trade.bonding_curve, Pubkey::default());
1649 assert_ne!(trade.associated_bonding_curve, Pubkey::default());
1650 } else {
1651 panic!("Expected PumpFunTrade event");
1652 }
1653 }
1654
1655 #[test]
1656 fn merge_pumpfun_trade_non_leg_propagates_holder_rewards() {
1657 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent::default());
1658 let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1659 holder_rewards_bps: 300,
1660 holder_rewards: 400,
1661 ..Default::default()
1662 });
1663
1664 merge_events(&mut base, inner);
1665
1666 let DexEvent::PumpFunTrade(trade) = base else {
1667 panic!("expected PumpFunTrade event");
1668 };
1669 assert_eq!(trade.holder_rewards_bps, 300);
1670 assert_eq!(trade.holder_rewards, 400);
1671 }
1672
1673 #[test]
1674 fn merge_preserves_instruction_context_when_log_tail_is_absent() {
1675 let metadata = EventMetadata {
1676 signature: Signature::default(),
1677 slot: 100,
1678 tx_index: 1,
1679 block_time_us: 1000,
1680 grpc_recv_us: 2000,
1681 recent_blockhash: None,
1682 };
1683 let quote_mint = Pubkey::new_unique();
1684 let associated_quote_user = Pubkey::new_unique();
1685
1686 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1687 metadata: metadata.clone(),
1688 ix_name: "buy_exact_quote_in".to_string(),
1689 quote_mint,
1690 spendable_quote_in: 1_000,
1691 min_tokens_out: 2_000,
1692 associated_quote_user,
1693 ..Default::default()
1694 });
1695
1696 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1697 metadata,
1698 sol_amount: 1_000,
1699 token_amount: 2_000,
1700 is_buy: true,
1701 ..Default::default()
1702 });
1703
1704 merge_events(&mut base, inner);
1705
1706 if let DexEvent::PumpFunTrade(t) = base {
1707 assert_eq!(t.sol_amount, 1_000);
1708 assert_eq!(t.token_amount, 2_000);
1709 assert_eq!(t.ix_name, "buy_exact_quote_in");
1710 assert_eq!(t.quote_mint, quote_mint);
1711 assert_eq!(t.spendable_quote_in, 1_000);
1712 assert_eq!(t.min_tokens_out, 2_000);
1713 assert_eq!(t.associated_quote_user, associated_quote_user);
1714 } else {
1715 panic!("Expected PumpFunTrade event");
1716 }
1717 }
1718
1719 #[test]
1720 fn merge_replaces_sol_quote_sentinel_with_real_quote_mint() {
1721 let quote_mint = Pubkey::new_unique();
1722 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1723 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1724 ..Default::default()
1725 });
1726
1727 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent { quote_mint, ..Default::default() });
1728
1729 merge_events(&mut base, inner);
1730
1731 if let DexEvent::PumpFunTrade(t) = base {
1732 assert_eq!(t.quote_mint, quote_mint);
1733 } else {
1734 panic!("Expected PumpFunTrade event");
1735 }
1736 }
1737
1738 #[test]
1739 fn merge_replaces_sol_quote_sentinel_with_wsol_mint() {
1740 let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1741 quote_mint: PUMPFUN_SOLSCAN_SOL_QUOTE_MINT,
1742 ..Default::default()
1743 });
1744
1745 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1746 quote_mint: PUMPFUN_WSOL_QUOTE_MINT,
1747 ..Default::default()
1748 });
1749
1750 merge_events(&mut base, inner);
1751
1752 if let DexEvent::PumpFunTrade(t) = base {
1753 assert_eq!(t.quote_mint, PUMPFUN_WSOL_QUOTE_MINT);
1754 } else {
1755 panic!("Expected PumpFunTrade event");
1756 }
1757 }
1758
1759 #[test]
1760 fn test_can_merge() {
1761 let metadata = EventMetadata {
1762 signature: Signature::default(),
1763 slot: 100,
1764 tx_index: 1,
1765 block_time_us: 1000,
1766 grpc_recv_us: 2000,
1767 recent_blockhash: None,
1768 };
1769
1770 let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1771 metadata: metadata.clone(),
1772 ..Default::default()
1773 });
1774
1775 let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
1776 metadata: metadata.clone(),
1777 ..Default::default()
1778 });
1779
1780 assert!(can_merge(&base, &inner));
1782
1783 let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
1785 metadata: EventMetadata { signature: Signature::new_unique(), ..metadata },
1786 ..Default::default()
1787 });
1788
1789 assert!(!can_merge(&base, &different_sig));
1790 }
1791
1792 #[test]
1793 fn dlmm_position_event_keeps_instruction_only_fields() {
1794 let pool = Pubkey::new_unique();
1795 let position = Pubkey::new_unique();
1796 let owner = Pubkey::new_unique();
1797 let mut base = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1798 metadata: EventMetadata::default(),
1799 pool,
1800 position,
1801 owner,
1802 lower_bin_id: -42,
1803 width: 70,
1804 });
1805 let inner = DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1806 metadata: EventMetadata::default(),
1807 pool,
1808 position,
1809 owner,
1810 lower_bin_id: 0,
1811 width: 0,
1812 });
1813
1814 assert!(try_merge_events(&mut base, inner, &mut None));
1815 let DexEvent::MeteoraDlmmCreatePosition(event) = base else { panic!("position") };
1816 assert_eq!(event.lower_bin_id, -42);
1817 assert_eq!(event.width, 70);
1818 }
1819
1820 #[test]
1821 fn grpc_merge_fills_fee_recipient_from_ix_when_log_default() {
1822 let metadata = EventMetadata {
1823 signature: Signature::default(),
1824 slot: 1,
1825 tx_index: 0,
1826 block_time_us: 0,
1827 grpc_recv_us: 0,
1828 recent_blockhash: None,
1829 };
1830 let fr = Pubkey::new_unique();
1831 let log_t =
1832 PumpFunTradeEvent { metadata: metadata.clone(), sol_amount: 50, ..Default::default() };
1833 let mut ix_t = log_t.clone();
1834 ix_t.fee_recipient = fr;
1835 ix_t.sol_amount = 777;
1836 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1837 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1838 match log_ev {
1839 DexEvent::PumpFunTrade(t) => {
1840 assert_eq!(t.fee_recipient, fr);
1841 assert_eq!(t.sol_amount, 50);
1842 }
1843 _ => panic!("expected trade"),
1844 }
1845 }
1846
1847 #[test]
1848 fn grpc_merge_keeps_log_trade_fields() {
1849 let metadata = EventMetadata {
1850 signature: Signature::default(),
1851 slot: 1,
1852 tx_index: 0,
1853 block_time_us: 0,
1854 grpc_recv_us: 0,
1855 recent_blockhash: None,
1856 };
1857 let log_t = PumpFunTradeEvent {
1858 metadata: metadata.clone(),
1859 mayhem_mode: true,
1860 sol_amount: 100,
1861 ..Default::default()
1862 };
1863 let mut ix_t = log_t.clone();
1864 ix_t.mayhem_mode = false;
1865 ix_t.sol_amount = 999;
1866
1867 let mut log_ev = DexEvent::PumpFunTrade(log_t);
1868 merge_grpc_instruction_into_log(&mut log_ev, DexEvent::PumpFunBuy(ix_t));
1869 match log_ev {
1870 DexEvent::PumpFunTrade(t) => {
1871 assert!(t.mayhem_mode);
1872 assert_eq!(t.sol_amount, 100);
1873 }
1874 _ => panic!("variant preserved"),
1875 }
1876 }
1877
1878 #[test]
1879 fn pumpfun_create_merge_propagates_holder_rewards_fields() {
1880 let mut base = DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1881 creator_fee_bps: 125,
1882 ..Default::default()
1883 });
1884 let inner = DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1885 creator_fee_bps: 300,
1886 is_holder_reward: true,
1887 ..Default::default()
1888 });
1889
1890 merge_events(&mut base, inner);
1891
1892 let DexEvent::PumpFunCreate(event) = base else { panic!("create") };
1893 assert_eq!(event.creator_fee_bps, 300);
1894 assert!(event.is_holder_reward);
1895 }
1896
1897 #[test]
1898 fn grpc_create_merge_keeps_log_fee_and_adds_instruction_holder_reward() {
1899 let mut log = DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1900 creator_fee_bps: 300,
1901 ..Default::default()
1902 });
1903 let instruction = DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
1904 creator_fee_bps: 500,
1905 is_holder_reward: true,
1906 ..Default::default()
1907 });
1908
1909 merge_grpc_instruction_into_log(&mut log, instruction);
1910
1911 let DexEvent::PumpFunCreate(event) = log else { panic!("create") };
1912 assert_eq!(event.creator_fee_bps, 300);
1913 assert!(event.is_holder_reward);
1914 }
1915}
1916
1917#[cfg(test)]
1918mod orca_swap_context_tests {
1919 use super::*;
1920
1921 #[test]
1922 fn both_merge_paths_preserve_zero_wire_values_and_authoritative_execution() {
1923 for input_mode in [false, true] {
1924 let ix = OrcaWhirlpoolSwapEvent {
1925 ix_name: "swap_v2".into(),
1926 amount: 0,
1927 other_amount_threshold: u64::MAX,
1928 sqrt_price_limit: 0,
1929 amount_specified_is_input: input_mode,
1930 token_authority: Pubkey::new_unique(),
1931 token_owner_account_a: Pubkey::new_unique(),
1932 token_owner_account_b: Pubkey::new_unique(),
1933 ..Default::default()
1934 };
1935 let log = OrcaWhirlpoolSwapEvent {
1936 amount: 99,
1937 sqrt_price_limit: 99,
1938 input_amount: 123,
1939 output_amount: 456,
1940 pre_sqrt_price: 789,
1941 post_sqrt_price: 987,
1942 input_transfer_fee: 1,
1943 output_transfer_fee: 2,
1944 lp_fee: 3,
1945 protocol_fee: 4,
1946 a_to_b: true,
1947 ..Default::default()
1948 };
1949 for log_first in [false, true] {
1950 let mut result = if log_first {
1951 DexEvent::OrcaWhirlpoolSwap(log.clone())
1952 } else {
1953 DexEvent::OrcaWhirlpoolSwap(ix.clone())
1954 };
1955 if log_first {
1956 merge_grpc_instruction_into_log(
1957 &mut result,
1958 DexEvent::OrcaWhirlpoolSwap(ix.clone()),
1959 );
1960 } else {
1961 merge_events(&mut result, DexEvent::OrcaWhirlpoolSwap(log.clone()));
1962 }
1963 let DexEvent::OrcaWhirlpoolSwap(e) = result else { panic!("swap") };
1964 assert_eq!(
1965 (e.amount, e.other_amount_threshold, e.sqrt_price_limit),
1966 (0, u64::MAX, 0)
1967 );
1968 assert_eq!(e.amount_specified_is_input, input_mode);
1969 assert_eq!(e.ix_name, "swap_v2");
1970 assert_eq!(
1971 (e.input_amount, e.output_amount, e.pre_sqrt_price, e.post_sqrt_price),
1972 (123, 456, 789, 987)
1973 );
1974 assert_eq!(
1975 (e.input_transfer_fee, e.output_transfer_fee, e.lp_fee, e.protocol_fee),
1976 (1, 2, 3, 4)
1977 );
1978 assert!(e.a_to_b);
1979 assert_eq!(
1980 (e.token_authority, e.token_owner_account_a, e.token_owner_account_b),
1981 (ix.token_authority, ix.token_owner_account_a, ix.token_owner_account_b)
1982 );
1983 }
1984 }
1985 }
1986}
1987
1988#[cfg(test)]
1989mod pools_swap_merge_tests {
1990 use super::*;
1991
1992 #[test]
1993 fn both_paths_keep_instruction_zero_values_and_log_execution_values() {
1994 let ix = MeteoraPoolsSwapEvent {
1995 ix_name: "swap".into(),
1996 amount_in: 0,
1997 minimum_out_amount: u64::MAX,
1998 pool: Pubkey::new_unique(),
1999 user_source_token: Pubkey::new_unique(),
2000 user_destination_token: Pubkey::new_unique(),
2001 a_vault: Pubkey::new_unique(),
2002 b_vault: Pubkey::new_unique(),
2003 a_token_vault: Pubkey::new_unique(),
2004 b_token_vault: Pubkey::new_unique(),
2005 a_vault_lp_mint: Pubkey::new_unique(),
2006 b_vault_lp_mint: Pubkey::new_unique(),
2007 a_vault_lp: Pubkey::new_unique(),
2008 b_vault_lp: Pubkey::new_unique(),
2009 protocol_token_fee: Pubkey::new_unique(),
2010 user: Pubkey::new_unique(),
2011 vault_program: Pubkey::new_unique(),
2012 token_program: Pubkey::new_unique(),
2013 ..Default::default()
2014 };
2015 let log = MeteoraPoolsSwapEvent {
2016 amount_in: 99,
2017 minimum_out_amount: 99,
2018 in_amount: 123,
2019 out_amount: 456,
2020 trade_fee: 1,
2021 admin_fee: 2,
2022 host_fee: 3,
2023 token_program: Pubkey::new_unique(),
2024 ..Default::default()
2025 };
2026 for log_first in [false, true] {
2027 let mut result =
2028 DexEvent::MeteoraPoolsSwap(if log_first { log.clone() } else { ix.clone() });
2029 if log_first {
2030 merge_grpc_instruction_into_log(
2031 &mut result,
2032 DexEvent::MeteoraPoolsSwap(ix.clone()),
2033 );
2034 } else {
2035 merge_events(&mut result, DexEvent::MeteoraPoolsSwap(log.clone()));
2036 }
2037 let DexEvent::MeteoraPoolsSwap(e) = result else { panic!("swap") };
2038 assert_eq!(e.ix_name, "swap");
2039 assert_eq!((e.amount_in, e.minimum_out_amount), (0, u64::MAX));
2040 assert_eq!(
2041 (e.in_amount, e.out_amount, e.trade_fee, e.admin_fee, e.host_fee),
2042 (123, 456, 1, 2, 3)
2043 );
2044 assert_eq!(e.pool, ix.pool);
2045 assert_eq!(e.user_source_token, ix.user_source_token);
2046 assert_eq!(e.user_destination_token, ix.user_destination_token);
2047 assert_eq!(e.a_vault, ix.a_vault);
2048 assert_eq!(e.b_vault, ix.b_vault);
2049 assert_eq!(e.a_token_vault, ix.a_token_vault);
2050 assert_eq!(e.b_token_vault, ix.b_token_vault);
2051 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2052 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2053 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2054 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2055 assert_eq!(e.protocol_token_fee, ix.protocol_token_fee);
2056 assert_eq!(e.user, ix.user);
2057 assert_eq!(e.vault_program, ix.vault_program);
2058 assert_eq!(e.token_program, log.token_program);
2059 }
2060 }
2061}
2062
2063#[cfg(test)]
2064mod pools_liquidity_merge_tests {
2065 use super::*;
2066 #[test]
2067 fn add_balance_liquidity_both_merge_paths_preserve_limits_and_execution() {
2068 let ix = MeteoraPoolsAddLiquidityEvent {
2069 ix_name: "add_balance_liquidity".into(),
2070 pool_token_amount: 0,
2071 maximum_token_a_amount: u64::MAX,
2072 maximum_token_b_amount: 0,
2073 pool: Pubkey::new_unique(),
2074 lp_mint: Pubkey::new_unique(),
2075 user_pool_lp: Pubkey::new_unique(),
2076 a_vault_lp: Pubkey::new_unique(),
2077 b_vault_lp: Pubkey::new_unique(),
2078 a_vault: Pubkey::new_unique(),
2079 b_vault: Pubkey::new_unique(),
2080 a_vault_lp_mint: Pubkey::new_unique(),
2081 b_vault_lp_mint: Pubkey::new_unique(),
2082 a_token_vault: Pubkey::new_unique(),
2083 b_token_vault: Pubkey::new_unique(),
2084 user_a_token: Pubkey::new_unique(),
2085 user_b_token: Pubkey::new_unique(),
2086 user: Pubkey::new_unique(),
2087 vault_program: Pubkey::new_unique(),
2088 token_program: Pubkey::new_unique(),
2089 ..Default::default()
2090 };
2091 let log = MeteoraPoolsAddLiquidityEvent {
2092 pool_token_amount: 99,
2093 maximum_token_a_amount: 99,
2094 maximum_token_b_amount: 99,
2095 lp_mint_amount: 123,
2096 token_a_amount: 124,
2097 token_b_amount: 125,
2098 token_program: Pubkey::new_unique(),
2099 ..Default::default()
2100 };
2101 for log_first in [false, true] {
2102 let mut result = DexEvent::MeteoraPoolsAddLiquidity(if log_first {
2103 log.clone()
2104 } else {
2105 ix.clone()
2106 });
2107 if log_first {
2108 merge_grpc_instruction_into_log(
2109 &mut result,
2110 DexEvent::MeteoraPoolsAddLiquidity(ix.clone()),
2111 );
2112 } else {
2113 merge_events(&mut result, DexEvent::MeteoraPoolsAddLiquidity(log.clone()));
2114 }
2115 let DexEvent::MeteoraPoolsAddLiquidity(e) = result else { panic!("liquidity") };
2116 assert_eq!(e.ix_name, "add_balance_liquidity");
2117 assert_eq!(e.pool_token_amount, ix.pool_token_amount);
2118 assert_eq!(e.maximum_token_a_amount, ix.maximum_token_a_amount);
2119 assert_eq!(e.maximum_token_b_amount, ix.maximum_token_b_amount);
2120 assert_eq!(e.pool, ix.pool);
2121 assert_eq!(e.lp_mint, ix.lp_mint);
2122 assert_eq!(e.user_pool_lp, ix.user_pool_lp);
2123 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2124 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2125 assert_eq!(e.a_vault, ix.a_vault);
2126 assert_eq!(e.b_vault, ix.b_vault);
2127 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2128 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2129 assert_eq!(e.a_token_vault, ix.a_token_vault);
2130 assert_eq!(e.b_token_vault, ix.b_token_vault);
2131 assert_eq!(e.user_a_token, ix.user_a_token);
2132 assert_eq!(e.user_b_token, ix.user_b_token);
2133 assert_eq!(e.user, ix.user);
2134 assert_eq!(e.vault_program, ix.vault_program);
2135 assert_eq!(e.lp_mint_amount, log.lp_mint_amount);
2136 assert_eq!(e.token_a_amount, log.token_a_amount);
2137 assert_eq!(e.token_b_amount, log.token_b_amount);
2138 assert_eq!(e.token_program, log.token_program);
2139 }
2140 }
2141 #[test]
2142 fn add_imbalance_liquidity_both_merge_paths_preserve_limits_and_execution() {
2143 let ix = MeteoraPoolsAddLiquidityEvent {
2144 ix_name: "add_imbalance_liquidity".into(),
2145 minimum_pool_token_amount: 0,
2146 token_a_in_amount: u64::MAX,
2147 token_b_in_amount: 0,
2148 pool: Pubkey::new_unique(),
2149 lp_mint: Pubkey::new_unique(),
2150 user_pool_lp: Pubkey::new_unique(),
2151 a_vault_lp: Pubkey::new_unique(),
2152 b_vault_lp: Pubkey::new_unique(),
2153 a_vault: Pubkey::new_unique(),
2154 b_vault: Pubkey::new_unique(),
2155 a_vault_lp_mint: Pubkey::new_unique(),
2156 b_vault_lp_mint: Pubkey::new_unique(),
2157 a_token_vault: Pubkey::new_unique(),
2158 b_token_vault: Pubkey::new_unique(),
2159 user_a_token: Pubkey::new_unique(),
2160 user_b_token: Pubkey::new_unique(),
2161 user: Pubkey::new_unique(),
2162 vault_program: Pubkey::new_unique(),
2163 token_program: Pubkey::new_unique(),
2164 ..Default::default()
2165 };
2166 let log = MeteoraPoolsAddLiquidityEvent {
2167 minimum_pool_token_amount: 99,
2168 token_a_in_amount: 99,
2169 token_b_in_amount: 99,
2170 lp_mint_amount: 123,
2171 token_a_amount: 124,
2172 token_b_amount: 125,
2173 token_program: Pubkey::new_unique(),
2174 ..Default::default()
2175 };
2176 for log_first in [false, true] {
2177 let mut result = DexEvent::MeteoraPoolsAddLiquidity(if log_first {
2178 log.clone()
2179 } else {
2180 ix.clone()
2181 });
2182 if log_first {
2183 merge_grpc_instruction_into_log(
2184 &mut result,
2185 DexEvent::MeteoraPoolsAddLiquidity(ix.clone()),
2186 );
2187 } else {
2188 merge_events(&mut result, DexEvent::MeteoraPoolsAddLiquidity(log.clone()));
2189 }
2190 let DexEvent::MeteoraPoolsAddLiquidity(e) = result else { panic!("liquidity") };
2191 assert_eq!(e.ix_name, "add_imbalance_liquidity");
2192 assert_eq!(e.minimum_pool_token_amount, ix.minimum_pool_token_amount);
2193 assert_eq!(e.token_a_in_amount, ix.token_a_in_amount);
2194 assert_eq!(e.token_b_in_amount, ix.token_b_in_amount);
2195 assert_eq!(e.pool, ix.pool);
2196 assert_eq!(e.lp_mint, ix.lp_mint);
2197 assert_eq!(e.user_pool_lp, ix.user_pool_lp);
2198 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2199 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2200 assert_eq!(e.a_vault, ix.a_vault);
2201 assert_eq!(e.b_vault, ix.b_vault);
2202 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2203 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2204 assert_eq!(e.a_token_vault, ix.a_token_vault);
2205 assert_eq!(e.b_token_vault, ix.b_token_vault);
2206 assert_eq!(e.user_a_token, ix.user_a_token);
2207 assert_eq!(e.user_b_token, ix.user_b_token);
2208 assert_eq!(e.user, ix.user);
2209 assert_eq!(e.vault_program, ix.vault_program);
2210 assert_eq!(e.lp_mint_amount, log.lp_mint_amount);
2211 assert_eq!(e.token_a_amount, log.token_a_amount);
2212 assert_eq!(e.token_b_amount, log.token_b_amount);
2213 assert_eq!(e.token_program, log.token_program);
2214 }
2215 }
2216 #[test]
2217 fn remove_balance_liquidity_both_merge_paths_preserve_limits_and_execution() {
2218 let ix = MeteoraPoolsRemoveLiquidityEvent {
2219 ix_name: "remove_balance_liquidity".into(),
2220 pool_token_amount: 0,
2221 minimum_a_token_out: u64::MAX,
2222 minimum_b_token_out: 0,
2223 pool: Pubkey::new_unique(),
2224 lp_mint: Pubkey::new_unique(),
2225 user_pool_lp: Pubkey::new_unique(),
2226 a_vault_lp: Pubkey::new_unique(),
2227 b_vault_lp: Pubkey::new_unique(),
2228 a_vault: Pubkey::new_unique(),
2229 b_vault: Pubkey::new_unique(),
2230 a_vault_lp_mint: Pubkey::new_unique(),
2231 b_vault_lp_mint: Pubkey::new_unique(),
2232 a_token_vault: Pubkey::new_unique(),
2233 b_token_vault: Pubkey::new_unique(),
2234 user_a_token: Pubkey::new_unique(),
2235 user_b_token: Pubkey::new_unique(),
2236 user: Pubkey::new_unique(),
2237 vault_program: Pubkey::new_unique(),
2238 token_program: Pubkey::new_unique(),
2239 ..Default::default()
2240 };
2241 let log = MeteoraPoolsRemoveLiquidityEvent {
2242 pool_token_amount: 99,
2243 minimum_a_token_out: 99,
2244 minimum_b_token_out: 99,
2245 lp_unmint_amount: 123,
2246 token_a_out_amount: 124,
2247 token_b_out_amount: 125,
2248 token_program: Pubkey::new_unique(),
2249 ..Default::default()
2250 };
2251 for log_first in [false, true] {
2252 let mut result = DexEvent::MeteoraPoolsRemoveLiquidity(if log_first {
2253 log.clone()
2254 } else {
2255 ix.clone()
2256 });
2257 if log_first {
2258 merge_grpc_instruction_into_log(
2259 &mut result,
2260 DexEvent::MeteoraPoolsRemoveLiquidity(ix.clone()),
2261 );
2262 } else {
2263 merge_events(&mut result, DexEvent::MeteoraPoolsRemoveLiquidity(log.clone()));
2264 }
2265 let DexEvent::MeteoraPoolsRemoveLiquidity(e) = result else { panic!("liquidity") };
2266 assert_eq!(e.ix_name, "remove_balance_liquidity");
2267 assert_eq!(e.pool_token_amount, ix.pool_token_amount);
2268 assert_eq!(e.minimum_a_token_out, ix.minimum_a_token_out);
2269 assert_eq!(e.minimum_b_token_out, ix.minimum_b_token_out);
2270 assert_eq!(e.pool, ix.pool);
2271 assert_eq!(e.lp_mint, ix.lp_mint);
2272 assert_eq!(e.user_pool_lp, ix.user_pool_lp);
2273 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2274 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2275 assert_eq!(e.a_vault, ix.a_vault);
2276 assert_eq!(e.b_vault, ix.b_vault);
2277 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2278 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2279 assert_eq!(e.a_token_vault, ix.a_token_vault);
2280 assert_eq!(e.b_token_vault, ix.b_token_vault);
2281 assert_eq!(e.user_a_token, ix.user_a_token);
2282 assert_eq!(e.user_b_token, ix.user_b_token);
2283 assert_eq!(e.user, ix.user);
2284 assert_eq!(e.vault_program, ix.vault_program);
2285 assert_eq!(e.lp_unmint_amount, log.lp_unmint_amount);
2286 assert_eq!(e.token_a_out_amount, log.token_a_out_amount);
2287 assert_eq!(e.token_b_out_amount, log.token_b_out_amount);
2288 assert_eq!(e.token_program, log.token_program);
2289 }
2290 }
2291}
2292
2293#[cfg(test)]
2294mod remaining_liquidity_merge_tests {
2295 use super::*;
2296 #[test]
2297 fn remove_liquidity_single_side_both_mergers_keep_zero_inputs_and_log_execution() {
2298 let ix = MeteoraPoolsRemoveLiquidityEvent {
2299 ix_name: "remove_liquidity_single_side".into(),
2300 pool_token_amount: 0,
2301 minimum_out_amount: u64::MAX,
2302 pool: Pubkey::new_unique(),
2303 lp_mint: Pubkey::new_unique(),
2304 user_pool_lp: Pubkey::new_unique(),
2305 a_vault_lp: Pubkey::new_unique(),
2306 b_vault_lp: Pubkey::new_unique(),
2307 a_vault: Pubkey::new_unique(),
2308 b_vault: Pubkey::new_unique(),
2309 a_vault_lp_mint: Pubkey::new_unique(),
2310 b_vault_lp_mint: Pubkey::new_unique(),
2311 a_token_vault: Pubkey::new_unique(),
2312 b_token_vault: Pubkey::new_unique(),
2313 user_destination_token: Pubkey::new_unique(),
2314 user: Pubkey::new_unique(),
2315 vault_program: Pubkey::new_unique(),
2316 token_program: Pubkey::new_unique(),
2317 ..Default::default()
2318 };
2319 let log = MeteoraPoolsRemoveLiquidityEvent {
2320 pool_token_amount: 99,
2321 minimum_out_amount: 99,
2322 lp_unmint_amount: 123,
2323 token_a_out_amount: 124,
2324 token_b_out_amount: 125,
2325 token_program: Pubkey::new_unique(),
2326 ..Default::default()
2327 };
2328 for log_first in [false, true] {
2329 let mut result = DexEvent::MeteoraPoolsRemoveLiquidity(if log_first {
2330 log.clone()
2331 } else {
2332 ix.clone()
2333 });
2334 if log_first {
2335 merge_grpc_instruction_into_log(
2336 &mut result,
2337 DexEvent::MeteoraPoolsRemoveLiquidity(ix.clone()),
2338 );
2339 } else {
2340 merge_events(&mut result, DexEvent::MeteoraPoolsRemoveLiquidity(log.clone()));
2341 }
2342 let DexEvent::MeteoraPoolsRemoveLiquidity(e) = result else { panic!("liquidity") };
2343 assert_eq!(e.ix_name, "remove_liquidity_single_side");
2344 assert_eq!(e.pool_token_amount, ix.pool_token_amount);
2345 assert_eq!(e.minimum_out_amount, ix.minimum_out_amount);
2346 assert_eq!(e.pool, ix.pool);
2347 assert_eq!(e.lp_mint, ix.lp_mint);
2348 assert_eq!(e.user_pool_lp, ix.user_pool_lp);
2349 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2350 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2351 assert_eq!(e.a_vault, ix.a_vault);
2352 assert_eq!(e.b_vault, ix.b_vault);
2353 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2354 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2355 assert_eq!(e.a_token_vault, ix.a_token_vault);
2356 assert_eq!(e.b_token_vault, ix.b_token_vault);
2357 assert_eq!(e.user_destination_token, ix.user_destination_token);
2358 assert_eq!(e.user, ix.user);
2359 assert_eq!(e.vault_program, ix.vault_program);
2360 assert_eq!(e.lp_unmint_amount, log.lp_unmint_amount);
2361 assert_eq!(e.token_a_out_amount, log.token_a_out_amount);
2362 assert_eq!(e.token_b_out_amount, log.token_b_out_amount);
2363 assert_eq!(e.token_program, log.token_program);
2364 }
2365 }
2366 #[test]
2367 fn bootstrap_liquidity_both_mergers_keep_zero_inputs_and_log_execution() {
2368 let ix = MeteoraPoolsBootstrapLiquidityEvent {
2369 ix_name: "bootstrap_liquidity".into(),
2370 token_a_in_amount: 0,
2371 token_b_in_amount: u64::MAX,
2372 pool: Pubkey::new_unique(),
2373 lp_mint: Pubkey::new_unique(),
2374 user_pool_lp: Pubkey::new_unique(),
2375 a_vault_lp: Pubkey::new_unique(),
2376 b_vault_lp: Pubkey::new_unique(),
2377 a_vault: Pubkey::new_unique(),
2378 b_vault: Pubkey::new_unique(),
2379 a_vault_lp_mint: Pubkey::new_unique(),
2380 b_vault_lp_mint: Pubkey::new_unique(),
2381 a_token_vault: Pubkey::new_unique(),
2382 b_token_vault: Pubkey::new_unique(),
2383 user_a_token: Pubkey::new_unique(),
2384 user_b_token: Pubkey::new_unique(),
2385 user: Pubkey::new_unique(),
2386 vault_program: Pubkey::new_unique(),
2387 token_program: Pubkey::new_unique(),
2388 ..Default::default()
2389 };
2390 let log = MeteoraPoolsBootstrapLiquidityEvent {
2391 token_a_in_amount: 99,
2392 token_b_in_amount: 99,
2393 lp_mint_amount: 123,
2394 token_a_amount: 124,
2395 token_b_amount: 125,
2396 token_program: Pubkey::new_unique(),
2397 ..Default::default()
2398 };
2399 for log_first in [false, true] {
2400 let mut result = DexEvent::MeteoraPoolsBootstrapLiquidity(if log_first {
2401 log.clone()
2402 } else {
2403 ix.clone()
2404 });
2405 if log_first {
2406 merge_grpc_instruction_into_log(
2407 &mut result,
2408 DexEvent::MeteoraPoolsBootstrapLiquidity(ix.clone()),
2409 );
2410 } else {
2411 merge_events(&mut result, DexEvent::MeteoraPoolsBootstrapLiquidity(log.clone()));
2412 }
2413 let DexEvent::MeteoraPoolsBootstrapLiquidity(e) = result else { panic!("liquidity") };
2414 assert_eq!(e.ix_name, "bootstrap_liquidity");
2415 assert_eq!(e.token_a_in_amount, ix.token_a_in_amount);
2416 assert_eq!(e.token_b_in_amount, ix.token_b_in_amount);
2417 assert_eq!(e.pool, ix.pool);
2418 assert_eq!(e.lp_mint, ix.lp_mint);
2419 assert_eq!(e.user_pool_lp, ix.user_pool_lp);
2420 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2421 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2422 assert_eq!(e.a_vault, ix.a_vault);
2423 assert_eq!(e.b_vault, ix.b_vault);
2424 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2425 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2426 assert_eq!(e.a_token_vault, ix.a_token_vault);
2427 assert_eq!(e.b_token_vault, ix.b_token_vault);
2428 assert_eq!(e.user_a_token, ix.user_a_token);
2429 assert_eq!(e.user_b_token, ix.user_b_token);
2430 assert_eq!(e.user, ix.user);
2431 assert_eq!(e.vault_program, ix.vault_program);
2432 assert_eq!(e.lp_mint_amount, log.lp_mint_amount);
2433 assert_eq!(e.token_a_amount, log.token_a_amount);
2434 assert_eq!(e.token_b_amount, log.token_b_amount);
2435 assert_eq!(e.token_program, log.token_program);
2436 }
2437 }
2438}
2439
2440#[cfg(test)]
2441mod pools_management_merge_tests {
2442 use super::*;
2443 #[test]
2444 fn creation_both_paths_keep_zero_inputs_optional_activation_and_accounts() {
2445 for activation_point in [None, Some(0), Some(u64::MAX)] {
2446 let ix = MeteoraPoolsPoolCreatedEvent {
2447 ix_name: "initialize_permissionless_constant_product_pool_with_config2".into(),
2448 token_a_in_amount: 0,
2449 token_b_in_amount: u64::MAX,
2450 activation_point,
2451 pool: Pubkey::new_unique(),
2452 config: Pubkey::new_unique(),
2453 lp_mint: Pubkey::new_unique(),
2454 token_a_mint: Pubkey::new_unique(),
2455 token_b_mint: Pubkey::new_unique(),
2456 a_vault: Pubkey::new_unique(),
2457 b_vault: Pubkey::new_unique(),
2458 a_token_vault: Pubkey::new_unique(),
2459 b_token_vault: Pubkey::new_unique(),
2460 a_vault_lp_mint: Pubkey::new_unique(),
2461 b_vault_lp_mint: Pubkey::new_unique(),
2462 a_vault_lp: Pubkey::new_unique(),
2463 b_vault_lp: Pubkey::new_unique(),
2464 payer_token_a: Pubkey::new_unique(),
2465 payer_token_b: Pubkey::new_unique(),
2466 payer_pool_lp: Pubkey::new_unique(),
2467 protocol_token_a_fee: Pubkey::new_unique(),
2468 protocol_token_b_fee: Pubkey::new_unique(),
2469 payer: Pubkey::new_unique(),
2470 rent: Pubkey::new_unique(),
2471 mint_metadata: Pubkey::new_unique(),
2472 metadata_program: Pubkey::new_unique(),
2473 vault_program: Pubkey::new_unique(),
2474 token_program: Pubkey::new_unique(),
2475 associated_token_program: Pubkey::new_unique(),
2476 system_program: Pubkey::new_unique(),
2477 ..Default::default()
2478 };
2479 let log = MeteoraPoolsPoolCreatedEvent {
2480 token_a_in_amount: 99,
2481 activation_point: Some(99),
2482 pool_type: 1,
2483 token_program: Pubkey::new_unique(),
2484 ..Default::default()
2485 };
2486 for log_first in [false, true] {
2487 let mut result = DexEvent::MeteoraPoolsPoolCreated(if log_first {
2488 log.clone()
2489 } else {
2490 ix.clone()
2491 });
2492 if log_first {
2493 merge_grpc_instruction_into_log(
2494 &mut result,
2495 DexEvent::MeteoraPoolsPoolCreated(ix.clone()),
2496 );
2497 } else {
2498 merge_events(&mut result, DexEvent::MeteoraPoolsPoolCreated(log.clone()));
2499 }
2500 let DexEvent::MeteoraPoolsPoolCreated(e) = result else { panic!("create") };
2501 assert_eq!(
2502 (e.token_a_in_amount, e.token_b_in_amount, e.activation_point),
2503 (0, u64::MAX, activation_point)
2504 );
2505 assert_eq!(e.pool_type, 1);
2506 assert_eq!(e.pool, ix.pool);
2507 assert_eq!(e.config, ix.config);
2508 assert_eq!(e.lp_mint, ix.lp_mint);
2509 assert_eq!(e.token_a_mint, ix.token_a_mint);
2510 assert_eq!(e.token_b_mint, ix.token_b_mint);
2511 assert_eq!(e.a_vault, ix.a_vault);
2512 assert_eq!(e.b_vault, ix.b_vault);
2513 assert_eq!(e.a_token_vault, ix.a_token_vault);
2514 assert_eq!(e.b_token_vault, ix.b_token_vault);
2515 assert_eq!(e.a_vault_lp_mint, ix.a_vault_lp_mint);
2516 assert_eq!(e.b_vault_lp_mint, ix.b_vault_lp_mint);
2517 assert_eq!(e.a_vault_lp, ix.a_vault_lp);
2518 assert_eq!(e.b_vault_lp, ix.b_vault_lp);
2519 assert_eq!(e.payer_token_a, ix.payer_token_a);
2520 assert_eq!(e.payer_token_b, ix.payer_token_b);
2521 assert_eq!(e.payer_pool_lp, ix.payer_pool_lp);
2522 assert_eq!(e.protocol_token_a_fee, ix.protocol_token_a_fee);
2523 assert_eq!(e.protocol_token_b_fee, ix.protocol_token_b_fee);
2524 assert_eq!(e.payer, ix.payer);
2525 assert_eq!(e.rent, ix.rent);
2526 assert_eq!(e.mint_metadata, ix.mint_metadata);
2527 assert_eq!(e.metadata_program, ix.metadata_program);
2528 assert_eq!(e.vault_program, ix.vault_program);
2529 assert_eq!(e.associated_token_program, ix.associated_token_program);
2530 assert_eq!(e.system_program, ix.system_program);
2531 assert_eq!(e.token_program, log.token_program);
2532 }
2533 }
2534 }
2535 #[test]
2536 fn fees_both_paths_keep_partner_parameter_and_log_fee_values() {
2537 let ix = MeteoraPoolsSetPoolFeesEvent {
2538 ix_name: "set_pool_fees".into(),
2539 new_partner_fee_numerator: 0,
2540 fee_operator: Pubkey::new_unique(),
2541 pool: Pubkey::new_unique(),
2542 ..Default::default()
2543 };
2544 let log = MeteoraPoolsSetPoolFeesEvent {
2545 new_partner_fee_numerator: 99,
2546 trade_fee_numerator: 1,
2547 trade_fee_denominator: 2,
2548 protocol_trade_fee_numerator: 3,
2549 protocol_trade_fee_denominator: 4,
2550 owner_trade_fee_numerator: 3,
2551 owner_trade_fee_denominator: 4,
2552 ..Default::default()
2553 };
2554 for log_first in [false, true] {
2555 let mut result =
2556 DexEvent::MeteoraPoolsSetPoolFees(if log_first { log.clone() } else { ix.clone() });
2557 if log_first {
2558 merge_grpc_instruction_into_log(
2559 &mut result,
2560 DexEvent::MeteoraPoolsSetPoolFees(ix.clone()),
2561 );
2562 } else {
2563 merge_events(&mut result, DexEvent::MeteoraPoolsSetPoolFees(log.clone()));
2564 }
2565 let DexEvent::MeteoraPoolsSetPoolFees(e) = result else { panic!("fees") };
2566 assert_eq!(e.new_partner_fee_numerator, 0);
2567 assert_eq!((e.pool, e.fee_operator), (ix.pool, ix.fee_operator));
2568 assert_eq!(
2569 (
2570 e.trade_fee_numerator,
2571 e.trade_fee_denominator,
2572 e.protocol_trade_fee_numerator,
2573 e.protocol_trade_fee_denominator
2574 ),
2575 (1, 2, 3, 4)
2576 );
2577 }
2578 }
2579}
2580
2581#[cfg(test)]
2582mod remaining_creation_merge_tests {
2583 use super::*;
2584 #[test]
2585 fn both_paths_preserve_curve_custom_parameters_and_clear_absent_options() {
2586 for populated in [false, true] {
2587 let ix = MeteoraPoolsPoolCreatedEvent {
2588 ix_name: "initialize_customizable_permissionless_constant_product_pool".into(),
2589 stable_curve: populated
2590 .then(|| MeteoraPoolsStableCurveParams { amp: u64::MAX, ..Default::default() }),
2591 trade_fee_bps: populated.then_some(0),
2592 customizable_params: populated.then(|| MeteoraPoolsCustomizableParams {
2593 trade_fee_numerator: u32::MAX,
2594 padding: vec![255; 90],
2595 ..Default::default()
2596 }),
2597 admin: Pubkey::new_unique(),
2598 admin_token_a: Pubkey::new_unique(),
2599 admin_token_b: Pubkey::new_unique(),
2600 admin_pool_lp: Pubkey::new_unique(),
2601 fee_owner: Pubkey::new_unique(),
2602 ..Default::default()
2603 };
2604 let log = MeteoraPoolsPoolCreatedEvent {
2605 pool_type: 1,
2606 stable_curve: Some(MeteoraPoolsStableCurveParams::default()),
2607 trade_fee_bps: Some(99),
2608 customizable_params: Some(MeteoraPoolsCustomizableParams::default()),
2609 ..Default::default()
2610 };
2611 for log_first in [false, true] {
2612 let mut merged = DexEvent::MeteoraPoolsPoolCreated(if log_first {
2613 log.clone()
2614 } else {
2615 ix.clone()
2616 });
2617 if log_first {
2618 merge_grpc_instruction_into_log(
2619 &mut merged,
2620 DexEvent::MeteoraPoolsPoolCreated(ix.clone()),
2621 );
2622 } else {
2623 merge_events(&mut merged, DexEvent::MeteoraPoolsPoolCreated(log.clone()));
2624 }
2625 let DexEvent::MeteoraPoolsPoolCreated(e) = merged else { panic!("create") };
2626 assert_eq!(e.stable_curve, ix.stable_curve);
2627 assert_eq!(e.trade_fee_bps, ix.trade_fee_bps);
2628 assert_eq!(e.customizable_params, ix.customizable_params);
2629 assert_eq!(
2630 (e.admin, e.admin_token_a, e.admin_token_b, e.admin_pool_lp, e.fee_owner),
2631 (ix.admin, ix.admin_token_a, ix.admin_token_b, ix.admin_pool_lp, ix.fee_owner)
2632 );
2633 assert_eq!(e.pool_type, 1);
2634 }
2635 }
2636 }
2637}
2638
2639#[cfg(test)]
2640mod amm_swap_parameter_merge_tests {
2641 use super::*;
2642 #[test]
2643 fn both_paths_preserve_zero_parameters_execution_and_account_context() {
2644 for name in ["swap_base_in", "swap_base_out", "swap_base_in_v2", "swap_base_out_v2"] {
2645 let input = name.starts_with("swap_base_in");
2646 let ix = RaydiumAmmV4SwapEvent {
2647 ix_name: name.into(),
2648 instruction_amount_in: 0,
2649 instruction_amount_out: 0,
2650 minimum_amount_out: if input { u64::MAX } else { 0 },
2651 max_amount_in: if input { 0 } else { u64::MAX },
2652 user_source_owner: Pubkey::new_unique(),
2653 user_source_token_account: Pubkey::new_unique(),
2654 user_destination_token_account: Pubkey::new_unique(),
2655 amm_target_orders: Some(Pubkey::new_unique()),
2656 ..Default::default()
2657 };
2658 let log = RaydiumAmmV4SwapEvent {
2659 amount_in: 123,
2660 amount_out: 456,
2661 instruction_amount_in: 99,
2662 instruction_amount_out: 99,
2663 minimum_amount_out: 99,
2664 max_amount_in: 99,
2665 token_program: Pubkey::new_unique(),
2666 ..Default::default()
2667 };
2668 for log_first in [false, true] {
2669 let mut merged =
2670 DexEvent::RaydiumAmmV4Swap(if log_first { log.clone() } else { ix.clone() });
2671 if log_first {
2672 merge_grpc_instruction_into_log(
2673 &mut merged,
2674 DexEvent::RaydiumAmmV4Swap(ix.clone()),
2675 );
2676 } else {
2677 merge_events(&mut merged, DexEvent::RaydiumAmmV4Swap(log.clone()));
2678 }
2679 let DexEvent::RaydiumAmmV4Swap(e) = merged else { panic!("swap") };
2680 assert_eq!(e.ix_name, name);
2681 assert_eq!((e.amount_in, e.amount_out), (123, 456));
2682 assert_eq!((e.instruction_amount_in, e.instruction_amount_out), (0, 0));
2683 assert_eq!(
2684 (e.minimum_amount_out, e.max_amount_in),
2685 (ix.minimum_amount_out, ix.max_amount_in)
2686 );
2687 assert_eq!(
2688 (
2689 e.user_source_owner,
2690 e.user_source_token_account,
2691 e.user_destination_token_account
2692 ),
2693 (
2694 ix.user_source_owner,
2695 ix.user_source_token_account,
2696 ix.user_destination_token_account
2697 )
2698 );
2699 assert_eq!(e.amm_target_orders, ix.amm_target_orders);
2700 assert_eq!(e.token_program, log.token_program);
2701 }
2702 }
2703 }
2704}