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
136    // 保留 base 的账户上下文字段(bonding_curve, associated_bonding_curve 等)
137    // 这些字段来自 instruction,不被 inner instruction 覆盖
138}
139
140/// 合并 PumpFun Create 事件
141#[inline(always)]
142fn merge_pumpfun_create(base: &mut PumpFunCreateTokenEvent, inner: PumpFunCreateTokenEvent) {
143    // Inner instruction 包含完整的 create 数据
144    base.name = inner.name;
145    base.symbol = inner.symbol;
146    base.uri = inner.uri;
147    base.mint = inner.mint;
148    base.bonding_curve = inner.bonding_curve;
149    base.user = inner.user;
150    base.creator = inner.creator;
151    base.timestamp = inner.timestamp;
152    base.virtual_token_reserves = inner.virtual_token_reserves;
153    base.virtual_sol_reserves = inner.virtual_sol_reserves;
154    base.real_token_reserves = inner.real_token_reserves;
155    base.token_total_supply = inner.token_total_supply;
156    base.token_program = inner.token_program;
157    base.is_mayhem_mode = inner.is_mayhem_mode;
158}
159
160/// 合并 PumpFun Migrate 事件
161#[inline(always)]
162fn merge_pumpfun_migrate(base: &mut PumpFunMigrateEvent, inner: PumpFunMigrateEvent) {
163    // Inner instruction 包含完整的 migrate 数据
164    base.user = inner.user;
165    base.mint = inner.mint;
166    base.mint_amount = inner.mint_amount;
167    base.sol_amount = inner.sol_amount;
168    base.pool_migration_fee = inner.pool_migration_fee;
169    base.bonding_curve = inner.bonding_curve;
170    base.timestamp = inner.timestamp;
171    base.pool = inner.pool;
172}
173
174// ============================================================================
175// 工具函数
176// ============================================================================
177
178/// 判断两个事件是否可以合并
179///
180/// 合并条件:
181/// 1. 都是同一个协议的事件
182/// 2. 事件类型兼容(例如 Trade 和 Buy 可以合并)
183/// 3. 来自同一个交易(signature 相同)
184#[inline(always)]
185pub fn can_merge(base: &DexEvent, inner: &DexEvent) -> bool {
186    // 检查 signature 是否相同
187    if base.metadata().signature != inner.metadata().signature {
188        return false;
189    }
190
191    // 检查事件类型是否兼容
192    match (base, inner) {
193        // PumpFun Trade 系列事件可以互相合并
194        (DexEvent::PumpFunTrade(_), DexEvent::PumpFunTrade(_))
195        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuy(_))
196        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunSell(_))
197        | (DexEvent::PumpFunTrade(_), DexEvent::PumpFunBuyExactSolIn(_))
198        | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunTrade(_))
199        | (DexEvent::PumpFunBuy(_), DexEvent::PumpFunBuy(_))
200        | (DexEvent::PumpFunSell(_), DexEvent::PumpFunTrade(_))
201        | (DexEvent::PumpFunSell(_), DexEvent::PumpFunSell(_))
202        | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunTrade(_))
203        | (DexEvent::PumpFunBuyExactSolIn(_), DexEvent::PumpFunBuyExactSolIn(_)) => true,
204
205        // PumpFun Create 可以合并
206        (DexEvent::PumpFunCreate(_), DexEvent::PumpFunCreate(_)) => true,
207
208        // PumpFun Migrate 可以合并
209        (DexEvent::PumpFunMigrate(_), DexEvent::PumpFunMigrate(_)) => true,
210
211        // 其他组合不支持合并
212        _ => false,
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219    use solana_sdk::{pubkey::Pubkey, signature::Signature};
220
221    #[test]
222    fn test_merge_pumpfun_trade() {
223        let metadata = EventMetadata {
224            signature: Signature::default(),
225            slot: 100,
226            tx_index: 1,
227            block_time_us: 1000,
228            grpc_recv_us: 2000,
229        };
230
231        // Base event 来自 instruction(包含账户上下文)
232        let mut base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
233            metadata: metadata.clone(),
234            bonding_curve: Pubkey::new_unique(),
235            associated_bonding_curve: Pubkey::new_unique(),
236            ..Default::default()
237        });
238
239        // Inner event 来自 inner instruction(包含交易数据)
240        let inner = DexEvent::PumpFunTrade(PumpFunTradeEvent {
241            metadata: metadata.clone(),
242            mint: Pubkey::new_unique(),
243            sol_amount: 1000,
244            token_amount: 2000,
245            is_buy: true,
246            user: Pubkey::new_unique(),
247            ..Default::default()
248        });
249
250        // 合并
251        merge_events(&mut base, inner);
252
253        // 验证合并结果
254        if let DexEvent::PumpFunTrade(trade) = base {
255            assert_eq!(trade.sol_amount, 1000);
256            assert_eq!(trade.token_amount, 2000);
257            assert!(trade.is_buy);
258            // 账户上下文保留
259            assert_ne!(trade.bonding_curve, Pubkey::default());
260            assert_ne!(trade.associated_bonding_curve, Pubkey::default());
261        } else {
262            panic!("Expected PumpFunTrade event");
263        }
264    }
265
266    #[test]
267    fn test_can_merge() {
268        let metadata = EventMetadata {
269            signature: Signature::default(),
270            slot: 100,
271            tx_index: 1,
272            block_time_us: 1000,
273            grpc_recv_us: 2000,
274        };
275
276        let base = DexEvent::PumpFunTrade(PumpFunTradeEvent {
277            metadata: metadata.clone(),
278            ..Default::default()
279        });
280
281        let inner = DexEvent::PumpFunBuy(PumpFunTradeEvent {
282            metadata: metadata.clone(),
283            ..Default::default()
284        });
285
286        // 应该可以合并(同一个 signature,兼容类型)
287        assert!(can_merge(&base, &inner));
288
289        // 不同 signature 不能合并
290        let different_sig = DexEvent::PumpFunTrade(PumpFunTradeEvent {
291            metadata: EventMetadata {
292                signature: Signature::new_unique(),
293                ..metadata
294            },
295            ..Default::default()
296        });
297
298        assert!(!can_merge(&base, &different_sig));
299    }
300}