mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-07 14:17:44 +00:00
feat: Add Raydium AMM V4 support and enhance protocol parsers
- Add complete Raydium AMM V4 protocol integration - Enhance PumpFun, CLMM, CPMM event parsing - Refactor parser architecture for better extensibility - Update documentation and examples
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-streamer-sdk"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
|
||||
repository = "https://github.com/0xfnzero/solana-streamer"
|
||||
|
||||
@@ -14,6 +14,7 @@ A lightweight Rust library for real-time event streaming from Solana DEX trading
|
||||
- **Bonk**: Token launch platform events (letsbonk.fun)
|
||||
- **Raydium CPMM**: Raydium's Concentrated Pool Market Maker events
|
||||
- **Raydium CLMM**: Raydium's Concentrated Liquidity Market Maker events
|
||||
- **Raydium AMM V4**: Raydium's Automated Market Maker V4 events
|
||||
5. **Unified Event Interface**: Consistent event handling across all supported protocols
|
||||
6. **Event Parsing System**: Automatic parsing and categorization of protocol-specific events
|
||||
7. **High Performance**: Optimized for low-latency event processing
|
||||
@@ -41,14 +42,14 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.2.0" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.2.1" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = "0.2.0"
|
||||
solana-streamer-sdk = "0.2.1"
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
@@ -63,7 +64,7 @@ cargo run --example parse_tx_events
|
||||
|
||||
This example demonstrates:
|
||||
- How to parse transaction data from Solana mainnet using RPC
|
||||
- Event parsing for multiple protocols (PumpFun, PumpSwap, Bonk, Raydium CPMM/CLMM)
|
||||
- Event parsing for multiple protocols (PumpFun, PumpSwap, Bonk, Raydium CPMM/CLMM/AMM V4)
|
||||
- Transaction details extraction including fees, logs, and compute units
|
||||
|
||||
The example uses a predefined transaction signature and shows how to extract protocol-specific events from the transaction data.
|
||||
@@ -80,18 +81,36 @@ use solana_streamer_sdk::{
|
||||
parser::BONK_PROGRAM_ID, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent,
|
||||
BonkPoolCreateEvent, BonkTradeEvent,
|
||||
},
|
||||
pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent},
|
||||
pumpfun::{
|
||||
parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunMigrateEvent,
|
||||
PumpFunTradeEvent,
|
||||
},
|
||||
pumpswap::{
|
||||
parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
|
||||
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
|
||||
},
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event,
|
||||
raydium_amm_v4::{
|
||||
RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent,
|
||||
RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent,
|
||||
},
|
||||
raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent}, BlockMetaEvent,
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmClosePositionEvent,
|
||||
RaydiumClmmCreatePoolEvent, RaydiumClmmDecreaseLiquidityV2Event,
|
||||
RaydiumClmmIncreaseLiquidityV2Event, RaydiumClmmOpenPositionV2Event,
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent, RaydiumClmmSwapEvent,
|
||||
RaydiumClmmSwapV2Event,
|
||||
},
|
||||
raydium_cpmm::{
|
||||
parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmDepositEvent,
|
||||
RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent, RaydiumCpmmWithdrawEvent,
|
||||
},
|
||||
BlockMetaEvent,
|
||||
},
|
||||
Protocol, UnifiedEvent,
|
||||
}, grpc::ClientConfig, shred_stream::ShredClientConfig, ShredStreamGrpc, YellowstoneGrpc
|
||||
},
|
||||
grpc::ClientConfig,
|
||||
shred_stream::ShredClientConfig,
|
||||
ShredStreamGrpc, YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -138,12 +157,11 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Filter accounts
|
||||
let account_include = vec![
|
||||
PUMPFUN_PROGRAM_ID.to_string(), // Monitor pumpfun program ID
|
||||
PUMPSWAP_PROGRAM_ID.to_string(), // Monitor pumpswap program ID
|
||||
BONK_PROGRAM_ID.to_string(), // Monitor bonk program ID
|
||||
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Monitor raydium_cpmm program ID
|
||||
RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Monitor raydium_clmm program ID
|
||||
"xxxxxxxx".to_string(), // Monitor xxxxx account
|
||||
PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID
|
||||
PUMPSWAP_PROGRAM_ID.to_string(), // Listen to pumpswap program ID
|
||||
BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID
|
||||
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID
|
||||
RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Listen to raydium_clmm program ID
|
||||
];
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
@@ -184,6 +202,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Protocol::Bonk,
|
||||
Protocol::RaydiumCpmm,
|
||||
Protocol::RaydiumClmm,
|
||||
Protocol::RaydiumAmmV4,
|
||||
];
|
||||
|
||||
println!("Listening for events, press Ctrl+C to stop...");
|
||||
@@ -196,9 +215,11 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
println!("🎉 Event received! Type: {:?}, ID: {}", event.event_type(), event.id());
|
||||
match_event!(event, {
|
||||
// block meta
|
||||
BlockMetaEvent => |e: BlockMetaEvent| {
|
||||
println!("BlockMetaEvent: {e:?}");
|
||||
},
|
||||
// bonk
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
// When using grpc, you can get block_time from each event
|
||||
println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms);
|
||||
@@ -213,12 +234,17 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| {
|
||||
println!("BonkMigrateToCpswapEvent: {e:?}");
|
||||
},
|
||||
// pumpfun
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {e:?}");
|
||||
},
|
||||
PumpFunMigrateEvent => |e: PumpFunMigrateEvent| {
|
||||
println!("PumpFunMigrateEvent: {e:?}");
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {e:?}");
|
||||
},
|
||||
// pumpswap
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {e:?}");
|
||||
},
|
||||
@@ -234,15 +260,60 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {e:?}");
|
||||
},
|
||||
// raydium_cpmm
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmDepositEvent => |e: RaydiumCpmmDepositEvent| {
|
||||
println!("RaydiumCpmmDepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmInitializeEvent => |e: RaydiumCpmmInitializeEvent| {
|
||||
println!("RaydiumCpmmInitializeEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| {
|
||||
println!("RaydiumCpmmWithdrawEvent: {e:?}");
|
||||
},
|
||||
// raydium_clmm
|
||||
RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| {
|
||||
println!("RaydiumClmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| {
|
||||
println!("RaydiumClmmSwapV2Event: {e:?}");
|
||||
}
|
||||
},
|
||||
RaydiumClmmClosePositionEvent => |e: RaydiumClmmClosePositionEvent| {
|
||||
println!("RaydiumClmmClosePositionEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmDecreaseLiquidityV2Event => |e: RaydiumClmmDecreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmDecreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmCreatePoolEvent => |e: RaydiumClmmCreatePoolEvent| {
|
||||
println!("RaydiumClmmCreatePoolEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmIncreaseLiquidityV2Event => |e: RaydiumClmmIncreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmIncreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent => |e: RaydiumClmmOpenPositionWithToken22NftEvent| {
|
||||
println!("RaydiumClmmOpenPositionWithToken22NftEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| {
|
||||
println!("RaydiumClmmOpenPositionV2Event: {e:?}");
|
||||
},
|
||||
// raydium_amm_v4
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
println!("RaydiumAmmV4SwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4DepositEvent => |e: RaydiumAmmV4DepositEvent| {
|
||||
println!("RaydiumAmmV4DepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4Initialize2Event => |e: RaydiumAmmV4Initialize2Event| {
|
||||
println!("RaydiumAmmV4Initialize2Event: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawEvent => |e: RaydiumAmmV4WithdrawEvent| {
|
||||
println!("RaydiumAmmV4WithdrawEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawPnlEvent => |e: RaydiumAmmV4WithdrawPnlEvent| {
|
||||
println!("RaydiumAmmV4WithdrawPnlEvent: {e:?}");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -255,6 +326,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
- **Bonk**: Token launch platform (letsbonk.fun)
|
||||
- **Raydium CPMM**: Raydium's Concentrated Pool Market Maker protocol
|
||||
- **Raydium CLMM**: Raydium's Concentrated Liquidity Market Maker protocol
|
||||
- **Raydium AMM V4**: Raydium's Automated Market Maker V4 protocol
|
||||
|
||||
## Event Streaming Services
|
||||
|
||||
@@ -295,6 +367,7 @@ src/
|
||||
│ │ │ ├── bonk/ # Bonk event parsing
|
||||
│ │ │ ├── pumpfun/ # PumpFun event parsing
|
||||
│ │ │ ├── pumpswap/ # PumpSwap event parsing
|
||||
│ │ │ ├── raydium_amm_v4/ # Raydium AMM V4 event parsing
|
||||
│ │ │ ├── raydium_cpmm/ # Raydium CPMM event parsing
|
||||
│ │ │ └── raydium_clmm/ # Raydium CLMM event parsing
|
||||
│ │ └── factory.rs # Parser factory
|
||||
|
||||
+83
-10
@@ -14,6 +14,7 @@
|
||||
- **Bonk**: 代币发布平台事件 (letsbonk.fun)
|
||||
- **Raydium CPMM**: Raydium 集中池做市商事件
|
||||
- **Raydium CLMM**: Raydium 集中流动性做市商事件
|
||||
- **Raydium AMM V4**: Raydium 自动做市商 V4 事件
|
||||
5. **统一事件接口**: 在所有支持的协议中保持一致的事件处理
|
||||
6. **事件解析系统**: 自动解析和分类协议特定事件
|
||||
7. **高性能**: 针对低延迟事件处理进行优化
|
||||
@@ -41,14 +42,14 @@ git clone https://github.com/0xfnzero/solana-streamer
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.2.0" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.2.1" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = "0.2.0"
|
||||
solana-streamer-sdk = "0.2.1"
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
@@ -63,7 +64,7 @@ cargo run --example parse_tx_events
|
||||
|
||||
该示例演示了:
|
||||
- 如何使用 RPC 从 Solana 主网解析交易数据
|
||||
- 多协议事件解析(PumpFun、PumpSwap、Bonk、Raydium CPMM/CLMM)
|
||||
- 多协议事件解析(PumpFun、PumpSwap、Bonk、Raydium CPMM/CLMM/AMM V4)
|
||||
- 交易详情提取,包括费用、日志和计算单元
|
||||
|
||||
该示例使用预定义的交易签名,展示如何从交易数据中提取协议特定的事件。
|
||||
@@ -80,18 +81,36 @@ use solana_streamer_sdk::{
|
||||
parser::BONK_PROGRAM_ID, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent,
|
||||
BonkPoolCreateEvent, BonkTradeEvent,
|
||||
},
|
||||
pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent},
|
||||
pumpfun::{
|
||||
parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunMigrateEvent,
|
||||
PumpFunTradeEvent,
|
||||
},
|
||||
pumpswap::{
|
||||
parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
|
||||
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
|
||||
},
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event,
|
||||
raydium_amm_v4::{
|
||||
RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent,
|
||||
RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent,
|
||||
},
|
||||
raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent}, BlockMetaEvent,
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmClosePositionEvent,
|
||||
RaydiumClmmCreatePoolEvent, RaydiumClmmDecreaseLiquidityV2Event,
|
||||
RaydiumClmmIncreaseLiquidityV2Event, RaydiumClmmOpenPositionV2Event,
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent, RaydiumClmmSwapEvent,
|
||||
RaydiumClmmSwapV2Event,
|
||||
},
|
||||
raydium_cpmm::{
|
||||
parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmDepositEvent,
|
||||
RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent, RaydiumCpmmWithdrawEvent,
|
||||
},
|
||||
BlockMetaEvent,
|
||||
},
|
||||
Protocol, UnifiedEvent,
|
||||
}, grpc::ClientConfig, shred_stream::ShredClientConfig, ShredStreamGrpc, YellowstoneGrpc
|
||||
},
|
||||
grpc::ClientConfig,
|
||||
shred_stream::ShredClientConfig,
|
||||
ShredStreamGrpc, YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -138,7 +157,6 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
BONK_PROGRAM_ID.to_string(), // 监听 bonk 程序ID
|
||||
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // 监听 raydium_cpmm 程序ID
|
||||
RAYDIUM_CLMM_PROGRAM_ID.to_string(), // 监听 raydium_clmm 程序ID
|
||||
"xxxxxxxx".to_string(), // 监听 xxxxx 账号
|
||||
];
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
@@ -179,6 +197,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Protocol::Bonk,
|
||||
Protocol::RaydiumCpmm,
|
||||
Protocol::RaydiumClmm,
|
||||
Protocol::RaydiumAmmV4,
|
||||
];
|
||||
|
||||
println!("Listening for events, press Ctrl+C to stop...");
|
||||
@@ -191,9 +210,11 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
println!("🎉 Event received! Type: {:?}, ID: {}", event.event_type(), event.id());
|
||||
match_event!(event, {
|
||||
// block meta
|
||||
BlockMetaEvent => |e: BlockMetaEvent| {
|
||||
println!("BlockMetaEvent: {e:?}");
|
||||
},
|
||||
// bonk
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
// 使用grpc的时候,可以从每个事件中获取到block_time
|
||||
println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms);
|
||||
@@ -208,12 +229,17 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| {
|
||||
println!("BonkMigrateToCpswapEvent: {e:?}");
|
||||
},
|
||||
// pumpfun
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {e:?}");
|
||||
},
|
||||
PumpFunMigrateEvent => |e: PumpFunMigrateEvent| {
|
||||
println!("PumpFunMigrateEvent: {e:?}");
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {e:?}");
|
||||
},
|
||||
// pumpswap
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {e:?}");
|
||||
},
|
||||
@@ -229,15 +255,60 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {e:?}");
|
||||
},
|
||||
// raydium_cpmm
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmDepositEvent => |e: RaydiumCpmmDepositEvent| {
|
||||
println!("RaydiumCpmmDepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmInitializeEvent => |e: RaydiumCpmmInitializeEvent| {
|
||||
println!("RaydiumCpmmInitializeEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| {
|
||||
println!("RaydiumCpmmWithdrawEvent: {e:?}");
|
||||
},
|
||||
// raydium_clmm
|
||||
RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| {
|
||||
println!("RaydiumClmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| {
|
||||
println!("RaydiumClmmSwapV2Event: {e:?}");
|
||||
}
|
||||
},
|
||||
RaydiumClmmClosePositionEvent => |e: RaydiumClmmClosePositionEvent| {
|
||||
println!("RaydiumClmmClosePositionEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmDecreaseLiquidityV2Event => |e: RaydiumClmmDecreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmDecreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmCreatePoolEvent => |e: RaydiumClmmCreatePoolEvent| {
|
||||
println!("RaydiumClmmCreatePoolEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmIncreaseLiquidityV2Event => |e: RaydiumClmmIncreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmIncreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent => |e: RaydiumClmmOpenPositionWithToken22NftEvent| {
|
||||
println!("RaydiumClmmOpenPositionWithToken22NftEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| {
|
||||
println!("RaydiumClmmOpenPositionV2Event: {e:?}");
|
||||
},
|
||||
// raydium_amm_v4
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
println!("RaydiumAmmV4SwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4DepositEvent => |e: RaydiumAmmV4DepositEvent| {
|
||||
println!("RaydiumAmmV4DepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4Initialize2Event => |e: RaydiumAmmV4Initialize2Event| {
|
||||
println!("RaydiumAmmV4Initialize2Event: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawEvent => |e: RaydiumAmmV4WithdrawEvent| {
|
||||
println!("RaydiumAmmV4WithdrawEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawPnlEvent => |e: RaydiumAmmV4WithdrawPnlEvent| {
|
||||
println!("RaydiumAmmV4WithdrawPnlEvent: {e:?}");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -250,6 +321,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
- **Bonk**: 代币发布平台 (letsbonk.fun)
|
||||
- **Raydium CPMM**: Raydium 集中池做市商协议
|
||||
- **Raydium CLMM**: Raydium 集中流动性做市商协议
|
||||
- **Raydium AMM V4**: Raydium 自动做市商 V4 协议
|
||||
|
||||
## 事件流服务
|
||||
|
||||
@@ -290,6 +362,7 @@ src/
|
||||
│ │ │ ├── bonk/ # Bonk 事件解析
|
||||
│ │ │ ├── pumpfun/ # PumpFun 事件解析
|
||||
│ │ │ ├── pumpswap/ # PumpSwap 事件解析
|
||||
│ │ │ ├── raydium_amm_v4/ # Raydium AMM V4 事件解析
|
||||
│ │ │ ├── raydium_cpmm/ # Raydium CPMM 事件解析
|
||||
│ │ │ └── raydium_clmm/ # Raydium CLMM 事件解析
|
||||
│ │ └── factory.rs # 解析器工厂
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::sync::Arc;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let signatures = vec![
|
||||
"tb6KxvMAZtctQw2yszKk6QGVuzbv1CAbS5mKK7JjVaeb2i5rCafNHfnqE1PhaWukx79sDYcsoZqbHXRJgPu93w4",
|
||||
"42agNk1heHabNAVRzEKqQEt5adGkQzRYf9M1Q81uBJPCCHyP4cyCA1RNkgxXrtEAWeeGcytyh2TsnkBDgqnHeq4z",
|
||||
];
|
||||
// Validate signature format
|
||||
let mut valid_signatures = Vec::new();
|
||||
@@ -93,6 +93,7 @@ async fn get_single_transaction_details(signature_str: &str) -> Result<()> {
|
||||
Protocol::PumpSwap,
|
||||
Protocol::PumpFun,
|
||||
Protocol::RaydiumCpmm,
|
||||
Protocol::RaydiumAmmV4,
|
||||
];
|
||||
let parser: Arc<dyn EventParser> = Arc::new(MutilEventParser::new(protocols));
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
+77
-6
@@ -7,18 +7,36 @@ use solana_streamer_sdk::{
|
||||
parser::BONK_PROGRAM_ID, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent,
|
||||
BonkPoolCreateEvent, BonkTradeEvent,
|
||||
},
|
||||
pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent},
|
||||
pumpfun::{
|
||||
parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunMigrateEvent,
|
||||
PumpFunTradeEvent,
|
||||
},
|
||||
pumpswap::{
|
||||
parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
|
||||
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
|
||||
},
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event,
|
||||
raydium_amm_v4::{
|
||||
RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent,
|
||||
RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent,
|
||||
},
|
||||
raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent}, BlockMetaEvent,
|
||||
raydium_clmm::{
|
||||
parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmClosePositionEvent,
|
||||
RaydiumClmmCreatePoolEvent, RaydiumClmmDecreaseLiquidityV2Event,
|
||||
RaydiumClmmIncreaseLiquidityV2Event, RaydiumClmmOpenPositionV2Event,
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent, RaydiumClmmSwapEvent,
|
||||
RaydiumClmmSwapV2Event,
|
||||
},
|
||||
raydium_cpmm::{
|
||||
parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmDepositEvent,
|
||||
RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent, RaydiumCpmmWithdrawEvent,
|
||||
},
|
||||
BlockMetaEvent,
|
||||
},
|
||||
Protocol, UnifiedEvent,
|
||||
}, grpc::ClientConfig, shred_stream::ShredClientConfig, ShredStreamGrpc, YellowstoneGrpc
|
||||
},
|
||||
grpc::ClientConfig,
|
||||
shred_stream::ShredClientConfig,
|
||||
ShredStreamGrpc, YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -105,6 +123,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Protocol::Bonk,
|
||||
Protocol::RaydiumCpmm,
|
||||
Protocol::RaydiumClmm,
|
||||
Protocol::RaydiumAmmV4,
|
||||
];
|
||||
|
||||
println!("Listening for events, press Ctrl+C to stop...");
|
||||
@@ -117,9 +136,11 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
println!("🎉 Event received! Type: {:?}, ID: {}", event.event_type(), event.id());
|
||||
match_event!(event, {
|
||||
// block meta
|
||||
BlockMetaEvent => |e: BlockMetaEvent| {
|
||||
println!("BlockMetaEvent: {e:?}");
|
||||
},
|
||||
// bonk
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
// When using grpc, you can get block_time from each event
|
||||
println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms);
|
||||
@@ -134,12 +155,17 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| {
|
||||
println!("BonkMigrateToCpswapEvent: {e:?}");
|
||||
},
|
||||
// pumpfun
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {e:?}");
|
||||
},
|
||||
PumpFunMigrateEvent => |e: PumpFunMigrateEvent| {
|
||||
println!("PumpFunMigrateEvent: {e:?}");
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {e:?}");
|
||||
},
|
||||
// pumpswap
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {e:?}");
|
||||
},
|
||||
@@ -155,15 +181,60 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {e:?}");
|
||||
},
|
||||
// raydium_cpmm
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmDepositEvent => |e: RaydiumCpmmDepositEvent| {
|
||||
println!("RaydiumCpmmDepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmInitializeEvent => |e: RaydiumCpmmInitializeEvent| {
|
||||
println!("RaydiumCpmmInitializeEvent: {e:?}");
|
||||
},
|
||||
RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| {
|
||||
println!("RaydiumCpmmWithdrawEvent: {e:?}");
|
||||
},
|
||||
// raydium_clmm
|
||||
RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| {
|
||||
println!("RaydiumClmmSwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| {
|
||||
println!("RaydiumClmmSwapV2Event: {e:?}");
|
||||
}
|
||||
},
|
||||
RaydiumClmmClosePositionEvent => |e: RaydiumClmmClosePositionEvent| {
|
||||
println!("RaydiumClmmClosePositionEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmDecreaseLiquidityV2Event => |e: RaydiumClmmDecreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmDecreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmCreatePoolEvent => |e: RaydiumClmmCreatePoolEvent| {
|
||||
println!("RaydiumClmmCreatePoolEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmIncreaseLiquidityV2Event => |e: RaydiumClmmIncreaseLiquidityV2Event| {
|
||||
println!("RaydiumClmmIncreaseLiquidityV2Event: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionWithToken22NftEvent => |e: RaydiumClmmOpenPositionWithToken22NftEvent| {
|
||||
println!("RaydiumClmmOpenPositionWithToken22NftEvent: {e:?}");
|
||||
},
|
||||
RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| {
|
||||
println!("RaydiumClmmOpenPositionV2Event: {e:?}");
|
||||
},
|
||||
// raydium_amm_v4
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
println!("RaydiumAmmV4SwapEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4DepositEvent => |e: RaydiumAmmV4DepositEvent| {
|
||||
println!("RaydiumAmmV4DepositEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4Initialize2Event => |e: RaydiumAmmV4Initialize2Event| {
|
||||
println!("RaydiumAmmV4Initialize2Event: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawEvent => |e: RaydiumAmmV4WithdrawEvent| {
|
||||
println!("RaydiumAmmV4WithdrawEvent: {e:?}");
|
||||
},
|
||||
RaydiumAmmV4WithdrawPnlEvent => |e: RaydiumAmmV4WithdrawPnlEvent| {
|
||||
println!("RaydiumAmmV4WithdrawPnlEvent: {e:?}");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::{
|
||||
bonk::BonkTradeEvent,
|
||||
pumpfun::PumpFunTradeEvent,
|
||||
pumpswap::{PumpSwapBuyEvent, PumpSwapSellEvent},
|
||||
raydium_amm_v4::RaydiumAmmV4SwapEvent,
|
||||
raydium_clmm::{RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event},
|
||||
raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
},
|
||||
@@ -101,6 +102,7 @@ pub enum ProtocolType {
|
||||
Bonk,
|
||||
RaydiumCpmm,
|
||||
RaydiumClmm,
|
||||
RaydiumAmmV4,
|
||||
Common,
|
||||
}
|
||||
|
||||
@@ -121,6 +123,7 @@ pub enum EventType {
|
||||
PumpFunCreateToken,
|
||||
PumpFunBuy,
|
||||
PumpFunSell,
|
||||
PumpFunMigrate,
|
||||
|
||||
// Bonk events
|
||||
BonkBuyExactIn,
|
||||
@@ -134,10 +137,27 @@ pub enum EventType {
|
||||
// Raydium CPMM events
|
||||
RaydiumCpmmSwapBaseInput,
|
||||
RaydiumCpmmSwapBaseOutput,
|
||||
RaydiumCpmmDeposit,
|
||||
RaydiumCpmmInitialize,
|
||||
RaydiumCpmmWithdraw,
|
||||
|
||||
// Raydium CLMM events
|
||||
RaydiumClmmSwap,
|
||||
RaydiumClmmSwapV2,
|
||||
RaydiumClmmClosePosition,
|
||||
RaydiumClmmIncreaseLiquidityV2,
|
||||
RaydiumClmmDecreaseLiquidityV2,
|
||||
RaydiumClmmCreatePool,
|
||||
RaydiumClmmOpenPositionWithToken22Nft,
|
||||
RaydiumClmmOpenPositionV2,
|
||||
|
||||
// Raydium AMM V4 events
|
||||
RaydiumAmmV4SwapBaseIn,
|
||||
RaydiumAmmV4SwapBaseOut,
|
||||
RaydiumAmmV4Deposit,
|
||||
RaydiumAmmV4Initialize2,
|
||||
RaydiumAmmV4Withdraw,
|
||||
RaydiumAmmV4WithdrawPnl,
|
||||
|
||||
// Common events
|
||||
BlockMeta,
|
||||
@@ -156,6 +176,7 @@ impl EventType {
|
||||
EventType::PumpFunCreateToken => "PumpFunCreateToken".to_string(),
|
||||
EventType::PumpFunBuy => "PumpFunBuy".to_string(),
|
||||
EventType::PumpFunSell => "PumpFunSell".to_string(),
|
||||
EventType::PumpFunMigrate => "PumpFunMigrate".to_string(),
|
||||
EventType::BonkBuyExactIn => "BonkBuyExactIn".to_string(),
|
||||
EventType::BonkBuyExactOut => "BonkBuyExactOut".to_string(),
|
||||
EventType::BonkSellExactIn => "BonkSellExactIn".to_string(),
|
||||
@@ -165,8 +186,29 @@ impl EventType {
|
||||
EventType::BonkMigrateToCpswap => "BonkMigrateToCpswap".to_string(),
|
||||
EventType::RaydiumCpmmSwapBaseInput => "RaydiumCpmmSwapBaseInput".to_string(),
|
||||
EventType::RaydiumCpmmSwapBaseOutput => "RaydiumCpmmSwapBaseOutput".to_string(),
|
||||
EventType::RaydiumCpmmDeposit => "RaydiumCpmmDeposit".to_string(),
|
||||
EventType::RaydiumCpmmInitialize => "RaydiumCpmmInitialize".to_string(),
|
||||
EventType::RaydiumCpmmWithdraw => "RaydiumCpmmWithdraw".to_string(),
|
||||
EventType::RaydiumClmmSwap => "RaydiumClmmSwap".to_string(),
|
||||
EventType::RaydiumClmmSwapV2 => "RaydiumClmmSwapV2".to_string(),
|
||||
EventType::RaydiumClmmClosePosition => "RaydiumClmmClosePosition".to_string(),
|
||||
EventType::RaydiumClmmDecreaseLiquidityV2 => {
|
||||
"RaydiumClmmDecreaseLiquidityV2".to_string()
|
||||
}
|
||||
EventType::RaydiumClmmCreatePool => "RaydiumClmmCreatePool".to_string(),
|
||||
EventType::RaydiumClmmIncreaseLiquidityV2 => {
|
||||
"RaydiumClmmIncreaseLiquidityV2".to_string()
|
||||
}
|
||||
EventType::RaydiumClmmOpenPositionWithToken22Nft => {
|
||||
"RaydiumClmmOpenPositionWithToken22Nft".to_string()
|
||||
}
|
||||
EventType::RaydiumClmmOpenPositionV2 => "RaydiumClmmOpenPositionV2".to_string(),
|
||||
EventType::RaydiumAmmV4SwapBaseIn => "RaydiumAmmV4SwapBaseIn".to_string(),
|
||||
EventType::RaydiumAmmV4SwapBaseOut => "RaydiumAmmV4SwapBaseOut".to_string(),
|
||||
EventType::RaydiumAmmV4Deposit => "RaydiumAmmV4Deposit".to_string(),
|
||||
EventType::RaydiumAmmV4Initialize2 => "RaydiumAmmV4Initialize2".to_string(),
|
||||
EventType::RaydiumAmmV4Withdraw => "RaydiumAmmV4Withdraw".to_string(),
|
||||
EventType::RaydiumAmmV4WithdrawPnl => "RaydiumAmmV4WithdrawPnl".to_string(),
|
||||
EventType::BlockMeta => "BlockMeta".to_string(),
|
||||
EventType::Unknown => "Unknown".to_string(),
|
||||
}
|
||||
@@ -321,37 +363,25 @@ pub fn parse_transfer_datas_from_next_instructions(
|
||||
inner_instruction: &solana_transaction_status::UiInnerInstructions,
|
||||
current_index: i8,
|
||||
accounts: &[Pubkey],
|
||||
event_type: EventType,
|
||||
) -> (Vec<TransferData>, Option<SwapData>) {
|
||||
let take = match event_type {
|
||||
EventType::PumpFunBuy => 4,
|
||||
EventType::PumpFunSell => 1,
|
||||
EventType::PumpSwapBuy => 3,
|
||||
EventType::PumpSwapSell => 3,
|
||||
EventType::BonkBuyExactIn
|
||||
| EventType::BonkBuyExactOut
|
||||
| EventType::BonkSellExactIn
|
||||
| EventType::BonkSellExactOut => 3,
|
||||
EventType::RaydiumCpmmSwapBaseInput
|
||||
| EventType::RaydiumCpmmSwapBaseOutput
|
||||
| EventType::RaydiumClmmSwap
|
||||
| EventType::RaydiumClmmSwapV2 => 2,
|
||||
_ => 0,
|
||||
};
|
||||
if take == 0 {
|
||||
return (vec![], None);
|
||||
}
|
||||
let mut transfer_datas = vec![];
|
||||
// Get the next two instructions after the current instruction
|
||||
let next_instructions: Vec<&UiInstruction> = inner_instruction
|
||||
.instructions
|
||||
.iter()
|
||||
.skip((current_index + 1) as usize)
|
||||
.take(take)
|
||||
.collect();
|
||||
let next_instructions: Vec<&UiInstruction> =
|
||||
inner_instruction.instructions.iter().skip((current_index + 1) as usize).collect();
|
||||
|
||||
let system_programs = vec![
|
||||
// Token Program
|
||||
Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||
// Token 2022 Program
|
||||
Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
||||
// System Program
|
||||
Pubkey::from_str("11111111111111111111111111111111").unwrap(),
|
||||
];
|
||||
for instruction in next_instructions {
|
||||
if let UiInstruction::Compiled(compiled) = instruction {
|
||||
if !system_programs.contains(&accounts[compiled.program_id_index as usize]) {
|
||||
break;
|
||||
}
|
||||
if let Ok(data) = bs58::decode(compiled.data.clone()).into_vec() {
|
||||
// Token Program: transferChecked
|
||||
// Token 2022 Program: transferChecked
|
||||
@@ -495,7 +525,15 @@ pub fn parse_transfer_datas_from_next_instructions(
|
||||
user_to_token = Some(e.output_token_account);
|
||||
from_vault = Some(e.input_vault);
|
||||
to_vault = Some(e.output_vault);
|
||||
}
|
||||
},
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
user = Some(e.user_source_owner);
|
||||
swap_data.description = Some("Unable to get from_mint and to_mint from RaydiumAmmV4SwapEvent".to_string());
|
||||
user_from_token = Some(e.user_source_token_account);
|
||||
user_to_token = Some(e.user_destination_token_account);
|
||||
from_vault = Some(e.pool_pc_token_account);
|
||||
to_vault = Some(e.pool_coin_token_account);
|
||||
},
|
||||
});
|
||||
|
||||
for transfer_data in transfer_datas.clone() {
|
||||
|
||||
@@ -4,10 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// 获取当前时间戳
|
||||
pub fn current_timestamp() -> i64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
.as_secs() as i64
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64
|
||||
}
|
||||
|
||||
/// 从base64字符串解码数据
|
||||
@@ -57,6 +54,14 @@ pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
|
||||
Some(u64::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn read_i32_le(data: &[u8], offset: usize) -> Option<i32> {
|
||||
if data.len() < offset + 4 {
|
||||
return None;
|
||||
}
|
||||
let bytes: [u8; 4] = data[offset..offset + 4].try_into().ok()?;
|
||||
Some(i32::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn read_u128_le(data: &[u8], offset: usize) -> Option<u128> {
|
||||
if data.len() < offset + 16 {
|
||||
return None;
|
||||
@@ -73,6 +78,20 @@ pub fn read_u8_le(data: &[u8], offset: usize) -> Option<u8> {
|
||||
Some(u8::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn read_option_bool(data: &[u8], offset: &mut usize) -> Option<Option<bool>> {
|
||||
let has_value = data.get(*offset)?.clone();
|
||||
*offset += 1;
|
||||
|
||||
if has_value == 0 {
|
||||
return Some(None);
|
||||
}
|
||||
|
||||
let value = data.get(*offset)?.clone();
|
||||
*offset += 1;
|
||||
|
||||
Some(Some(value != 0))
|
||||
}
|
||||
|
||||
/// 安全地从字节数组中读取u32
|
||||
pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
|
||||
if data.len() < offset + 4 {
|
||||
|
||||
@@ -158,7 +158,6 @@ pub trait EventParser: Send + Sync {
|
||||
inn,
|
||||
-1_i8,
|
||||
&accounts,
|
||||
event.event_type(),
|
||||
);
|
||||
event.set_transfer_datas(transfer_datas, swap_data);
|
||||
});
|
||||
@@ -309,7 +308,6 @@ pub trait EventParser: Send + Sync {
|
||||
&inner_instruction,
|
||||
index as i8,
|
||||
&accounts,
|
||||
event.event_type(),
|
||||
);
|
||||
event.set_transfer_datas(transfer_datas, swap_data);
|
||||
});
|
||||
@@ -335,7 +333,6 @@ pub trait EventParser: Send + Sync {
|
||||
&inner_instruction,
|
||||
index as i8,
|
||||
&accounts,
|
||||
event.event_type(),
|
||||
);
|
||||
event.set_transfer_datas(transfer_datas, swap_data);
|
||||
});
|
||||
|
||||
@@ -3,10 +3,7 @@ use solana_sdk::pubkey::Pubkey;
|
||||
use std::{collections::HashMap, sync::{Arc, LazyLock}};
|
||||
|
||||
use crate::streaming::event_parser::protocols::{
|
||||
bonk::parser::BONK_PROGRAM_ID, pumpfun::parser::PUMPFUN_PROGRAM_ID,
|
||||
pumpswap::parser::PUMPSWAP_PROGRAM_ID, raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
|
||||
raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, BonkEventParser, RaydiumCpmmEventParser,
|
||||
RaydiumClmmEventParser,
|
||||
bonk::parser::BONK_PROGRAM_ID, pumpfun::parser::PUMPFUN_PROGRAM_ID, pumpswap::parser::PUMPSWAP_PROGRAM_ID, raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, BonkEventParser, RaydiumAmmV4EventParser, RaydiumClmmEventParser, RaydiumCpmmEventParser
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -22,6 +19,7 @@ pub enum Protocol {
|
||||
Bonk,
|
||||
RaydiumCpmm,
|
||||
RaydiumClmm,
|
||||
RaydiumAmmV4,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
@@ -32,6 +30,7 @@ impl Protocol {
|
||||
Protocol::Bonk => vec![BONK_PROGRAM_ID],
|
||||
Protocol::RaydiumCpmm => vec![RAYDIUM_CPMM_PROGRAM_ID],
|
||||
Protocol::RaydiumClmm => vec![RAYDIUM_CLMM_PROGRAM_ID],
|
||||
Protocol::RaydiumAmmV4 => vec![RAYDIUM_AMM_V4_PROGRAM_ID],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +43,7 @@ impl std::fmt::Display for Protocol {
|
||||
Protocol::Bonk => write!(f, "Bonk"),
|
||||
Protocol::RaydiumCpmm => write!(f, "RaydiumCpmm"),
|
||||
Protocol::RaydiumClmm => write!(f, "RaydiumClmm"),
|
||||
Protocol::RaydiumAmmV4 => write!(f, "RaydiumAmmV4"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@ impl std::str::FromStr for Protocol {
|
||||
"bonk" => Ok(Protocol::Bonk),
|
||||
"raydiumcpmm" => Ok(Protocol::RaydiumCpmm),
|
||||
"raydiumclmm" => Ok(Protocol::RaydiumClmm),
|
||||
"raydiumammv4" => Ok(Protocol::RaydiumAmmV4),
|
||||
_ => Err(anyhow!("Unsupported protocol: {}", s)),
|
||||
}
|
||||
}
|
||||
@@ -65,12 +66,13 @@ impl std::str::FromStr for Protocol {
|
||||
|
||||
static EVENT_PARSERS: LazyLock<HashMap<Protocol, Arc<dyn EventParser>>> = LazyLock::new(|| {
|
||||
// 预分配容量,避免动态扩容
|
||||
let mut parsers: HashMap<Protocol, Arc<dyn EventParser>> = HashMap::with_capacity(5);
|
||||
let mut parsers: HashMap<Protocol, Arc<dyn EventParser>> = HashMap::with_capacity(6);
|
||||
parsers.insert(Protocol::PumpSwap, Arc::new(PumpSwapEventParser::new()));
|
||||
parsers.insert(Protocol::PumpFun, Arc::new(PumpFunEventParser::new()));
|
||||
parsers.insert(Protocol::Bonk, Arc::new(BonkEventParser::new()));
|
||||
parsers.insert(Protocol::RaydiumCpmm, Arc::new(RaydiumCpmmEventParser::new()));
|
||||
parsers.insert(Protocol::RaydiumClmm, Arc::new(RaydiumClmmEventParser::new()));
|
||||
parsers.insert(Protocol::RaydiumAmmV4, Arc::new(RaydiumAmmV4EventParser::new()));
|
||||
parsers
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod pumpswap;
|
||||
pub mod bonk;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod raydium_clmm;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod block;
|
||||
pub mod mutil;
|
||||
|
||||
@@ -11,5 +12,6 @@ pub use pumpswap::PumpSwapEventParser;
|
||||
pub use bonk::BonkEventParser;
|
||||
pub use raydium_cpmm::RaydiumCpmmEventParser;
|
||||
pub use raydium_clmm::RaydiumClmmEventParser;
|
||||
pub use raydium_amm_v4::RaydiumAmmV4EventParser;
|
||||
pub use block::block_meta_event::BlockMetaEvent;
|
||||
pub use mutil::MutilEventParser;
|
||||
@@ -104,14 +104,84 @@ impl_unified_event!(
|
||||
creator_fee
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpFunMigrateEvent {
|
||||
#[borsh(skip)]
|
||||
pub metadata: EventMetadata,
|
||||
|
||||
pub user: Pubkey,
|
||||
pub mint: Pubkey,
|
||||
pub mint_amount: u64,
|
||||
pub sol_amount: u64,
|
||||
pub pool_migration_fee: u64,
|
||||
pub bonding_curve: Pubkey,
|
||||
pub timestamp: i64,
|
||||
pub pool: Pubkey,
|
||||
|
||||
#[borsh(skip)]
|
||||
pub global: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub withdraw_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub associated_bonding_curve: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub system_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub token_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pump_amm: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pool_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pool_authority_mint_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pool_authority_wsol_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub amm_global_config: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub wsol_mint: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub lp_mint: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub user_pool_token_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pool_base_token_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pool_quote_token_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub token_2022_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub associated_token_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub pump_amm_event_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub event_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub program: Pubkey,
|
||||
}
|
||||
|
||||
impl_unified_event!(
|
||||
PumpFunMigrateEvent,
|
||||
user,
|
||||
mint,
|
||||
mint_amount,
|
||||
sol_amount,
|
||||
pool_migration_fee,
|
||||
bonding_curve,
|
||||
timestamp,
|
||||
pool
|
||||
);
|
||||
|
||||
/// 事件鉴别器常量
|
||||
pub mod discriminators {
|
||||
// 事件鉴别器
|
||||
pub const CREATE_TOKEN_EVENT: &str = "0xe445a52e51cb9a1d1b72a94ddeeb6376";
|
||||
pub const TRADE_EVENT: &str = "0xe445a52e51cb9a1dbddb7fd34ee661ee";
|
||||
pub const COMPLETE_PUMP_AMM_MIGRATION_EVENT: &str = "0xe445a52e51cb9a1dbde95db95c94ea94";
|
||||
|
||||
// 指令鉴别器
|
||||
pub const CREATE_TOKEN_IX: &[u8] = &[24, 30, 200, 40, 5, 28, 7, 119];
|
||||
pub const BUY_IX: &[u8] = &[102, 6, 61, 18, 1, 218, 235, 234];
|
||||
pub const SELL_IX: &[u8] = &[51, 230, 133, 164, 1, 127, 131, 173];
|
||||
pub const MIGRATE_IX: &[u8] = &[155, 234, 231, 146, 236, 158, 162, 30];
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use solana_transaction_status::UiCompiledInstruction;
|
||||
use crate::streaming::event_parser::{
|
||||
common::{EventMetadata, EventType, ProtocolType},
|
||||
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
|
||||
protocols::pumpfun::{discriminators, PumpFunCreateTokenEvent, PumpFunTradeEvent},
|
||||
protocols::pumpfun::{
|
||||
discriminators, PumpFunCreateTokenEvent, PumpFunMigrateEvent, PumpFunTradeEvent,
|
||||
},
|
||||
};
|
||||
|
||||
/// PumpFun程序ID
|
||||
@@ -56,6 +58,15 @@ impl PumpFunEventParser {
|
||||
inner_instruction_parser: Self::parse_trade_inner_instruction,
|
||||
instruction_parser: Self::parse_sell_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: PUMPFUN_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::PumpFun,
|
||||
inner_instruction_discriminator: discriminators::COMPLETE_PUMP_AMM_MIGRATION_EVENT,
|
||||
instruction_discriminator: discriminators::MIGRATE_IX,
|
||||
event_type: EventType::PumpFunMigrate,
|
||||
inner_instruction_parser: Self::parse_migrate_inner_instruction,
|
||||
instruction_parser: Self::parse_migrate_instruction,
|
||||
},
|
||||
];
|
||||
|
||||
let inner = GenericEventParser::new(vec![PUMPFUN_PROGRAM_ID], configs);
|
||||
@@ -63,6 +74,20 @@ impl PumpFunEventParser {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// 解析迁移事件
|
||||
fn parse_migrate_inner_instruction(
|
||||
data: &[u8],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if let Ok(event) = borsh::from_slice::<PumpFunMigrateEvent>(data) {
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, event.user, event.mint));
|
||||
Some(Box::new(PumpFunMigrateEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析创建代币日志事件
|
||||
fn parse_create_token_inner_instruction(
|
||||
data: &[u8],
|
||||
@@ -72,15 +97,9 @@ impl PumpFunEventParser {
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature,
|
||||
event.name,
|
||||
event.symbol,
|
||||
event.mint
|
||||
metadata.signature, event.name, event.symbol, event.mint
|
||||
));
|
||||
Some(Box::new(PumpFunCreateTokenEvent {
|
||||
metadata,
|
||||
..event
|
||||
}))
|
||||
Some(Box::new(PumpFunCreateTokenEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -95,15 +114,9 @@ impl PumpFunEventParser {
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature,
|
||||
event.mint,
|
||||
event.user,
|
||||
event.is_buy
|
||||
metadata.signature, event.mint, event.user, event.is_buy
|
||||
));
|
||||
Some(Box::new(PumpFunTradeEvent {
|
||||
metadata,
|
||||
..event
|
||||
}))
|
||||
Some(Box::new(PumpFunTradeEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -138,13 +151,7 @@ impl PumpFunEventParser {
|
||||
};
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature,
|
||||
name,
|
||||
symbol,
|
||||
accounts[0]
|
||||
));
|
||||
metadata.set_id(format!("{}-{}-{}-{}", metadata.signature, name, symbol, accounts[0]));
|
||||
|
||||
Some(Box::new(PumpFunCreateTokenEvent {
|
||||
metadata,
|
||||
@@ -173,13 +180,7 @@ impl PumpFunEventParser {
|
||||
let amount = u64::from_le_bytes(data[0..8].try_into().unwrap());
|
||||
let max_sol_cost = u64::from_le_bytes(data[8..16].try_into().unwrap());
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature,
|
||||
accounts[2],
|
||||
accounts[6],
|
||||
true
|
||||
));
|
||||
metadata.set_id(format!("{}-{}-{}-{}", metadata.signature, accounts[2], accounts[6], true));
|
||||
Some(Box::new(PumpFunTradeEvent {
|
||||
metadata,
|
||||
fee_recipient: accounts[1],
|
||||
@@ -210,13 +211,8 @@ impl PumpFunEventParser {
|
||||
let amount = u64::from_le_bytes(data[0..8].try_into().unwrap());
|
||||
let min_sol_output = u64::from_le_bytes(data[8..16].try_into().unwrap());
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature,
|
||||
accounts[2],
|
||||
accounts[6],
|
||||
false
|
||||
));
|
||||
metadata
|
||||
.set_id(format!("{}-{}-{}-{}", metadata.signature, accounts[2], accounts[6], false));
|
||||
Some(Box::new(PumpFunTradeEvent {
|
||||
metadata,
|
||||
fee_recipient: accounts[1],
|
||||
@@ -232,6 +228,47 @@ impl PumpFunEventParser {
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析迁移指令事件
|
||||
fn parse_migrate_instruction(
|
||||
_data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if accounts.len() < 24 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[5], accounts[2]));
|
||||
Some(Box::new(PumpFunMigrateEvent {
|
||||
metadata,
|
||||
global: accounts[0],
|
||||
withdraw_authority: accounts[1],
|
||||
mint: accounts[2],
|
||||
bonding_curve: accounts[3],
|
||||
associated_bonding_curve: accounts[4],
|
||||
user: accounts[5],
|
||||
system_program: accounts[6],
|
||||
token_program: accounts[7],
|
||||
pump_amm: accounts[8],
|
||||
pool: accounts[9],
|
||||
pool_authority: accounts[10],
|
||||
pool_authority_mint_account: accounts[11],
|
||||
pool_authority_wsol_account: accounts[12],
|
||||
amm_global_config: accounts[13],
|
||||
wsol_mint: accounts[14],
|
||||
lp_mint: accounts[15],
|
||||
user_pool_token_account: accounts[16],
|
||||
pool_base_token_account: accounts[17],
|
||||
pool_quote_token_account: accounts[18],
|
||||
token_2022_program: accounts[19],
|
||||
associated_token_program: accounts[20],
|
||||
pump_amm_event_authority: accounts[21],
|
||||
event_authority: accounts[22],
|
||||
program: accounts[23],
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
use crate::impl_unified_event;
|
||||
use crate::streaming::event_parser::common::EventMetadata;
|
||||
use borsh::BorshDeserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
/// 交易
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumAmmV4SwapEvent {
|
||||
pub metadata: EventMetadata,
|
||||
// base in
|
||||
pub amount_in: u64,
|
||||
pub minimum_amount_out: u64,
|
||||
// base out
|
||||
pub max_amount_in: u64,
|
||||
pub amount_out: u64,
|
||||
|
||||
pub token_program: Pubkey,
|
||||
pub amm: Pubkey,
|
||||
pub amm_authority: Pubkey,
|
||||
pub amm_open_orders: Pubkey,
|
||||
pub amm_target_orders: Option<Pubkey>,
|
||||
pub pool_coin_token_account: Pubkey,
|
||||
pub pool_pc_token_account: Pubkey,
|
||||
pub serum_program: Pubkey,
|
||||
pub serum_market: Pubkey,
|
||||
pub serum_bids: Pubkey,
|
||||
pub serum_asks: Pubkey,
|
||||
pub serum_event_queue: Pubkey,
|
||||
pub serum_coin_vault_account: Pubkey,
|
||||
pub serum_pc_vault_account: Pubkey,
|
||||
pub serum_vault_signer: Pubkey,
|
||||
pub user_source_token_account: Pubkey,
|
||||
pub user_destination_token_account: Pubkey,
|
||||
pub user_source_owner: Pubkey,
|
||||
}
|
||||
|
||||
impl_unified_event!(RaydiumAmmV4SwapEvent,);
|
||||
|
||||
/// 添加流动性
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumAmmV4DepositEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub max_coin_amount: u64,
|
||||
pub max_pc_amount: u64,
|
||||
pub base_side: u64,
|
||||
|
||||
pub token_program: Pubkey,
|
||||
pub amm: Pubkey,
|
||||
pub amm_authority: Pubkey,
|
||||
pub amm_open_orders: Pubkey,
|
||||
pub amm_target_orders: Pubkey,
|
||||
pub lp_mint_address: Pubkey,
|
||||
pub pool_coin_token_account: Pubkey,
|
||||
pub pool_pc_token_account: Pubkey,
|
||||
pub serum_market: Pubkey,
|
||||
pub user_coin_token_account: Pubkey,
|
||||
pub user_pc_token_account: Pubkey,
|
||||
pub user_lp_token_account: Pubkey,
|
||||
pub user_owner: Pubkey,
|
||||
pub serum_event_queue: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumAmmV4DepositEvent,);
|
||||
|
||||
/// 初始化
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumAmmV4Initialize2Event {
|
||||
pub metadata: EventMetadata,
|
||||
pub nonce: u8,
|
||||
pub open_time: u64,
|
||||
pub init_pc_amount: u64,
|
||||
pub init_coin_amount: u64,
|
||||
|
||||
pub token_program: Pubkey,
|
||||
pub spl_associated_token_account: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub rent: Pubkey,
|
||||
pub amm: Pubkey,
|
||||
pub amm_authority: Pubkey,
|
||||
pub amm_open_orders: Pubkey,
|
||||
pub lp_mint: Pubkey,
|
||||
pub coin_mint: Pubkey,
|
||||
pub pc_mint: Pubkey,
|
||||
pub pool_coin_token_account: Pubkey,
|
||||
pub pool_pc_token_account: Pubkey,
|
||||
pub pool_withdraw_queue: Pubkey,
|
||||
pub amm_target_orders: Pubkey,
|
||||
pub pool_temp_lp: Pubkey,
|
||||
pub serum_program: Pubkey,
|
||||
pub serum_market: Pubkey,
|
||||
pub user_wallet: Pubkey,
|
||||
pub user_token_coin: Pubkey,
|
||||
pub user_token_pc: Pubkey,
|
||||
pub user_lp_token_account: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumAmmV4Initialize2Event,);
|
||||
|
||||
/// 移除流动性
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumAmmV4WithdrawEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub amount: u64,
|
||||
|
||||
pub token_program: Pubkey,
|
||||
pub amm: Pubkey,
|
||||
pub amm_authority: Pubkey,
|
||||
pub amm_open_orders: Pubkey,
|
||||
pub amm_target_orders: Pubkey,
|
||||
pub lp_mint_address: Pubkey,
|
||||
pub pool_coin_token_account: Pubkey,
|
||||
pub pool_pc_token_account: Pubkey,
|
||||
pub pool_withdraw_queue: Pubkey,
|
||||
pub pool_temp_lp_token_account: Pubkey,
|
||||
pub serum_program: Pubkey,
|
||||
pub serum_market: Pubkey,
|
||||
pub serum_coin_vault_account: Pubkey,
|
||||
pub serum_pc_vault_account: Pubkey,
|
||||
pub serum_vault_signer: Pubkey,
|
||||
pub user_lp_token_account: Pubkey,
|
||||
pub user_coin_token_account: Pubkey,
|
||||
pub user_pc_token_account: Pubkey,
|
||||
pub user_owner: Pubkey,
|
||||
pub serum_event_queue: Pubkey,
|
||||
pub serum_bids: Pubkey,
|
||||
pub serum_asks: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumAmmV4WithdrawEvent,);
|
||||
|
||||
/// 提现
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumAmmV4WithdrawPnlEvent {
|
||||
pub metadata: EventMetadata,
|
||||
|
||||
pub token_program: Pubkey,
|
||||
pub amm: Pubkey,
|
||||
pub amm_config: Pubkey,
|
||||
pub amm_authority: Pubkey,
|
||||
pub amm_open_orders: Pubkey,
|
||||
pub pool_coin_token_account: Pubkey,
|
||||
pub pool_pc_token_account: Pubkey,
|
||||
pub coin_pnl_token_account: Pubkey,
|
||||
pub pc_pnl_token_account: Pubkey,
|
||||
pub pnl_owner_account: Pubkey,
|
||||
pub amm_target_orders: Pubkey,
|
||||
pub serum_program: Pubkey,
|
||||
pub serum_market: Pubkey,
|
||||
pub serum_event_queue: Pubkey,
|
||||
pub serum_coin_vault_account: Pubkey,
|
||||
pub serum_pc_vault_account: Pubkey,
|
||||
pub serum_vault_signer: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumAmmV4WithdrawPnlEvent,);
|
||||
|
||||
/// 事件鉴别器常量
|
||||
pub mod discriminators {
|
||||
// 指令鉴别器
|
||||
pub const SWAP_BASE_IN: &[u8] = &[9];
|
||||
pub const SWAP_BASE_OUT: &[u8] = &[11];
|
||||
pub const DEPOSIT: &[u8] = &[03];
|
||||
pub const INITIALIZE2: &[u8] = &[01];
|
||||
pub const WITHDRAW: &[u8] = &[04];
|
||||
pub const WITHDRAW_PNL: &[u8] = &[07];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod events;
|
||||
pub mod parser;
|
||||
|
||||
pub use events::*;
|
||||
pub use parser::RaydiumAmmV4EventParser;
|
||||
@@ -0,0 +1,439 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use prost_types::Timestamp;
|
||||
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
||||
use solana_transaction_status::UiCompiledInstruction;
|
||||
|
||||
use crate::streaming::event_parser::{
|
||||
common::{read_u64_le, EventMetadata, EventType, ProtocolType},
|
||||
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
|
||||
protocols::raydium_amm_v4::{
|
||||
discriminators, RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event,
|
||||
RaydiumAmmV4SwapEvent, RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent,
|
||||
},
|
||||
};
|
||||
|
||||
/// Raydium CPMM程序ID
|
||||
pub const RAYDIUM_AMM_V4_PROGRAM_ID: Pubkey =
|
||||
solana_sdk::pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
|
||||
|
||||
/// Raydium CPMM事件解析器
|
||||
pub struct RaydiumAmmV4EventParser {
|
||||
inner: GenericEventParser,
|
||||
}
|
||||
|
||||
impl Default for RaydiumAmmV4EventParser {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl RaydiumAmmV4EventParser {
|
||||
pub fn new() -> Self {
|
||||
// 配置所有事件类型
|
||||
let configs = vec![
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP_BASE_IN,
|
||||
event_type: EventType::RaydiumAmmV4SwapBaseIn,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_base_input_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP_BASE_OUT,
|
||||
event_type: EventType::RaydiumAmmV4SwapBaseOut,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_base_output_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::DEPOSIT,
|
||||
event_type: EventType::RaydiumAmmV4Deposit,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_deposit_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::INITIALIZE2,
|
||||
event_type: EventType::RaydiumAmmV4Initialize2,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_initialize2_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::WITHDRAW,
|
||||
event_type: EventType::RaydiumAmmV4Withdraw,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_withdraw_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumAmmV4,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::WITHDRAW_PNL,
|
||||
event_type: EventType::RaydiumAmmV4WithdrawPnl,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_withdraw_pnl_instruction,
|
||||
},
|
||||
];
|
||||
|
||||
let inner = GenericEventParser::new(vec![RAYDIUM_AMM_V4_PROGRAM_ID], configs);
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
fn empty_parse(_data: &[u8], _metadata: EventMetadata) -> Option<Box<dyn UnifiedEvent>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// 解析提现指令事件
|
||||
fn parse_withdraw_pnl_instruction(
|
||||
_data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if accounts.len() < 17 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
Some(Box::new(RaydiumAmmV4WithdrawPnlEvent {
|
||||
metadata,
|
||||
token_program: accounts[0],
|
||||
amm: accounts[1],
|
||||
amm_config: accounts[2],
|
||||
amm_authority: accounts[3],
|
||||
amm_open_orders: accounts[4],
|
||||
pool_coin_token_account: accounts[5],
|
||||
pool_pc_token_account: accounts[6],
|
||||
coin_pnl_token_account: accounts[7],
|
||||
pc_pnl_token_account: accounts[8],
|
||||
pnl_owner_account: accounts[9],
|
||||
amm_target_orders: accounts[10],
|
||||
serum_program: accounts[11],
|
||||
serum_market: accounts[12],
|
||||
serum_event_queue: accounts[13],
|
||||
serum_coin_vault_account: accounts[14],
|
||||
serum_pc_vault_account: accounts[15],
|
||||
serum_vault_signer: accounts[16],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析移除流动性指令事件
|
||||
fn parse_withdraw_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 8 || accounts.len() < 22 {
|
||||
return None;
|
||||
}
|
||||
let amount = read_u64_le(data, 0)?;
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
Some(Box::new(RaydiumAmmV4WithdrawEvent {
|
||||
metadata,
|
||||
amount,
|
||||
|
||||
token_program: accounts[0],
|
||||
amm: accounts[1],
|
||||
amm_authority: accounts[2],
|
||||
amm_open_orders: accounts[3],
|
||||
amm_target_orders: accounts[4],
|
||||
lp_mint_address: accounts[5],
|
||||
pool_coin_token_account: accounts[6],
|
||||
pool_pc_token_account: accounts[7],
|
||||
pool_withdraw_queue: accounts[8],
|
||||
pool_temp_lp_token_account: accounts[9],
|
||||
serum_program: accounts[10],
|
||||
serum_market: accounts[11],
|
||||
serum_coin_vault_account: accounts[12],
|
||||
serum_pc_vault_account: accounts[13],
|
||||
serum_vault_signer: accounts[14],
|
||||
user_lp_token_account: accounts[15],
|
||||
user_coin_token_account: accounts[16],
|
||||
user_pc_token_account: accounts[17],
|
||||
user_owner: accounts[18],
|
||||
serum_event_queue: accounts[19],
|
||||
serum_bids: accounts[20],
|
||||
serum_asks: accounts[21],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析初始化指令事件
|
||||
fn parse_initialize2_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 25 || accounts.len() < 21 {
|
||||
return None;
|
||||
}
|
||||
let nonce = data[0];
|
||||
let open_time = read_u64_le(data, 1)?;
|
||||
let init_pc_amount = read_u64_le(data, 9)?;
|
||||
let init_coin_amount = read_u64_le(data, 17)?;
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
Some(Box::new(RaydiumAmmV4Initialize2Event {
|
||||
metadata,
|
||||
nonce,
|
||||
open_time,
|
||||
init_pc_amount,
|
||||
init_coin_amount,
|
||||
|
||||
token_program: accounts[0],
|
||||
spl_associated_token_account: accounts[1],
|
||||
system_program: accounts[2],
|
||||
rent: accounts[3],
|
||||
amm: accounts[4],
|
||||
amm_authority: accounts[5],
|
||||
amm_open_orders: accounts[6],
|
||||
lp_mint: accounts[7],
|
||||
coin_mint: accounts[8],
|
||||
pc_mint: accounts[9],
|
||||
pool_coin_token_account: accounts[10],
|
||||
pool_pc_token_account: accounts[11],
|
||||
pool_withdraw_queue: accounts[12],
|
||||
amm_target_orders: accounts[13],
|
||||
pool_temp_lp: accounts[14],
|
||||
serum_program: accounts[15],
|
||||
serum_market: accounts[16],
|
||||
user_wallet: accounts[17],
|
||||
user_token_coin: accounts[18],
|
||||
user_token_pc: accounts[19],
|
||||
user_lp_token_account: accounts[20],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析添加流动性指令事件
|
||||
fn parse_deposit_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 24 || accounts.len() < 14 {
|
||||
return None;
|
||||
}
|
||||
let max_coin_amount = read_u64_le(data, 0)?;
|
||||
let max_pc_amount = read_u64_le(data, 8)?;
|
||||
let base_side = read_u64_le(data, 16)?;
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
Some(Box::new(RaydiumAmmV4DepositEvent {
|
||||
metadata,
|
||||
max_coin_amount,
|
||||
max_pc_amount,
|
||||
base_side,
|
||||
|
||||
token_program: accounts[0],
|
||||
amm: accounts[1],
|
||||
amm_authority: accounts[2],
|
||||
amm_open_orders: accounts[3],
|
||||
amm_target_orders: accounts[4],
|
||||
lp_mint_address: accounts[5],
|
||||
pool_coin_token_account: accounts[6],
|
||||
pool_pc_token_account: accounts[7],
|
||||
serum_market: accounts[8],
|
||||
user_coin_token_account: accounts[9],
|
||||
user_pc_token_account: accounts[10],
|
||||
user_lp_token_account: accounts[11],
|
||||
user_owner: accounts[12],
|
||||
serum_event_queue: accounts[13],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析买入指令事件
|
||||
fn parse_swap_base_output_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 16 || accounts.len() < 17 {
|
||||
return None;
|
||||
}
|
||||
let max_amount_in = read_u64_le(data, 0)?;
|
||||
let amount_out = read_u64_le(data, 8)?;
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
let mut accounts = accounts.to_vec();
|
||||
if accounts.len() == 17 {
|
||||
// 添加一个默认的 Pubkey 作为 amm_target_orders 的占位符
|
||||
// 因为在某些情况下,amm_target_orders 可能是可选的
|
||||
accounts.insert(4, Pubkey::default());
|
||||
}
|
||||
|
||||
Some(Box::new(RaydiumAmmV4SwapEvent {
|
||||
metadata,
|
||||
max_amount_in,
|
||||
amount_out,
|
||||
|
||||
token_program: accounts[0],
|
||||
amm: accounts[1],
|
||||
amm_authority: accounts[2],
|
||||
amm_open_orders: accounts[3],
|
||||
amm_target_orders: Some(accounts[4]),
|
||||
pool_coin_token_account: accounts[5],
|
||||
pool_pc_token_account: accounts[6],
|
||||
serum_program: accounts[7],
|
||||
serum_market: accounts[8],
|
||||
serum_bids: accounts[9],
|
||||
serum_asks: accounts[10],
|
||||
serum_event_queue: accounts[11],
|
||||
serum_coin_vault_account: accounts[12],
|
||||
serum_pc_vault_account: accounts[13],
|
||||
serum_vault_signer: accounts[14],
|
||||
user_source_token_account: accounts[15],
|
||||
user_destination_token_account: accounts[16],
|
||||
user_source_owner: accounts[17],
|
||||
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析买入指令事件
|
||||
fn parse_swap_base_input_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 16 || accounts.len() < 17 {
|
||||
return None;
|
||||
}
|
||||
let amount_in = read_u64_le(data, 0)?;
|
||||
let minimum_amount_out = read_u64_le(data, 8)?;
|
||||
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!(
|
||||
"{}-{}-{}-{}",
|
||||
metadata.signature, accounts[3], accounts[10], accounts[11]
|
||||
));
|
||||
|
||||
let mut accounts = accounts.to_vec();
|
||||
if accounts.len() == 17 {
|
||||
// 添加一个默认的 Pubkey 作为 amm_target_orders 的占位符
|
||||
// 因为在某些情况下,amm_target_orders 可能是可选的
|
||||
accounts.insert(4, Pubkey::default());
|
||||
}
|
||||
|
||||
Some(Box::new(RaydiumAmmV4SwapEvent {
|
||||
metadata,
|
||||
amount_in,
|
||||
minimum_amount_out,
|
||||
|
||||
token_program: accounts[0],
|
||||
amm: accounts[1],
|
||||
amm_authority: accounts[2],
|
||||
amm_open_orders: accounts[3],
|
||||
amm_target_orders: Some(accounts[4]),
|
||||
pool_coin_token_account: accounts[5],
|
||||
pool_pc_token_account: accounts[6],
|
||||
serum_program: accounts[7],
|
||||
serum_market: accounts[8],
|
||||
serum_bids: accounts[9],
|
||||
serum_asks: accounts[10],
|
||||
serum_event_queue: accounts[11],
|
||||
serum_coin_vault_account: accounts[12],
|
||||
serum_pc_vault_account: accounts[13],
|
||||
serum_vault_signer: accounts[14],
|
||||
user_source_token_account: accounts[15],
|
||||
user_destination_token_account: accounts[16],
|
||||
user_source_owner: accounts[17],
|
||||
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EventParser for RaydiumAmmV4EventParser {
|
||||
fn inner_instruction_configs(&self) -> HashMap<&'static str, Vec<GenericEventParseConfig>> {
|
||||
self.inner.inner_instruction_configs()
|
||||
}
|
||||
fn instruction_configs(&self) -> HashMap<Vec<u8>, Vec<GenericEventParseConfig>> {
|
||||
self.inner.instruction_configs()
|
||||
}
|
||||
fn parse_events_from_inner_instruction(
|
||||
&self,
|
||||
inner_instruction: &UiCompiledInstruction,
|
||||
signature: &str,
|
||||
slot: u64,
|
||||
block_time: Option<Timestamp>,
|
||||
program_received_time_ms: i64,
|
||||
index: String,
|
||||
) -> Vec<Box<dyn UnifiedEvent>> {
|
||||
self.inner.parse_events_from_inner_instruction(
|
||||
inner_instruction,
|
||||
signature,
|
||||
slot,
|
||||
block_time,
|
||||
program_received_time_ms,
|
||||
index,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_events_from_instruction(
|
||||
&self,
|
||||
instruction: &CompiledInstruction,
|
||||
accounts: &[Pubkey],
|
||||
signature: &str,
|
||||
slot: u64,
|
||||
block_time: Option<Timestamp>,
|
||||
program_received_time_ms: i64,
|
||||
index: String,
|
||||
) -> Vec<Box<dyn UnifiedEvent>> {
|
||||
self.inner.parse_events_from_instruction(
|
||||
instruction,
|
||||
accounts,
|
||||
signature,
|
||||
slot,
|
||||
block_time,
|
||||
program_received_time_ms,
|
||||
index,
|
||||
)
|
||||
}
|
||||
|
||||
fn should_handle(&self, program_id: &Pubkey) -> bool {
|
||||
self.inner.should_handle(program_id)
|
||||
}
|
||||
|
||||
fn supported_program_ids(&self) -> Vec<Pubkey> {
|
||||
self.inner.supported_program_ids()
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ pub struct RaydiumClmmSwapEvent {
|
||||
|
||||
impl_unified_event!(RaydiumClmmSwapEvent,);
|
||||
|
||||
/// 交易v2
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmSwapV2Event {
|
||||
pub metadata: EventMetadata,
|
||||
@@ -51,9 +52,180 @@ pub struct RaydiumClmmSwapV2Event {
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmSwapV2Event,);
|
||||
|
||||
/// 关闭仓位
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmClosePositionEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub nft_owner: Pubkey,
|
||||
pub position_nft_mint: Pubkey,
|
||||
pub position_nft_account: Pubkey,
|
||||
pub personal_position: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmClosePositionEvent,);
|
||||
|
||||
/// 减少流动性v2
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmDecreaseLiquidityV2Event {
|
||||
pub metadata: EventMetadata,
|
||||
pub liquidity: u128,
|
||||
pub amount0_min: u64,
|
||||
pub amount1_min: u64,
|
||||
pub nft_owner: Pubkey,
|
||||
pub nft_account: Pubkey,
|
||||
pub personal_position: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub protocol_position: Pubkey,
|
||||
pub token_vault0: Pubkey,
|
||||
pub token_vault1: Pubkey,
|
||||
pub tick_array_lower: Pubkey,
|
||||
pub tick_array_upper: Pubkey,
|
||||
pub recipient_token_account0: Pubkey,
|
||||
pub recipient_token_account1: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub memo_program: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
pub remaining_accounts: Vec<Pubkey>,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmDecreaseLiquidityV2Event,);
|
||||
|
||||
/// 创建池
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmCreatePoolEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub sqrt_price_x64: u128,
|
||||
pub open_time: u64,
|
||||
pub pool_creator: Pubkey,
|
||||
pub amm_config: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub token_mint0: Pubkey,
|
||||
pub token_mint1: Pubkey,
|
||||
pub token_vault0: Pubkey,
|
||||
pub token_vault1: Pubkey,
|
||||
pub observation_state: Pubkey,
|
||||
pub tick_array_bitmap: Pubkey,
|
||||
pub token_program0: Pubkey,
|
||||
pub token_program1: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub rent: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmCreatePoolEvent,);
|
||||
|
||||
/// 增加流动性v2
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmIncreaseLiquidityV2Event {
|
||||
pub metadata: EventMetadata,
|
||||
pub liquidity: u128,
|
||||
pub amount0_max: u64,
|
||||
pub amount1_max: u64,
|
||||
pub base_flag: Option<bool>,
|
||||
pub nft_owner: Pubkey,
|
||||
pub nft_account: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub protocol_position: Pubkey,
|
||||
pub personal_position: Pubkey,
|
||||
pub tick_array_lower: Pubkey,
|
||||
pub tick_array_upper: Pubkey,
|
||||
pub token_account0: Pubkey,
|
||||
pub token_account1: Pubkey,
|
||||
pub token_vault0: Pubkey,
|
||||
pub token_vault1: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmIncreaseLiquidityV2Event,);
|
||||
|
||||
/// 打开仓位v2
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmOpenPositionWithToken22NftEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub tick_lower_index: i32,
|
||||
pub tick_upper_index: i32,
|
||||
pub tick_array_lower_start_index: i32,
|
||||
pub tick_array_upper_start_index: i32,
|
||||
pub liquidity: u128,
|
||||
pub amount0_max: u64,
|
||||
pub amount1_max: u64,
|
||||
pub with_metadata: bool,
|
||||
pub base_flag: Option<bool>,
|
||||
|
||||
pub payer: Pubkey,
|
||||
pub position_nft_owner: Pubkey,
|
||||
pub position_nft_mint: Pubkey,
|
||||
pub position_nft_account: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub protocol_position: Pubkey,
|
||||
pub tick_array_lower: Pubkey,
|
||||
pub tick_array_upper: Pubkey,
|
||||
pub personal_position: Pubkey,
|
||||
pub token_account0: Pubkey,
|
||||
pub token_account1: Pubkey,
|
||||
pub token_vault0: Pubkey,
|
||||
pub token_vault1: Pubkey,
|
||||
pub rent: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub associated_token_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmOpenPositionWithToken22NftEvent,);
|
||||
|
||||
/// 打开仓位V2
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RaydiumClmmOpenPositionV2Event {
|
||||
pub metadata: EventMetadata,
|
||||
pub tick_lower_index: i32,
|
||||
pub tick_upper_index: i32,
|
||||
pub tick_array_lower_start_index: i32,
|
||||
pub tick_array_upper_start_index: i32,
|
||||
pub liquidity: u128,
|
||||
pub amount0_max: u64,
|
||||
pub amount1_max: u64,
|
||||
pub with_metadata: bool,
|
||||
pub base_flag: Option<bool>,
|
||||
|
||||
pub payer: Pubkey,
|
||||
pub position_nft_owner: Pubkey,
|
||||
pub position_nft_mint: Pubkey,
|
||||
pub position_nft_account: Pubkey,
|
||||
pub metadata_account: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub protocol_position: Pubkey,
|
||||
pub tick_array_lower: Pubkey,
|
||||
pub tick_array_upper: Pubkey,
|
||||
pub personal_position: Pubkey,
|
||||
pub token_account0: Pubkey,
|
||||
pub token_account1: Pubkey,
|
||||
pub token_vault0: Pubkey,
|
||||
pub token_vault1: Pubkey,
|
||||
pub rent: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub associated_token_program: Pubkey,
|
||||
pub metadata_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
pub remaining_accounts: Vec<Pubkey>,
|
||||
}
|
||||
impl_unified_event!(RaydiumClmmOpenPositionV2Event,);
|
||||
|
||||
/// 事件鉴别器常量
|
||||
pub mod discriminators {
|
||||
// 指令鉴别器
|
||||
pub const SWAP: &[u8] = &[248, 198, 158, 145, 225, 117, 135, 200];
|
||||
pub const SWAP_V2: &[u8] = &[43, 4, 237, 11, 26, 201, 30, 98];
|
||||
pub const CLOSE_POSITION: &[u8] = &[123, 134, 81, 0, 49, 68, 98, 98];
|
||||
pub const INCREASE_LIQUIDITY_V2: &[u8] = &[133, 29, 89, 223, 69, 238, 176, 10];
|
||||
pub const DECREASE_LIQUIDITY_V2: &[u8] = &[58, 127, 188, 62, 79, 82, 196, 96];
|
||||
pub const CREATE_POOL: &[u8] = &[233, 146, 209, 142, 207, 104, 64, 188];
|
||||
pub const OPEN_POSITION_WITH_TOKEN_22_NFT: &[u8] = &[77, 255, 174, 82, 125, 29, 201, 46];
|
||||
pub const OPEN_POSITION_V2: &[u8] = &[77, 184, 74, 214, 112, 86, 241, 199];
|
||||
}
|
||||
|
||||
@@ -5,9 +5,17 @@ use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
||||
use solana_transaction_status::UiCompiledInstruction;
|
||||
|
||||
use crate::streaming::event_parser::{
|
||||
common::{read_u128_le, read_u64_le, read_u8_le, EventMetadata, EventType, ProtocolType},
|
||||
common::{
|
||||
read_i32_le, read_option_bool, read_u128_le, read_u64_le, read_u8_le, EventMetadata,
|
||||
EventType, ProtocolType,
|
||||
},
|
||||
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
|
||||
protocols::raydium_clmm::{discriminators, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event},
|
||||
protocols::raydium_clmm::{
|
||||
discriminators, RaydiumClmmClosePositionEvent, RaydiumClmmCreatePoolEvent,
|
||||
RaydiumClmmDecreaseLiquidityV2Event, RaydiumClmmIncreaseLiquidityV2Event,
|
||||
RaydiumClmmOpenPositionV2Event, RaydiumClmmOpenPositionWithToken22NftEvent,
|
||||
RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event,
|
||||
},
|
||||
};
|
||||
|
||||
/// Raydium CLMM程序ID
|
||||
@@ -35,7 +43,7 @@ impl RaydiumClmmEventParser {
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP,
|
||||
event_type: EventType::RaydiumClmmSwap,
|
||||
inner_instruction_parser: Self::parse_trade_inner_instruction,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
@@ -44,9 +52,63 @@ impl RaydiumClmmEventParser {
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP_V2,
|
||||
event_type: EventType::RaydiumClmmSwapV2,
|
||||
inner_instruction_parser: Self::parse_trade_inner_instruction,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_v2_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::CLOSE_POSITION,
|
||||
event_type: EventType::RaydiumClmmClosePosition,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_close_position_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::DECREASE_LIQUIDITY_V2,
|
||||
event_type: EventType::RaydiumClmmDecreaseLiquidityV2,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_decrease_liquidity_v2_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::CREATE_POOL,
|
||||
event_type: EventType::RaydiumClmmCreatePool,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_create_pool_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::INCREASE_LIQUIDITY_V2,
|
||||
event_type: EventType::RaydiumClmmIncreaseLiquidityV2,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_increase_liquidity_v2_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::OPEN_POSITION_WITH_TOKEN_22_NFT,
|
||||
event_type: EventType::RaydiumClmmOpenPositionWithToken22Nft,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_open_position_with_token_22_nft_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CLMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumClmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::OPEN_POSITION_V2,
|
||||
event_type: EventType::RaydiumClmmOpenPositionV2,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_open_position_v2_instruction,
|
||||
},
|
||||
];
|
||||
|
||||
let inner = GenericEventParser::new(vec![RAYDIUM_CLMM_PROGRAM_ID], configs);
|
||||
@@ -54,21 +116,234 @@ impl RaydiumClmmEventParser {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// 解析交易事件
|
||||
fn parse_trade_inner_instruction(
|
||||
_data: &[u8],
|
||||
_metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
fn empty_parse(_data: &[u8], _metadata: EventMetadata) -> Option<Box<dyn UnifiedEvent>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// 解析打开仓位V2指令事件
|
||||
fn parse_open_position_v2_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 51 || accounts.len() < 22 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmOpenPositionV2Event {
|
||||
metadata,
|
||||
tick_lower_index: read_i32_le(data, 0)?,
|
||||
tick_upper_index: read_i32_le(data, 4)?,
|
||||
tick_array_lower_start_index: read_i32_le(data, 8)?,
|
||||
tick_array_upper_start_index: read_i32_le(data, 12)?,
|
||||
liquidity: read_u128_le(data, 16)?,
|
||||
amount0_max: read_u64_le(data, 32)?,
|
||||
amount1_max: read_u64_le(data, 40)?,
|
||||
with_metadata: read_u8_le(data, 48)? == 1,
|
||||
base_flag: read_option_bool(data, &mut 49)?,
|
||||
payer: accounts[0],
|
||||
position_nft_owner: accounts[1],
|
||||
position_nft_mint: accounts[2],
|
||||
position_nft_account: accounts[3],
|
||||
metadata_account: accounts[4],
|
||||
pool_state: accounts[5],
|
||||
protocol_position: accounts[6],
|
||||
tick_array_lower: accounts[7],
|
||||
tick_array_upper: accounts[8],
|
||||
personal_position: accounts[9],
|
||||
token_account0: accounts[10],
|
||||
token_account1: accounts[11],
|
||||
token_vault0: accounts[12],
|
||||
token_vault1: accounts[13],
|
||||
rent: accounts[14],
|
||||
system_program: accounts[15],
|
||||
token_program: accounts[16],
|
||||
associated_token_program: accounts[17],
|
||||
metadata_program: accounts[18],
|
||||
token_program2022: accounts[19],
|
||||
vault0_mint: accounts[20],
|
||||
vault1_mint: accounts[21],
|
||||
remaining_accounts: accounts[22..].to_vec(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析打开仓位v2指令事件
|
||||
fn parse_open_position_with_token_22_nft_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 51 || accounts.len() < 20 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmOpenPositionWithToken22NftEvent {
|
||||
metadata,
|
||||
tick_lower_index: read_i32_le(data, 0)?,
|
||||
tick_upper_index: read_i32_le(data, 4)?,
|
||||
tick_array_lower_start_index: read_i32_le(data, 8)?,
|
||||
tick_array_upper_start_index: read_i32_le(data, 12)?,
|
||||
liquidity: read_u128_le(data, 16)?,
|
||||
amount0_max: read_u64_le(data, 32)?,
|
||||
amount1_max: read_u64_le(data, 40)?,
|
||||
with_metadata: read_u8_le(data, 48)? == 1,
|
||||
base_flag: read_option_bool(data, &mut 49)?,
|
||||
payer: accounts[0],
|
||||
position_nft_owner: accounts[1],
|
||||
position_nft_mint: accounts[2],
|
||||
position_nft_account: accounts[3],
|
||||
pool_state: accounts[4],
|
||||
protocol_position: accounts[5],
|
||||
tick_array_lower: accounts[6],
|
||||
tick_array_upper: accounts[7],
|
||||
personal_position: accounts[8],
|
||||
token_account0: accounts[9],
|
||||
token_account1: accounts[10],
|
||||
token_vault0: accounts[11],
|
||||
token_vault1: accounts[12],
|
||||
rent: accounts[13],
|
||||
system_program: accounts[14],
|
||||
token_program: accounts[15],
|
||||
associated_token_program: accounts[16],
|
||||
token_program2022: accounts[17],
|
||||
vault0_mint: accounts[18],
|
||||
vault1_mint: accounts[19],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析增加流动性v2指令事件
|
||||
fn parse_increase_liquidity_v2_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 34 || accounts.len() < 15 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmIncreaseLiquidityV2Event {
|
||||
metadata,
|
||||
liquidity: read_u128_le(data, 0)?,
|
||||
amount0_max: read_u64_le(data, 16)?,
|
||||
amount1_max: read_u64_le(data, 24)?,
|
||||
base_flag: read_option_bool(data, &mut 32)?,
|
||||
nft_owner: accounts[0],
|
||||
nft_account: accounts[1],
|
||||
pool_state: accounts[2],
|
||||
protocol_position: accounts[3],
|
||||
personal_position: accounts[4],
|
||||
tick_array_lower: accounts[5],
|
||||
tick_array_upper: accounts[6],
|
||||
token_account0: accounts[7],
|
||||
token_account1: accounts[8],
|
||||
token_vault0: accounts[9],
|
||||
token_vault1: accounts[10],
|
||||
token_program: accounts[11],
|
||||
token_program2022: accounts[12],
|
||||
vault0_mint: accounts[13],
|
||||
vault1_mint: accounts[14],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析创建池指令事件
|
||||
fn parse_create_pool_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 24 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmCreatePoolEvent {
|
||||
metadata,
|
||||
sqrt_price_x64: read_u128_le(data, 0)?,
|
||||
open_time: read_u64_le(data, 16)?,
|
||||
pool_creator: accounts[0],
|
||||
amm_config: accounts[1],
|
||||
pool_state: accounts[2],
|
||||
token_mint0: accounts[3],
|
||||
token_mint1: accounts[4],
|
||||
token_vault0: accounts[5],
|
||||
token_vault1: accounts[6],
|
||||
observation_state: accounts[7],
|
||||
tick_array_bitmap: accounts[8],
|
||||
token_program0: accounts[9],
|
||||
token_program1: accounts[10],
|
||||
system_program: accounts[11],
|
||||
rent: accounts[12],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析减少流动性v2指令事件
|
||||
fn parse_decrease_liquidity_v2_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 32 || accounts.len() < 16 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmDecreaseLiquidityV2Event {
|
||||
metadata,
|
||||
liquidity: read_u128_le(data, 0)?,
|
||||
amount0_min: read_u64_le(data, 16)?,
|
||||
amount1_min: read_u64_le(data, 24)?,
|
||||
nft_owner: accounts[0],
|
||||
nft_account: accounts[1],
|
||||
personal_position: accounts[2],
|
||||
pool_state: accounts[3],
|
||||
protocol_position: accounts[4],
|
||||
token_vault0: accounts[5],
|
||||
token_vault1: accounts[6],
|
||||
tick_array_lower: accounts[7],
|
||||
tick_array_upper: accounts[8],
|
||||
recipient_token_account0: accounts[9],
|
||||
recipient_token_account1: accounts[10],
|
||||
token_program: accounts[11],
|
||||
token_program2022: accounts[12],
|
||||
memo_program: accounts[13],
|
||||
vault0_mint: accounts[14],
|
||||
vault1_mint: accounts[15],
|
||||
remaining_accounts: accounts[16..].to_vec(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析关闭仓位指令事件
|
||||
fn parse_close_position_instruction(
|
||||
_data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if accounts.len() < 6 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumClmmClosePositionEvent {
|
||||
metadata,
|
||||
nft_owner: accounts[0],
|
||||
position_nft_mint: accounts[1],
|
||||
position_nft_account: accounts[2],
|
||||
personal_position: accounts[3],
|
||||
system_program: accounts[4],
|
||||
token_program: accounts[5],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析交易指令事件
|
||||
fn parse_swap_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 16 || accounts.len() < 10 {
|
||||
if data.len() < 33 || accounts.len() < 10 {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -108,7 +383,7 @@ impl RaydiumClmmEventParser {
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 16 || accounts.len() < 13 {
|
||||
if data.len() < 33 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,92 @@ pub struct RaydiumCpmmSwapEvent {
|
||||
|
||||
impl_unified_event!(RaydiumCpmmSwapEvent,);
|
||||
|
||||
/// 存款
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumCpmmDepositEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub lp_token_amount: u64,
|
||||
pub maximum_token0_amount: u64,
|
||||
pub maximum_token1_amount: u64,
|
||||
|
||||
pub owner: Pubkey,
|
||||
pub authority: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub owner_lp_token: Pubkey,
|
||||
pub token0_account: Pubkey,
|
||||
pub token1_account: Pubkey,
|
||||
pub token0_vault: Pubkey,
|
||||
pub token1_vault: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
pub lp_mint: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumCpmmDepositEvent,);
|
||||
|
||||
/// 初始化
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumCpmmInitializeEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub init_amount0: u64,
|
||||
pub init_amount1: u64,
|
||||
pub open_time: u64,
|
||||
|
||||
pub creator: Pubkey,
|
||||
pub amm_config: Pubkey,
|
||||
pub authority: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub token0_mint: Pubkey,
|
||||
pub token1_mint: Pubkey,
|
||||
pub lp_mint: Pubkey,
|
||||
pub creator_token0: Pubkey,
|
||||
pub creator_token1: Pubkey,
|
||||
pub creator_lp_token: Pubkey,
|
||||
pub token0_vault: Pubkey,
|
||||
pub token1_vault: Pubkey,
|
||||
pub create_pool_fee: Pubkey,
|
||||
pub observation_state: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub token0_program: Pubkey,
|
||||
pub token1_program: Pubkey,
|
||||
pub associated_token_program: Pubkey,
|
||||
pub system_program: Pubkey,
|
||||
pub rent: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumCpmmInitializeEvent,);
|
||||
|
||||
/// 提款
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RaydiumCpmmWithdrawEvent {
|
||||
pub metadata: EventMetadata,
|
||||
pub lp_token_amount: u64,
|
||||
pub minimum_token0_amount: u64,
|
||||
pub minimum_token1_amount: u64,
|
||||
|
||||
pub owner: Pubkey,
|
||||
pub authority: Pubkey,
|
||||
pub pool_state: Pubkey,
|
||||
pub owner_lp_token: Pubkey,
|
||||
pub token0_account: Pubkey,
|
||||
pub token1_account: Pubkey,
|
||||
pub token0_vault: Pubkey,
|
||||
pub token1_vault: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
pub token_program2022: Pubkey,
|
||||
pub vault0_mint: Pubkey,
|
||||
pub vault1_mint: Pubkey,
|
||||
pub lp_mint: Pubkey,
|
||||
pub memo_program: Pubkey,
|
||||
}
|
||||
impl_unified_event!(RaydiumCpmmWithdrawEvent,);
|
||||
|
||||
/// 事件鉴别器常量
|
||||
pub mod discriminators {
|
||||
// 指令鉴别器
|
||||
pub const SWAP_BASE_IN: &[u8] = &[143, 190, 90, 218, 196, 30, 51, 222];
|
||||
pub const SWAP_BASE_OUT: &[u8] = &[55, 217, 98, 86, 163, 74, 180, 173];
|
||||
pub const DEPOSIT: &[u8] = &[242, 35, 198, 137, 82, 225, 242, 182];
|
||||
pub const INITIALIZE: &[u8] = &[175, 175, 109, 31, 13, 152, 155, 237];
|
||||
pub const WITHDRAW: &[u8] = &[183, 18, 70, 156, 148, 109, 161, 34];
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ use solana_transaction_status::UiCompiledInstruction;
|
||||
use crate::streaming::event_parser::{
|
||||
common::{read_u64_le, EventMetadata, EventType, ProtocolType},
|
||||
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
|
||||
protocols::raydium_cpmm::{discriminators, RaydiumCpmmSwapEvent},
|
||||
protocols::raydium_cpmm::{
|
||||
discriminators, RaydiumCpmmDepositEvent, RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent,
|
||||
RaydiumCpmmWithdrawEvent,
|
||||
},
|
||||
};
|
||||
|
||||
/// Raydium CPMM程序ID
|
||||
@@ -35,7 +38,7 @@ impl RaydiumCpmmEventParser {
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP_BASE_IN,
|
||||
event_type: EventType::RaydiumCpmmSwapBaseInput,
|
||||
inner_instruction_parser: Self::parse_trade_inner_instruction,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_base_input_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
@@ -44,9 +47,36 @@ impl RaydiumCpmmEventParser {
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::SWAP_BASE_OUT,
|
||||
event_type: EventType::RaydiumCpmmSwapBaseOutput,
|
||||
inner_instruction_parser: Self::parse_trade_inner_instruction,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_swap_base_output_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CPMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumCpmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::DEPOSIT,
|
||||
event_type: EventType::RaydiumCpmmDeposit,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_deposit_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CPMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumCpmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::INITIALIZE,
|
||||
event_type: EventType::RaydiumCpmmInitialize,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_initialize_instruction,
|
||||
},
|
||||
GenericEventParseConfig {
|
||||
program_id: RAYDIUM_CPMM_PROGRAM_ID,
|
||||
protocol_type: ProtocolType::RaydiumCpmm,
|
||||
inner_instruction_discriminator: "",
|
||||
instruction_discriminator: discriminators::WITHDRAW,
|
||||
event_type: EventType::RaydiumCpmmWithdraw,
|
||||
inner_instruction_parser: Self::empty_parse,
|
||||
instruction_parser: Self::parse_withdraw_instruction,
|
||||
},
|
||||
];
|
||||
|
||||
let inner = GenericEventParser::new(vec![RAYDIUM_CPMM_PROGRAM_ID], configs);
|
||||
@@ -54,14 +84,114 @@ impl RaydiumCpmmEventParser {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// 解析交易事件
|
||||
fn parse_trade_inner_instruction(
|
||||
_data: &[u8],
|
||||
_metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
fn empty_parse(_data: &[u8], _metadata: EventMetadata) -> Option<Box<dyn UnifiedEvent>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// 解析提款指令事件
|
||||
fn parse_withdraw_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 24 || accounts.len() < 14 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumCpmmWithdrawEvent {
|
||||
metadata,
|
||||
lp_token_amount: read_u64_le(data, 0)?,
|
||||
minimum_token0_amount: read_u64_le(data, 8)?,
|
||||
minimum_token1_amount: read_u64_le(data, 16)?,
|
||||
owner: accounts[0],
|
||||
authority: accounts[1],
|
||||
pool_state: accounts[2],
|
||||
owner_lp_token: accounts[3],
|
||||
token0_account: accounts[4],
|
||||
token1_account: accounts[5],
|
||||
token0_vault: accounts[6],
|
||||
token1_vault: accounts[7],
|
||||
token_program: accounts[8],
|
||||
token_program2022: accounts[9],
|
||||
vault0_mint: accounts[10],
|
||||
vault1_mint: accounts[11],
|
||||
lp_mint: accounts[12],
|
||||
memo_program: accounts[13],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析初始化指令事件
|
||||
fn parse_initialize_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 24 || accounts.len() < 20 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumCpmmInitializeEvent {
|
||||
metadata,
|
||||
init_amount0: read_u64_le(data, 0)?,
|
||||
init_amount1: read_u64_le(data, 8)?,
|
||||
open_time: read_u64_le(data, 16)?,
|
||||
creator: accounts[0],
|
||||
amm_config: accounts[1],
|
||||
authority: accounts[2],
|
||||
pool_state: accounts[3],
|
||||
token0_mint: accounts[4],
|
||||
token1_mint: accounts[5],
|
||||
lp_mint: accounts[6],
|
||||
creator_token0: accounts[7],
|
||||
creator_token1: accounts[8],
|
||||
creator_lp_token: accounts[9],
|
||||
token0_vault: accounts[10],
|
||||
token1_vault: accounts[11],
|
||||
create_pool_fee: accounts[12],
|
||||
observation_state: accounts[13],
|
||||
token_program: accounts[14],
|
||||
token0_program: accounts[15],
|
||||
token1_program: accounts[16],
|
||||
associated_token_program: accounts[17],
|
||||
system_program: accounts[18],
|
||||
rent: accounts[19],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析存款指令事件
|
||||
fn parse_deposit_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
if data.len() < 24 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
let mut metadata = metadata;
|
||||
metadata.set_id(format!("{}-{}-{}", metadata.signature, accounts[0], accounts[1]));
|
||||
Some(Box::new(RaydiumCpmmDepositEvent {
|
||||
metadata,
|
||||
lp_token_amount: read_u64_le(data, 0)?,
|
||||
maximum_token0_amount: read_u64_le(data, 8)?,
|
||||
maximum_token1_amount: read_u64_le(data, 16)?,
|
||||
owner: accounts[0],
|
||||
authority: accounts[1],
|
||||
pool_state: accounts[2],
|
||||
owner_lp_token: accounts[3],
|
||||
token0_account: accounts[4],
|
||||
token1_account: accounts[5],
|
||||
token0_vault: accounts[6],
|
||||
token1_vault: accounts[7],
|
||||
token_program: accounts[8],
|
||||
token_program2022: accounts[9],
|
||||
vault0_mint: accounts[10],
|
||||
vault1_mint: accounts[11],
|
||||
lp_mint: accounts[12],
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析买入指令事件
|
||||
fn parse_swap_base_input_instruction(
|
||||
data: &[u8],
|
||||
|
||||
Reference in New Issue
Block a user