Skip to main content

sol_parser_sdk/core/
merger.rs

1//! 轻量级事件合并机制 - 零拷贝高性能实现
2//!
3//! 将 inner instruction 事件数据合并到主 instruction 事件中
4//! 设计原则:
5//! - 只合并必要的字段
6//! - 保持零拷贝特性
7//! - 内联优化,最小化开销
8
9use crate::core::events::*;
10
11/// 合并 instruction 事件和 inner instruction 事件
12///
13/// # 设计
14/// - Inner instruction 包含完整的交易数据(来自程序日志)
15/// - Instruction 包含账户上下文(来自指令本身)
16/// - 合并后的事件包含两者的完整信息
17///
18/// # 性能
19/// - 内联优化,编译器会将其优化为直接赋值
20/// - 零堆分配
21/// - 预期开销 < 10ns
22#[inline(always)]
23pub fn merge_events(base: &mut DexEvent, inner: DexEvent) {
24    use DexEvent::*;
25
26    match (base, inner) {
27        // ========== PumpFun 系列 ==========
28        (PumpFunTrade(b), PumpFunTrade(i)) | (PumpFunTrade(b), PumpFunBuy(i))
29        | (PumpFunTrade(b), PumpFunSell(i)) | (PumpFunTrade(b), PumpFunBuyExactSolIn(i))
30        | (PumpFunBuy(b), PumpFunTrade(i)) | (PumpFunBuy(b), PumpFunBuy(i))
31        | (PumpFunSell(b), PumpFunTrade(i)) | (PumpFunSell(b), PumpFunSell(i))
32        | (PumpFunBuyExactSolIn(b), PumpFunTrade(i)) | (PumpFunBuyExactSolIn(b), PumpFunBuyExactSolIn(i))
33            => merge_pumpfun_trade(b, i),
34
35        (PumpFunCreate(b), PumpFunCreate(i)) => merge_pumpfun_create(b, i),
36        (PumpFunMigrate(b), PumpFunMigrate(i)) => merge_pumpfun_migrate(b, i),
37
38        // ========== PumpSwap 系列 ==========
39        (PumpSwapBuy(b), PumpSwapBuy(i)) => merge_generic(b, i),
40        (PumpSwapSell(b), PumpSwapSell(i)) => merge_generic(b, i),
41        (PumpSwapCreatePool(b), PumpSwapCreatePool(i)) => merge_generic(b, i),
42        (PumpSwapLiquidityAdded(b), PumpSwapLiquidityAdded(i)) => merge_generic(b, i),
43        (PumpSwapLiquidityRemoved(b), PumpSwapLiquidityRemoved(i)) => merge_generic(b, i),
44
45        // ========== Raydium CLMM 系列 ==========
46        (RaydiumClmmSwap(b), RaydiumClmmSwap(i)) => merge_generic(b, i),
47        (RaydiumClmmIncreaseLiquidity(b), RaydiumClmmIncreaseLiquidity(i)) => merge_generic(b, i),
48        (RaydiumClmmDecreaseLiquidity(b), RaydiumClmmDecreaseLiquidity(i)) => merge_generic(b, i),
49        (RaydiumClmmCreatePool(b), RaydiumClmmCreatePool(i)) => merge_generic(b, i),
50        (RaydiumClmmCollectFee(b), RaydiumClmmCollectFee(i)) => merge_generic(b, i),
51
52        // ========== Raydium CPMM 系列 ==========
53        (RaydiumCpmmSwap(b), RaydiumCpmmSwap(i)) => merge_generic(b, i),
54        (RaydiumCpmmDeposit(b), RaydiumCpmmDeposit(i)) => merge_generic(b, i),
55        (RaydiumCpmmWithdraw(b), RaydiumCpmmWithdraw(i)) => merge_generic(b, i),
56
57        // ========== Raydium AMM V4 系列 ==========
58        (RaydiumAmmV4Swap(b), RaydiumAmmV4Swap(i)) => merge_generic(b, i),
59        (RaydiumAmmV4Deposit(b), RaydiumAmmV4Deposit(i)) => merge_generic(b, i),
60        (RaydiumAmmV4Withdraw(b), RaydiumAmmV4Withdraw(i)) => merge_generic(b, i),
61
62        // ========== Orca Whirlpool 系列 ==========
63        (OrcaWhirlpoolSwap(b), OrcaWhirlpoolSwap(i)) => merge_generic(b, i),
64        (OrcaWhirlpoolLiquidityIncreased(b), OrcaWhirlpoolLiquidityIncreased(i)) => merge_generic(b, i),
65        (OrcaWhirlpoolLiquidityDecreased(b), OrcaWhirlpoolLiquidityDecreased(i)) => merge_generic(b, i),
66
67        // ========== Meteora Pools (AMM) 系列 ==========
68        (MeteoraPoolsSwap(b), MeteoraPoolsSwap(i)) => merge_generic(b, i),
69        (MeteoraPoolsAddLiquidity(b), MeteoraPoolsAddLiquidity(i)) => merge_generic(b, i),
70        (MeteoraPoolsRemoveLiquidity(b), MeteoraPoolsRemoveLiquidity(i)) => merge_generic(b, i),
71
72        // ========== Meteora DAMM V2 系列 ==========
73        (MeteoraDammV2Swap(b), MeteoraDammV2Swap(i)) => merge_generic(b, i),
74        (MeteoraDammV2AddLiquidity(b), MeteoraDammV2AddLiquidity(i)) => merge_generic(b, i),
75        (MeteoraDammV2RemoveLiquidity(b), MeteoraDammV2RemoveLiquidity(i)) => merge_generic(b, i),
76        (MeteoraDammV2CreatePosition(b), MeteoraDammV2CreatePosition(i)) => merge_generic(b, i),
77        (MeteoraDammV2ClosePosition(b), MeteoraDammV2ClosePosition(i)) => merge_generic(b, i),
78
79        // ========== Bonk 系列 ==========
80        (BonkTrade(b), BonkTrade(i)) => merge_generic(b, i),
81
82        // 其他组合不需要合并(类型不匹配)
83        _ => {}
84    }
85}
86
87/// 通用合并函数 - 对于大多数事件,inner instruction 包含完整数据
88///
89/// 这个函数简单地用 inner 的数据覆盖 base,因为:
90/// - Inner instruction 来自程序日志,包含完整的交易数据
91/// - Instruction 主要提供账户上下文
92/// - 对于大多数协议,inner instruction 的数据已经足够完整
93#[inline(always)]
94fn merge_generic<T>(base: &mut T, inner: T) {
95    *base = inner;
96}
97
98// ============================================================================
99// PumpFun 事件合并实现
100// ============================================================================
101
102/// 合并 PumpFun Trade 事件
103///
104/// 合并策略:
105/// - Inner instruction 提供: 交易数据(amount, reserves, fees 等)
106/// - Instruction 提供: 账户上下文(bonding_curve, associated_bonding_curve 等)
107/// - 合并后: 完整的交易事件
108#[inline(always)]
109fn merge_pumpfun_trade(base: &mut PumpFunTradeEvent, inner: PumpFunTradeEvent) {
110    // 从 inner instruction 合并交易核心数据
111    base.mint = inner.mint;
112    base.sol_amount = inner.sol_amount;
113    base.token_amount = inner.token_amount;
114    base.is_buy = inner.is_buy;
115    base.user = inner.user;
116    base.timestamp = inner.timestamp;
117    base.virtual_sol_reserves = inner.virtual_sol_reserves;
118    base.virtual_token_reserves = inner.virtual_token_reserves;
119    base.real_sol_reserves = inner.real_sol_reserves;
120    base.real_token_reserves = inner.real_token_reserves;
121    base.fee_recipient = inner.fee_recipient;
122    base.fee_basis_points = inner.fee_basis_points;
123    base.fee = inner.fee;
124    base.creator = inner.creator;
125    base.creator_fee_basis_points = inner.creator_fee_basis_points;
126    base.creator_fee = inner.creator_fee;
127
128    // 可选字段
129    base.track_volume = inner.track_volume;
130    base.total_unclaimed_tokens = inner.total_unclaimed_tokens;
131    base.total_claimed_tokens = inner.total_claimed_tokens;
132    base.current_sol_volume = inner.current_sol_volume;
133    base.last_update_timestamp = inner.last_update_timestamp;
134    base.ix_name = inner.ix_name;
135    base.is_created_buy = inner.is_created_buy;
136    base.mayhem_mode = inner.mayhem_mode;
137    base.cashback_fee_basis_points = inner.cashback_fee_basis_points;
138    base.cashback = inner.cashback;
139    base.is_cashback_coin = inner.is_cashback_coin;
140
141    // 保留 base 的账户上下文字段(bonding_curve, associated_bonding_curve 等)
142    // 这些字段来自 instruction,不被 inner instruction 覆盖
143}
144
145/// 合并 PumpFun Create 事件
146#[inline(always)]
147fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
148    // Inner instruction 包含完整的 create 数据
149    base.name = inner.name;
150    base.symbol = inner.symbol;
151    base.uri = inner.uri;
152    base.mint = inner.mint;
153    base.bonding_curve = inner.bonding_curve;
154    base.user = inner.user;
155    base.creator = inner.creator;
156    base.timestamp = inner.timestamp;
157    base.virtual_token_reserves = inner.virtual_token_reserves;
158    base.virtual_sol_reserves = inner.virtual_sol_reserves;
159    base.real_token_reserves = inner.real_token_reserves;
160    base.token_total_supply = inner.token_total_supply;
161    base.token_program = inner.token_program;
162    base.is_mayhem_mode = inner.is_mayhem_mode;
163}
164
165/// 合并 PumpFun Migrate 事件
166#[inline(always)]
167fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
168    // Inner instruction 包含完整的 migrate 数据
169    base.user = inner.user;
170    base.mint = inner.mint;
171    base.mint_amount = inner.mint_amount;
172    base.sol_amount = inner.sol_amount;
173    base.pool_migration_fee = inner.pool_migration_fee;
174    base.bonding_curve = inner.bonding_curve;
175    base.timestamp = inner.timestamp;
176    base.pool = inner.pool;
177}
178
179// ============================================================================
180// 工具函数
181// ============================================================================
182
183/// 判断两个事件是否可以合并
184///
185/// 合并条件:
186/// 1. 都是同一个协议的事件
187/// 2. 事件类型兼容(例如 Trade 和 Buy 可以合并)
188/// 3. 来自同一个交易(signature 相同)
189#[inline(always)]
190pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
191    // 检查 signature 是否相同
192    if base.metadata().signature != inner.metadata().signature {
193        return false;
194    }
195
196    // 检查事件类型是否兼容
197    match (base, inner) {
198        // PumpFun Trade 系列事件可以互相合并
199        (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
200        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
201        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
202        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
203        | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
204        | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
205        | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
206        | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
207        | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
208        | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
209
210        // PumpFun Create 可以合并
211        (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
212
213        // PumpFun Migrate 可以合并
214        (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
215
216        // 其他组合不支持合并
217        _ => false,
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224    use solana_sdk::{pubkey::Pubkey, signature::Signature};
225
226    #[test]
227    fn test_merge_pumpfun_trade() {
228        let metadata = EventMetadata {
229            signature: Signature::default(),
230            slot: 100,
231            tx_index: 1,
232            block_time_us: 1000,
233            grpc_recv_us: 2000,
234        };
235
236        // Base event 来自 instruction(包含账户上下文)
237        let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
238            metadata: metadata.clone(),
239            bonding_curve: Pubkey::new_unique(),
240            associated_bonding_curve: Pubkey::new_unique(),
241            ..Default::default()
242        });
243
244        // Inner event 来自 inner instruction(包含交易数据)
245        let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
246            metadata: metadata.clone(),
247            mint: Pubkey::new_unique(),
248            sol_amount: 1000,
249            token_amount: 2000,
250            is_buy: true,
251            user: Pubkey::new_unique(),
252            ..Default::default()
253        });
254
255        // 合并
256        merge_events(&mut base, inner);
257
258        // 验证合并结果
259        if let DexEvent::PumpFunTrade(trade) = base {
260            assert_eq!(trade.sol_amount, 1000);
261            assert_eq!(trade.token_amount, 2000);
262            assert!(trade.is_buy);
263            // 账户上下文保留
264            assert_ne!(trade.bonding_curve, Pubkey::default());
265            assert_ne!(trade.associated_bonding_curve, Pubkey::default());
266        } else {
267            panic!("Expected PumpFunTrade event");
268        }
269    }
270
271    #[test]
272    fn test_can_merge() {
273        let metadata = EventMetadata {
274            signature: Signature::default(),
275            slot: 100,
276            tx_index: 1,
277            block_time_us: 1000,
278            grpc_recv_us: 2000,
279        };
280
281        let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
282            metadata: metadata.clone(),
283            ..Default::default()
284        });
285
286        let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
287            metadata: metadata.clone(),
288            ..Default::default()
289        });
290
291        // 应该可以合并(同一个 signature,兼容类型)
292        assert!(can_merge(&base, &inner));
293
294        // 不同 signature 不能合并
295        let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
296            metadata: EventMetadata {
297                signature: Signature::new_unique(),
298                ..metadata
299            },
300            ..Default::default()
301        });
302
303        assert!(!can_merge(&base, &different_sig));
304    }
305}