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