mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-15 01:48:05 +00:00
refactor: upgrade to v1.0.0 with enum-based event architecture
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-streamer-sdk"
|
||||
version = "0.5.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
|
||||
repository = "https://github.com/0xfnzero/solana-streamer"
|
||||
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
# Migration Guide: v0.5.x to v1.0.0
|
||||
|
||||
## Overview
|
||||
|
||||
Version 1.0.0 introduces a significant architectural improvement by transitioning from a trait-based event system to an enum-based event system. This change brings:
|
||||
|
||||
- **Better Type Safety**: Compile-time guarantees for event types
|
||||
- **Improved Performance**: Eliminates dynamic dispatch overhead (no `Box<dyn Trait>`)
|
||||
- **Simpler Code**: Standard Rust patterns instead of custom macros
|
||||
- **Better IDE Support**: Full autocomplete and type inference
|
||||
|
||||
## Breaking Changes Summary
|
||||
|
||||
| Component | v0.5.x | v1.0.0 |
|
||||
| ------------------ | --------------------------- | --------------------------- |
|
||||
| Event Type | `Box<dyn UnifiedEvent>` | `DexEvent` (enum) |
|
||||
| Callback Signature | `Fn(Box<dyn UnifiedEvent>)` | `Fn(DexEvent)` |
|
||||
| Event Matching | `match_event!` macro | Standard `match` expression |
|
||||
| Metadata Access | `.event_type()` | `.metadata().event_type` |
|
||||
| Event Properties | `.signature()` | `.metadata().signature` |
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Update Callback Signatures
|
||||
|
||||
**Before (v0.5.x):**
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::UnifiedEvent;
|
||||
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!("Received event: {:?}", event);
|
||||
};
|
||||
```
|
||||
|
||||
**After (v1.0.0):**
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::DexEvent;
|
||||
|
||||
let callback = |event: DexEvent| {
|
||||
println!("Received event: {:?}", event);
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Update Event Matching
|
||||
|
||||
**Before (v0.5.x):**
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::match_event;
|
||||
|
||||
match_event!(event, {
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFun trade: {:?}", e);
|
||||
},
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("Raydium swap: {:?}", e);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**After (v1.0.0):**
|
||||
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => {
|
||||
println!("PumpFun trade: {:?}", e);
|
||||
}
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => {
|
||||
println!("Raydium swap: {:?}", e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Update Metadata Access
|
||||
|
||||
**Before (v0.5.x):**
|
||||
|
||||
```rust
|
||||
let event_type = event.event_type();
|
||||
let signature = event.signature();
|
||||
let slot = event.slot();
|
||||
let protocol = event.protocol();
|
||||
```
|
||||
|
||||
**After (v1.0.0):**
|
||||
|
||||
```rust
|
||||
let event_type = event.metadata().event_type;
|
||||
let signature = event.metadata().signature;
|
||||
let slot = event.metadata().slot;
|
||||
let protocol = event.metadata().protocol;
|
||||
```
|
||||
|
||||
### Step 4: Update Import Statements
|
||||
|
||||
**Before (v0.5.x):**
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::event_parser::{
|
||||
UnifiedEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::{RaydiumCpmmSwapEvent},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**After (v1.0.0):**
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::{
|
||||
DexEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::{RaydiumCpmmSwapEvent},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Note: The `match_event!` macro is no longer needed or available.
|
||||
|
||||
## Complete Example Migration
|
||||
|
||||
### Before (v0.5.x)
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
UnifiedEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
},
|
||||
},
|
||||
YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let grpc = YellowstoneGrpc::new(
|
||||
"grpc-endpoint".to_string(),
|
||||
Some("api-key".to_string()),
|
||||
)?;
|
||||
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!(
|
||||
"Event type: {:?}, Signature: {}",
|
||||
event.event_type(),
|
||||
event.signature()
|
||||
);
|
||||
|
||||
match_event!(event, {
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFun trade: {} SOL", e.sol_amount);
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("New token: {}", e.name);
|
||||
},
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("Raydium swap");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
grpc.subscribe_events(
|
||||
protocols,
|
||||
event_filter,
|
||||
tx_filter,
|
||||
account_filter,
|
||||
callback,
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### After (v1.0.0)
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::{
|
||||
event_parser::{
|
||||
DexEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
},
|
||||
},
|
||||
YellowstoneGrpc,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let grpc = YellowstoneGrpc::new(
|
||||
"grpc-endpoint".to_string(),
|
||||
Some("api-key".to_string()),
|
||||
)?;
|
||||
|
||||
let callback = |event: DexEvent| {
|
||||
println!(
|
||||
"Event type: {:?}, Signature: {}",
|
||||
event.metadata().event_type,
|
||||
event.metadata().signature
|
||||
);
|
||||
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => {
|
||||
println!("PumpFun trade: {} SOL", e.sol_amount);
|
||||
}
|
||||
DexEvent::PumpFunCreateTokenEvent(e) => {
|
||||
println!("New token: {}", e.name);
|
||||
}
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => {
|
||||
println!("Raydium swap");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
grpc.subscribe_events(
|
||||
protocols,
|
||||
event_filter,
|
||||
tx_filter,
|
||||
account_filter,
|
||||
callback,
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern 1: Event Filtering with Match
|
||||
|
||||
**v1.0.0:**
|
||||
|
||||
```rust
|
||||
let callback = |event: DexEvent| {
|
||||
// Only process specific event types
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) if e.is_buy => {
|
||||
println!("Buy: {} tokens", e.token_amount);
|
||||
}
|
||||
DexEvent::PumpFunTradeEvent(e) if !e.is_buy => {
|
||||
println!("Sell: {} tokens", e.token_amount);
|
||||
}
|
||||
_ => {} // Ignore other events
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Pattern 2: Generic Event Processing
|
||||
|
||||
**v1.0.0:**
|
||||
|
||||
```rust
|
||||
fn process_event(event: DexEvent) {
|
||||
let metadata = event.metadata();
|
||||
|
||||
println!("Protocol: {:?}", metadata.protocol);
|
||||
println!("Event Type: {:?}", metadata.event_type);
|
||||
println!("Signature: {}", metadata.signature);
|
||||
println!("Slot: {}", metadata.slot);
|
||||
|
||||
// Process specific event types
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => handle_pumpfun_trade(e),
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => handle_raydium_swap(e),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: Event Type Categorization
|
||||
|
||||
**v1.0.0:**
|
||||
|
||||
```rust
|
||||
fn categorize_event(event: &DexEvent) -> &'static str {
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(_)
|
||||
| DexEvent::PumpSwapBuyEvent(_)
|
||||
| DexEvent::PumpSwapSellEvent(_) => "Trade",
|
||||
|
||||
DexEvent::PumpFunCreateTokenEvent(_)
|
||||
| DexEvent::PumpSwapCreatePoolEvent(_) => "Creation",
|
||||
|
||||
DexEvent::RaydiumCpmmDepositEvent(_)
|
||||
| DexEvent::RaydiumCpmmWithdrawEvent(_) => "Liquidity",
|
||||
|
||||
_ => "Other",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## EventParser API Changes
|
||||
|
||||
### Parsing Transactions
|
||||
|
||||
**Before (v0.5.x):**
|
||||
|
||||
```rust
|
||||
let parser = Arc::new(EventParser::new(protocols, event_filter));
|
||||
parser.parse_encoded_confirmed_transaction_with_status_meta(
|
||||
signature,
|
||||
transaction,
|
||||
Arc::new(|event: &Box<dyn UnifiedEvent>| {
|
||||
println!("{:?}", event);
|
||||
}),
|
||||
).await?;
|
||||
```
|
||||
|
||||
**After (v1.0.0):**
|
||||
|
||||
```rust
|
||||
EventParser::parse_encoded_confirmed_transaction_with_status_meta(
|
||||
&protocols,
|
||||
event_filter.as_ref(),
|
||||
signature,
|
||||
transaction,
|
||||
Arc::new(|event: &DexEvent| {
|
||||
println!("{:?}", event);
|
||||
}),
|
||||
).await?;
|
||||
```
|
||||
|
||||
The `EventParser` is now stateless with static methods, eliminating the need to create an instance.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: Forgetting to Handle All Variants
|
||||
|
||||
❌ **Incorrect:**
|
||||
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
// Missing other variants!
|
||||
}
|
||||
```
|
||||
|
||||
✅ **Correct:**
|
||||
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
_ => {} // Handle or ignore other events
|
||||
}
|
||||
```
|
||||
|
||||
### Pitfall 2: Using Old Metadata Access Pattern
|
||||
|
||||
❌ **Incorrect:**
|
||||
|
||||
```rust
|
||||
let sig = event.signature(); // Method doesn't exist anymore
|
||||
```
|
||||
|
||||
✅ **Correct:**
|
||||
|
||||
```rust
|
||||
let sig = event.metadata().signature;
|
||||
```
|
||||
|
||||
### Pitfall 3: Attempting to Use `match_event!` Macro
|
||||
|
||||
❌ **Incorrect:**
|
||||
|
||||
```rust
|
||||
match_event!(event, { /* ... */ }); // Macro no longer exists
|
||||
```
|
||||
|
||||
✅ **Correct:**
|
||||
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
_ => {}
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits of the New System
|
||||
|
||||
1. **Type Safety**: The compiler catches more errors at compile time
|
||||
2. **Performance**: No dynamic dispatch overhead
|
||||
3. **Simplicity**: Standard Rust patterns, no custom macros
|
||||
4. **Better Tooling**: Full IDE support with autocomplete
|
||||
5. **Easier Debugging**: Clearer stack traces and error messages
|
||||
6. **Serialization**: Built-in `Serialize`/`Deserialize` support for all events
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
# 迁移指南:v0.5.x 到 v1.0.0
|
||||
|
||||
## 概述
|
||||
|
||||
版本 0.6.0 引入了一个重要的架构改进,从基于 trait 的事件系统过渡到基于 enum 的事件系统。此变更带来:
|
||||
|
||||
- **更好的类型安全**: 编译时保证事件类型
|
||||
- **改进的性能**: 消除动态分发开销(不再使用 `Box<dyn Trait>`)
|
||||
- **更简单的代码**: 标准 Rust 模式而不是自定义宏
|
||||
- **更好的 IDE 支持**: 完整的自动补全和类型推断
|
||||
|
||||
## 破坏性变更摘要
|
||||
|
||||
| 组件 | v0.5.x | v1.0.0 |
|
||||
|-----------|--------|--------|
|
||||
| 事件类型 | `Box<dyn UnifiedEvent>` | `DexEvent`(枚举) |
|
||||
| 回调签名 | `Fn(Box<dyn UnifiedEvent>)` | `Fn(DexEvent)` |
|
||||
| 事件匹配 | `match_event!` 宏 | 标准 `match` 表达式 |
|
||||
| 元数据访问 | `.event_type()` | `.metadata().event_type` |
|
||||
| 事件属性 | `.signature()` | `.metadata().signature` |
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
### 步骤 1:更新回调签名
|
||||
|
||||
**之前 (v0.5.x):**
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::UnifiedEvent;
|
||||
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!("接收到事件: {:?}", event);
|
||||
};
|
||||
```
|
||||
|
||||
**之后 (v1.0.0):**
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::DexEvent;
|
||||
|
||||
let callback = |event: DexEvent| {
|
||||
println!("接收到事件: {:?}", event);
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 2:更新事件匹配
|
||||
|
||||
**之前 (v0.5.x):**
|
||||
```rust
|
||||
use solana_streamer_sdk::match_event;
|
||||
|
||||
match_event!(event, {
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFun 交易: {:?}", e);
|
||||
},
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("Raydium 交换: {:?}", e);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**之后 (v1.0.0):**
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => {
|
||||
println!("PumpFun 交易: {:?}", e);
|
||||
}
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => {
|
||||
println!("Raydium 交换: {:?}", e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 3:更新元数据访问
|
||||
|
||||
**之前 (v0.5.x):**
|
||||
```rust
|
||||
let event_type = event.event_type();
|
||||
let signature = event.signature();
|
||||
let slot = event.slot();
|
||||
let protocol = event.protocol();
|
||||
```
|
||||
|
||||
**之后 (v1.0.0):**
|
||||
```rust
|
||||
let event_type = event.metadata().event_type;
|
||||
let signature = event.metadata().signature;
|
||||
let slot = event.metadata().slot;
|
||||
let protocol = event.metadata().protocol;
|
||||
```
|
||||
|
||||
### 步骤 4:更新导入语句
|
||||
|
||||
**之前 (v0.5.x):**
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::event_parser::{
|
||||
UnifiedEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::{RaydiumCpmmSwapEvent},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**之后 (v1.0.0):**
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::{
|
||||
DexEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::{RaydiumCpmmSwapEvent},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
注意:`match_event!` 宏不再需要或可用。
|
||||
|
||||
## 完整示例迁移
|
||||
|
||||
### 之前 (v0.5.x)
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
UnifiedEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
},
|
||||
},
|
||||
YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let grpc = YellowstoneGrpc::new(
|
||||
"grpc-endpoint".to_string(),
|
||||
Some("api-key".to_string()),
|
||||
)?;
|
||||
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!(
|
||||
"事件类型: {:?}, 签名: {}",
|
||||
event.event_type(),
|
||||
event.signature()
|
||||
);
|
||||
|
||||
match_event!(event, {
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFun 交易: {} SOL", e.sol_amount);
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("新代币: {}", e.name);
|
||||
},
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("Raydium 交换");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
grpc.subscribe_events(
|
||||
protocols,
|
||||
event_filter,
|
||||
tx_filter,
|
||||
account_filter,
|
||||
callback,
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 之后 (v1.0.0)
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::{
|
||||
event_parser::{
|
||||
DexEvent,
|
||||
protocols::{
|
||||
pumpfun::{PumpFunTradeEvent, PumpFunCreateTokenEvent},
|
||||
raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
},
|
||||
},
|
||||
YellowstoneGrpc,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let grpc = YellowstoneGrpc::new(
|
||||
"grpc-endpoint".to_string(),
|
||||
Some("api-key".to_string()),
|
||||
)?;
|
||||
|
||||
let callback = |event: DexEvent| {
|
||||
println!(
|
||||
"事件类型: {:?}, 签名: {}",
|
||||
event.metadata().event_type,
|
||||
event.metadata().signature
|
||||
);
|
||||
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => {
|
||||
println!("PumpFun 交易: {} SOL", e.sol_amount);
|
||||
}
|
||||
DexEvent::PumpFunCreateTokenEvent(e) => {
|
||||
println!("新代币: {}", e.name);
|
||||
}
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => {
|
||||
println!("Raydium 交换");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
grpc.subscribe_events(
|
||||
protocols,
|
||||
event_filter,
|
||||
tx_filter,
|
||||
account_filter,
|
||||
callback,
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## 高级模式
|
||||
|
||||
### 模式 1:使用 Match 进行事件过滤
|
||||
|
||||
**v1.0.0:**
|
||||
```rust
|
||||
let callback = |event: DexEvent| {
|
||||
// 只处理特定事件类型
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) if e.is_buy => {
|
||||
println!("买入: {} 代币", e.token_amount);
|
||||
}
|
||||
DexEvent::PumpFunTradeEvent(e) if !e.is_buy => {
|
||||
println!("卖出: {} 代币", e.token_amount);
|
||||
}
|
||||
_ => {} // 忽略其他事件
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 模式 2:通用事件处理
|
||||
|
||||
**v1.0.0:**
|
||||
```rust
|
||||
fn process_event(event: DexEvent) {
|
||||
let metadata = event.metadata();
|
||||
|
||||
println!("协议: {:?}", metadata.protocol);
|
||||
println!("事件类型: {:?}", metadata.event_type);
|
||||
println!("签名: {}", metadata.signature);
|
||||
println!("插槽: {}", metadata.slot);
|
||||
|
||||
// 处理特定事件类型
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => handle_pumpfun_trade(e),
|
||||
DexEvent::RaydiumCpmmSwapEvent(e) => handle_raydium_swap(e),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 模式 3:事件类型分类
|
||||
|
||||
**v1.0.0:**
|
||||
```rust
|
||||
fn categorize_event(event: &DexEvent) -> &'static str {
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(_)
|
||||
| DexEvent::PumpSwapBuyEvent(_)
|
||||
| DexEvent::PumpSwapSellEvent(_) => "交易",
|
||||
|
||||
DexEvent::PumpFunCreateTokenEvent(_)
|
||||
| DexEvent::PumpSwapCreatePoolEvent(_) => "创建",
|
||||
|
||||
DexEvent::RaydiumCpmmDepositEvent(_)
|
||||
| DexEvent::RaydiumCpmmWithdrawEvent(_) => "流动性",
|
||||
|
||||
_ => "其他",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## EventParser API 变更
|
||||
|
||||
### 解析交易
|
||||
|
||||
**之前 (v0.5.x):**
|
||||
```rust
|
||||
let parser = Arc::new(EventParser::new(protocols, event_filter));
|
||||
parser.parse_encoded_confirmed_transaction_with_status_meta(
|
||||
signature,
|
||||
transaction,
|
||||
Arc::new(|event: &Box<dyn UnifiedEvent>| {
|
||||
println!("{:?}", event);
|
||||
}),
|
||||
).await?;
|
||||
```
|
||||
|
||||
**之后 (v1.0.0):**
|
||||
```rust
|
||||
EventParser::parse_encoded_confirmed_transaction_with_status_meta(
|
||||
&protocols,
|
||||
event_filter.as_ref(),
|
||||
signature,
|
||||
transaction,
|
||||
Arc::new(|event: &DexEvent| {
|
||||
println!("{:?}", event);
|
||||
}),
|
||||
).await?;
|
||||
```
|
||||
|
||||
`EventParser` 现在是无状态的,使用静态方法,无需创建实例。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
### 陷阱 1:忘记处理所有变体
|
||||
|
||||
❌ **错误:**
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
// 缺少其他变体!
|
||||
}
|
||||
```
|
||||
|
||||
✅ **正确:**
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
_ => {} // 处理或忽略其他事件
|
||||
}
|
||||
```
|
||||
|
||||
### 陷阱 2:使用旧的元数据访问模式
|
||||
|
||||
❌ **错误:**
|
||||
```rust
|
||||
let sig = event.signature(); // 此方法不再存在
|
||||
```
|
||||
|
||||
✅ **正确:**
|
||||
```rust
|
||||
let sig = event.metadata().signature;
|
||||
```
|
||||
|
||||
### 陷阱 3:尝试使用 `match_event!` 宏
|
||||
|
||||
❌ **错误:**
|
||||
```rust
|
||||
match_event!(event, { /* ... */ }); // 宏不再存在
|
||||
```
|
||||
|
||||
✅ **正确:**
|
||||
```rust
|
||||
match event {
|
||||
DexEvent::PumpFunTradeEvent(e) => { /* ... */ }
|
||||
_ => {}
|
||||
}
|
||||
```
|
||||
|
||||
## 新系统的优势
|
||||
|
||||
1. **类型安全**: 编译器在编译时捕获更多错误
|
||||
2. **性能**: 无动态分发开销
|
||||
3. **简洁性**: 标准 Rust 模式,无自定义宏
|
||||
4. **更好的工具支持**: 完整的 IDE 自动补全支持
|
||||
5. **更易调试**: 更清晰的堆栈跟踪和错误消息
|
||||
6. **序列化**: 所有事件内置 `Serialize`/`Deserialize` 支持
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
- [🚀 Project Features](#-project-features)
|
||||
- [⚡ Installation](#-installation)
|
||||
- [🔄 Migration Guide](#-migration-guide)
|
||||
- [⚙️ Configuration System](#️-configuration-system)
|
||||
- [📚 Usage Examples](#-usage-examples)
|
||||
- [🔧 Supported Protocols](#-supported-protocols)
|
||||
@@ -108,74 +109,69 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.5.0" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.0.0" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-streamer-sdk = "1.0.0"
|
||||
```
|
||||
|
||||
## 🔄 Migration Guide
|
||||
|
||||
### Migrating from v0.5.x to v1.0.0
|
||||
|
||||
Version 1.0.0 introduces a major architectural change from trait-based event handling to enum-based events. This provides better type safety, improved performance, and simpler code patterns.
|
||||
|
||||
**Key Changes:**
|
||||
|
||||
1. **Event Type Changed** - `Box<dyn UnifiedEvent>` → `DexEvent` enum
|
||||
2. **Callback Signature** - Callbacks now receive concrete `DexEvent` instead of trait objects
|
||||
3. **Event Matching** - Use standard Rust `match` instead of `match_event!` macro
|
||||
4. **Metadata Access** - Event properties now accessed through `.metadata()` method
|
||||
|
||||
For detailed migration steps and code examples, see [MIGRATION.md](MIGRATION.md) or [MIGRATION_CN.md](MIGRATION_CN.md) (Chinese version).
|
||||
|
||||
**Quick Migration Example:**
|
||||
|
||||
```rust
|
||||
// Old (v0.5.x)
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!("Event: {:?}", event.event_type());
|
||||
};
|
||||
|
||||
// New (v1.0.0)
|
||||
let callback = |event: DexEvent| {
|
||||
println!("Event: {:?}", event.metadata().event_type);
|
||||
};
|
||||
```
|
||||
|
||||
## ⚙️ Configuration System
|
||||
|
||||
### Preset Configurations
|
||||
|
||||
The library provides three preset configurations optimized for different use cases:
|
||||
|
||||
#### 1. High Throughput Configuration (`high_throughput()`)
|
||||
|
||||
Optimized for high-concurrency scenarios, prioritizing throughput over latency:
|
||||
You can customize client configuration:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig::high_throughput();
|
||||
// Or use convenience methods
|
||||
let grpc = YellowstoneGrpc::new_high_throughput(endpoint, token)?;
|
||||
let shred = ShredStreamGrpc::new_high_throughput(endpoint).await?;
|
||||
use solana_streamer_sdk::streaming::grpc::ClientConfig;
|
||||
|
||||
// Use default configuration
|
||||
let grpc = YellowstoneGrpc::new(endpoint, token)?;
|
||||
|
||||
// Or create custom configuration
|
||||
let mut config = ClientConfig::default();
|
||||
config.enable_metrics = true; // Enable performance monitoring
|
||||
config.connection.connect_timeout = 30; // 30 seconds
|
||||
config.connection.request_timeout = 120; // 120 seconds
|
||||
|
||||
let grpc = YellowstoneGrpc::new_with_config(endpoint, token, config)?;
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- **Backpressure Strategy**: Drop - drops messages during high load to avoid blocking
|
||||
- **Buffer Size**: 5,000 permits to handle burst traffic
|
||||
- **Use Case**: Scenarios where you need to process large volumes of data and can tolerate occasional message drops during peak loads
|
||||
|
||||
#### 2. Low Latency Configuration (`low_latency()`)
|
||||
|
||||
Optimized for real-time scenarios, prioritizing latency over throughput:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig::low_latency();
|
||||
// Or use convenience methods
|
||||
let grpc = YellowstoneGrpc::new_low_latency(endpoint, token)?;
|
||||
let shred = ShredStreamGrpc::new_low_latency(endpoint).await?;
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- **Backpressure Strategy**: Block - ensures no data loss
|
||||
- **Buffer Size**: 4000 permits for balanced throughput and latency
|
||||
- **Immediate Processing**: No buffering, processes events immediately
|
||||
- **Use Case**: Scenarios where every millisecond counts and you cannot afford to lose any events, such as trading applications or real-time monitoring
|
||||
|
||||
|
||||
### Custom Configuration
|
||||
|
||||
You can also create custom configurations:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig {
|
||||
connection: ConnectionConfig {
|
||||
connect_timeout: 30,
|
||||
request_timeout: 120,
|
||||
max_decoding_message_size: 20 * 1024 * 1024, // 20MB
|
||||
},
|
||||
backpressure: BackpressureConfig {
|
||||
permits: 2000,
|
||||
strategy: BackpressureStrategy::Block,
|
||||
},
|
||||
enable_metrics: true,
|
||||
};
|
||||
```
|
||||
**Available Configuration Options:**
|
||||
- `enable_metrics`: Enable/disable performance monitoring (default: false)
|
||||
- `connection.connect_timeout`: Connection timeout in seconds (default: 10)
|
||||
- `connection.request_timeout`: Request timeout in seconds (default: 60)
|
||||
- `connection.max_decoding_message_size`: Maximum message size in bytes (default: 10MB)
|
||||
|
||||
## 📚 Usage Examples
|
||||
|
||||
@@ -296,7 +292,7 @@ Note: Multiple subscription attempts on the same client return an error.
|
||||
|
||||
### Unified Event Interface
|
||||
|
||||
- **DexEvent Trait**: All protocol events implement a common interface
|
||||
- **DexEvent Enum**: Type-safe enum containing all protocol events
|
||||
- **Protocol Enum**: Easy identification of event sources
|
||||
- **Event Factory**: Automatic event parsing and categorization
|
||||
|
||||
|
||||
+51
-55
@@ -45,6 +45,7 @@
|
||||
|
||||
- [🚀 项目特性](#-项目特性)
|
||||
- [⚡ 安装](#-安装)
|
||||
- [🔄 迁移指南](#-迁移指南)
|
||||
- [⚙️ 配置系统](#️-配置系统)
|
||||
- [📚 使用示例](#-使用示例)
|
||||
- [🔧 支持的协议](#-支持的协议)
|
||||
@@ -107,74 +108,69 @@ git clone https://github.com/0xfnzero/solana-streamer
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.5.0" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.0.0" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-streamer-sdk = "1.0.0"
|
||||
```
|
||||
|
||||
## 🔄 迁移指南
|
||||
|
||||
### 从 v0.5.x 迁移到 v1.0.0
|
||||
|
||||
版本 1.0.0 引入了从基于 trait 的事件处理到基于 enum 的事件的重大架构变更。这提供了更好的类型安全性、改进的性能和更简单的代码模式。
|
||||
|
||||
**主要变更:**
|
||||
|
||||
1. **事件类型变更** - `Box<dyn UnifiedEvent>` → `DexEvent` 枚举
|
||||
2. **回调签名** - 回调现在接收具体的 `DexEvent` 而不是 trait 对象
|
||||
3. **事件匹配** - 使用标准 Rust `match` 而不是 `match_event!` 宏
|
||||
4. **元数据访问** - 事件属性现在通过 `.metadata()` 方法访问
|
||||
|
||||
详细的迁移步骤和代码示例,请参阅 [MIGRATION.md](MIGRATION.md) 或 [MIGRATION_CN.md](MIGRATION_CN.md)(中文版本)。
|
||||
|
||||
**快速迁移示例:**
|
||||
|
||||
```rust
|
||||
// 旧版 (v0.5.x)
|
||||
let callback = |event: Box<dyn UnifiedEvent>| {
|
||||
println!("Event: {:?}", event.event_type());
|
||||
};
|
||||
|
||||
// 新版 (v1.0.0)
|
||||
let callback = |event: DexEvent| {
|
||||
println!("Event: {:?}", event.metadata().event_type);
|
||||
};
|
||||
```
|
||||
|
||||
## ⚙️ 配置系统
|
||||
|
||||
### 预设配置
|
||||
|
||||
库提供了三种预设配置,针对不同的使用场景进行了优化:
|
||||
|
||||
#### 1. 高吞吐量配置 (`high_throughput()`)
|
||||
|
||||
专为高并发场景优化,优先考虑吞吐量而非延迟:
|
||||
您可以自定义客户端配置:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig::high_throughput();
|
||||
// 或者使用便捷方法
|
||||
let grpc = YellowstoneGrpc::new_high_throughput(endpoint, token)?;
|
||||
let shred = ShredStreamGrpc::new_high_throughput(endpoint).await?;
|
||||
use solana_streamer_sdk::streaming::grpc::ClientConfig;
|
||||
|
||||
// 使用默认配置
|
||||
let grpc = YellowstoneGrpc::new(endpoint, token)?;
|
||||
|
||||
// 或创建自定义配置
|
||||
let mut config = ClientConfig::default();
|
||||
config.enable_metrics = true; // 启用性能监控
|
||||
config.connection.connect_timeout = 30; // 30 秒
|
||||
config.connection.request_timeout = 120; // 120 秒
|
||||
|
||||
let grpc = YellowstoneGrpc::new_with_config(endpoint, token, config)?;
|
||||
```
|
||||
|
||||
**特性:**
|
||||
- **背压策略**: Drop(丢弃策略)- 在高负载时丢弃消息以避免阻塞
|
||||
- **缓冲区大小**: 5,000 个许可证,处理突发流量
|
||||
- **适用场景**: 需要处理大量数据且可以容忍在峰值负载时偶尔丢失消息的场景
|
||||
|
||||
#### 2. 低延迟配置 (`low_latency()`)
|
||||
|
||||
专为实时场景优化,优先考虑延迟而非吞吐量:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig::low_latency();
|
||||
// 或者使用便捷方法
|
||||
let grpc = YellowstoneGrpc::new_low_latency(endpoint, token)?;
|
||||
let shred = ShredStreamGrpc::new_low_latency(endpoint).await?;
|
||||
```
|
||||
|
||||
**特性:**
|
||||
- **背压策略**: Block(阻塞策略)- 确保不丢失任何数据
|
||||
- **缓冲区大小**: 4000 个许可证,平衡吞吐量和延迟
|
||||
- **立即处理**: 不进行缓冲,立即处理事件
|
||||
- **适用场景**: 每毫秒都很重要且不能丢失任何事件的场景,如交易应用或实时监控
|
||||
|
||||
|
||||
### 自定义配置
|
||||
|
||||
您也可以创建自定义配置:
|
||||
|
||||
```rust
|
||||
let config = StreamClientConfig {
|
||||
connection: ConnectionConfig {
|
||||
connect_timeout: 30,
|
||||
request_timeout: 120,
|
||||
max_decoding_message_size: 20 * 1024 * 1024, // 20MB
|
||||
},
|
||||
backpressure: BackpressureConfig {
|
||||
permits: 2000,
|
||||
strategy: BackpressureStrategy::Block,
|
||||
},
|
||||
enable_metrics: true,
|
||||
};
|
||||
```
|
||||
**可用配置选项:**
|
||||
- `enable_metrics`: 启用/禁用性能监控(默认:false)
|
||||
- `connection.connect_timeout`: 连接超时(秒)(默认:10)
|
||||
- `connection.request_timeout`: 请求超时(秒)(默认:60)
|
||||
- `connection.max_decoding_message_size`: 最大消息大小(字节)(默认:10MB)
|
||||
|
||||
## 📚 使用示例
|
||||
|
||||
@@ -295,7 +291,7 @@ grpc.update_subscription(
|
||||
|
||||
### 统一事件接口
|
||||
|
||||
- **DexEvent Trait**: 所有协议事件实现通用接口
|
||||
- **DexEvent 枚举**: 包含所有协议事件的类型安全枚举
|
||||
- **Protocol Enum**: 轻松识别事件来源
|
||||
- **Event Factory**: 自动事件解析和分类
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to Yellowstone gRPC events...");
|
||||
// Create low-latency configuration
|
||||
let mut config: ClientConfig = ClientConfig::low_latency();
|
||||
let mut config: ClientConfig = ClientConfig::default();
|
||||
// Enable performance monitoring, has performance overhead, disabled by default
|
||||
config.enable_metrics = true;
|
||||
let grpc = YellowstoneGrpc::new_with_config(
|
||||
|
||||
@@ -15,7 +15,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to ShredStream events...");
|
||||
|
||||
// Create low-latency configuration
|
||||
let mut config = StreamClientConfig::low_latency();
|
||||
let mut config = StreamClientConfig::default();
|
||||
// Enable performance monitoring, has performance overhead, disabled by default
|
||||
config.enable_metrics = true;
|
||||
let shred_stream =
|
||||
|
||||
@@ -18,7 +18,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to Yellowstone gRPC events...");
|
||||
// Create low-latency configuration
|
||||
let mut config: ClientConfig = ClientConfig::low_latency();
|
||||
let mut config: ClientConfig = ClientConfig::default();
|
||||
// Enable performance monitoring, has performance overhead, disabled by default
|
||||
config.enable_metrics = true;
|
||||
let grpc = YellowstoneGrpc::new_with_config(
|
||||
|
||||
@@ -18,7 +18,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to Yellowstone gRPC events...");
|
||||
// Create low-latency configuration
|
||||
let mut config: ClientConfig = ClientConfig::low_latency();
|
||||
let mut config: ClientConfig = ClientConfig::default();
|
||||
// Enable performance monitoring, has performance overhead, disabled by default
|
||||
config.enable_metrics = true;
|
||||
let grpc = YellowstoneGrpc::new_with_config(
|
||||
|
||||
@@ -35,12 +35,3 @@ impl Default for StreamClientConfig {
|
||||
Self { connection: ConnectionConfig::default(), enable_metrics: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamClientConfig {
|
||||
pub fn low_latency() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn high_throughput() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user