1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::Path;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Config {
8 pub server: ServerConfig,
9 pub grpc: GrpcConfig,
10 pub protocols: ProtocolsConfig,
11 pub events: EventsConfig,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ServerConfig {
16 pub host: String,
17 pub port: u16,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct GrpcConfig {
22 pub endpoint: String,
23 pub token: Option<String>,
24 pub enable_metrics: bool,
25 pub enable_tls: bool,
26 pub connection_timeout_ms: u64,
27 pub request_timeout_ms: u64,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ProtocolsConfig {
32 pub pumpfun: bool,
33 pub pumpswap: bool,
34 pub bonk: bool,
35 pub raydium_amm_v4: bool,
36 pub raydium_clmm: bool,
37 pub raydium_cpmm: bool,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct EventsConfig {
42 pub block_meta: bool,
43 pub bonk_trade: bool,
44 pub bonk_pool_create: bool,
45 pub bonk_migrate_amm: bool,
46 pub pumpfun_trade: bool,
47 pub pumpfun_create: bool,
48 pub pumpfun_complete: bool,
49 pub pumpfun_migrate: bool,
50 pub pumpswap_buy: bool,
51 pub pumpswap_sell: bool,
52 pub pumpswap_create_pool: bool,
53 pub pumpswap_pool_created: bool,
54 pub pumpswap_trade: bool,
55 pub pumpswap_liquidity_added: bool,
56 pub pumpswap_liquidity_removed: bool,
57 pub pumpswap_pool_updated: bool,
58 pub pumpswap_fees_claimed: bool,
59 pub raydium_cpmm_swap: bool,
60 pub raydium_cpmm_deposit: bool,
61 pub raydium_cpmm_withdraw: bool,
62 pub raydium_cpmm_initialize: bool,
63 pub raydium_clmm_swap: bool,
64 pub raydium_clmm_create_pool: bool,
65 pub raydium_clmm_open_position: bool,
66 pub raydium_clmm_close_position: bool,
67 pub raydium_clmm_increase_liquidity: bool,
68 pub raydium_clmm_decrease_liquidity: bool,
69 pub raydium_clmm_open_position_with_token_ext_nft: bool,
70 pub raydium_clmm_collect_fee: bool,
71 pub raydium_amm_v4_swap: bool,
72 pub raydium_amm_v4_deposit: bool,
73 pub raydium_amm_v4_withdraw: bool,
74 pub raydium_amm_v4_initialize2: bool,
75 pub raydium_amm_v4_withdraw_pnl: bool,
76 pub orca_whirlpool_swap: bool,
77 pub orca_whirlpool_liquidity_increased: bool,
78 pub orca_whirlpool_liquidity_decreased: bool,
79 pub orca_whirlpool_pool_initialized: bool,
80 pub meteora_pools_swap: bool,
81 pub meteora_pools_add_liquidity: bool,
82 pub meteora_pools_remove_liquidity: bool,
83 pub meteora_pools_bootstrap_liquidity: bool,
84 pub meteora_pools_pool_created: bool,
85 pub meteora_pools_set_pool_fees: bool,
86 pub meteora_damm_v2_swap: bool,
87 pub meteora_damm_v2_add_liquidity: bool,
88 pub meteora_damm_v2_remove_liquidity: bool,
89 pub meteora_damm_v2_initialize_pool: bool,
90 pub meteora_damm_v2_create_position: bool,
91 pub meteora_damm_v2_close_position: bool,
92 pub meteora_damm_v2_claim_position_fee: bool,
93 pub meteora_damm_v2_initialize_reward: bool,
94 pub meteora_damm_v2_fund_reward: bool,
95 pub meteora_damm_v2_claim_reward: bool,
96 pub token_account: bool,
97 pub nonce_account: bool,
98 pub token_info: bool,
99}
100
101impl Default for Config {
102 fn default() -> Self {
103 Self {
104 server: ServerConfig {
105 host: "127.0.0.1".to_string(),
106 port: 9001,
107 },
108 grpc: GrpcConfig {
109 endpoint: "https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
110 token: None,
111 enable_metrics: true,
112 enable_tls: true,
113 connection_timeout_ms: 10000,
114 request_timeout_ms: 30000,
115 },
116 protocols: ProtocolsConfig {
117 pumpfun: true,
118 pumpswap: false,
119 bonk: false,
120 raydium_amm_v4: false,
121 raydium_clmm: false,
122 raydium_cpmm: false,
123 },
124 events: EventsConfig {
125 block_meta: false,
126 bonk_trade: false,
127 bonk_pool_create: false,
128 bonk_migrate_amm: false,
129 pumpfun_trade: true,
130 pumpfun_create: true,
131 pumpfun_complete: false,
132 pumpfun_migrate: false,
133 pumpswap_buy: false,
134 pumpswap_sell: false,
135 pumpswap_create_pool: false,
136 pumpswap_pool_created: false,
137 pumpswap_trade: false,
138 pumpswap_liquidity_added: false,
139 pumpswap_liquidity_removed: false,
140 pumpswap_pool_updated: false,
141 pumpswap_fees_claimed: false,
142 raydium_cpmm_swap: false,
143 raydium_cpmm_deposit: false,
144 raydium_cpmm_withdraw: false,
145 raydium_cpmm_initialize: false,
146 raydium_clmm_swap: false,
147 raydium_clmm_create_pool: false,
148 raydium_clmm_open_position: false,
149 raydium_clmm_close_position: false,
150 raydium_clmm_increase_liquidity: false,
151 raydium_clmm_decrease_liquidity: false,
152 raydium_clmm_open_position_with_token_ext_nft: false,
153 raydium_clmm_collect_fee: false,
154 raydium_amm_v4_swap: false,
155 raydium_amm_v4_deposit: false,
156 raydium_amm_v4_withdraw: false,
157 raydium_amm_v4_initialize2: false,
158 raydium_amm_v4_withdraw_pnl: false,
159 orca_whirlpool_swap: false,
160 orca_whirlpool_liquidity_increased: false,
161 orca_whirlpool_liquidity_decreased: false,
162 orca_whirlpool_pool_initialized: false,
163 meteora_pools_swap: false,
164 meteora_pools_add_liquidity: false,
165 meteora_pools_remove_liquidity: false,
166 meteora_pools_bootstrap_liquidity: false,
167 meteora_pools_pool_created: false,
168 meteora_pools_set_pool_fees: false,
169 meteora_damm_v2_swap: false,
170 meteora_damm_v2_add_liquidity: false,
171 meteora_damm_v2_remove_liquidity: false,
172 meteora_damm_v2_initialize_pool: false,
173 meteora_damm_v2_create_position: false,
174 meteora_damm_v2_close_position: false,
175 meteora_damm_v2_claim_position_fee: false,
176 meteora_damm_v2_initialize_reward: false,
177 meteora_damm_v2_fund_reward: false,
178 meteora_damm_v2_claim_reward: false,
179 token_account: false,
180 nonce_account: false,
181 token_info: false,
182 },
183 }
184 }
185}
186
187impl Config {
188 pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
189 let content = fs::read_to_string(&path)
190 .with_context(|| format!("Failed to read config file: {:?}", path.as_ref()))?;
191
192 let config: Config = toml::from_str(&content)
193 .with_context(|| format!("Failed to parse config file: {:?}", path.as_ref()))?;
194
195 Ok(config)
196 }
197
198 pub fn load_or_default<P: AsRef<Path>>(path: P) -> Self {
199 match Self::load_from_file(&path) {
200 Ok(config) => {
201 tracing::info!("✅ Loaded config from: {:?}", path.as_ref());
202 config
203 }
204 Err(e) => {
205 tracing::warn!("⚠️ Failed to load config: {}, using default", e);
206 Self::default()
207 }
208 }
209 }
210
211 pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
212 let content = toml::to_string_pretty(self)
213 .context("Failed to serialize config to TOML")?;
214
215 fs::write(&path, content)
216 .with_context(|| format!("Failed to write config file: {:?}", path.as_ref()))?;
217
218 Ok(())
219 }
220
221 pub fn get_enabled_protocols(&self) -> Vec<sol_parser_sdk::grpc::Protocol> {
222 use sol_parser_sdk::grpc::Protocol;
223 let mut protocols = Vec::new();
224
225 if self.protocols.pumpfun {
226 protocols.push(Protocol::PumpFun);
227 }
228 if self.protocols.pumpswap {
229 protocols.push(Protocol::PumpSwap);
230 }
231 if self.protocols.bonk {
232 protocols.push(Protocol::Bonk);
233 }
234 if self.protocols.raydium_amm_v4 {
235 protocols.push(Protocol::RaydiumAmmV4);
236 }
237 if self.protocols.raydium_clmm {
238 protocols.push(Protocol::RaydiumClmm);
239 }
240 if self.protocols.raydium_cpmm {
241 protocols.push(Protocol::RaydiumCpmm);
242 }
243
244 protocols
245 }
246
247 pub fn get_enabled_event_types(&self) -> Vec<sol_parser_sdk::grpc::EventType> {
248 use sol_parser_sdk::grpc::EventType;
249 let mut event_types = Vec::new();
250
251 if self.events.block_meta { event_types.push(EventType::BlockMeta); }
252 if self.events.bonk_trade { event_types.push(EventType::BonkTrade); }
253 if self.events.bonk_pool_create { event_types.push(EventType::BonkPoolCreate); }
254 if self.events.bonk_migrate_amm { event_types.push(EventType::BonkMigrateAmm); }
255 if self.events.pumpfun_trade { event_types.push(EventType::PumpFunTrade); }
256 if self.events.pumpfun_create { event_types.push(EventType::PumpFunCreate); }
257 if self.events.pumpfun_complete { event_types.push(EventType::PumpFunComplete); }
258 if self.events.pumpfun_migrate { event_types.push(EventType::PumpFunMigrate); }
259 if self.events.pumpswap_buy { event_types.push(EventType::PumpSwapBuy); }
260 if self.events.pumpswap_sell { event_types.push(EventType::PumpSwapSell); }
261 if self.events.pumpswap_create_pool { event_types.push(EventType::PumpSwapCreatePool); }
262 if self.events.pumpswap_pool_created { event_types.push(EventType::PumpSwapPoolCreated); }
263 if self.events.pumpswap_trade { event_types.push(EventType::PumpSwapTrade); }
264 if self.events.pumpswap_liquidity_added { event_types.push(EventType::PumpSwapLiquidityAdded); }
265 if self.events.pumpswap_liquidity_removed { event_types.push(EventType::PumpSwapLiquidityRemoved); }
266 if self.events.pumpswap_pool_updated { event_types.push(EventType::PumpSwapPoolUpdated); }
267 if self.events.pumpswap_fees_claimed { event_types.push(EventType::PumpSwapFeesClaimed); }
268 if self.events.raydium_cpmm_swap { event_types.push(EventType::RaydiumCpmmSwap); }
269 if self.events.raydium_cpmm_deposit { event_types.push(EventType::RaydiumCpmmDeposit); }
270 if self.events.raydium_cpmm_withdraw { event_types.push(EventType::RaydiumCpmmWithdraw); }
271 if self.events.raydium_cpmm_initialize { event_types.push(EventType::RaydiumCpmmInitialize); }
272 if self.events.raydium_clmm_swap { event_types.push(EventType::RaydiumClmmSwap); }
273 if self.events.raydium_clmm_create_pool { event_types.push(EventType::RaydiumClmmCreatePool); }
274 if self.events.raydium_clmm_open_position { event_types.push(EventType::RaydiumClmmOpenPosition); }
275 if self.events.raydium_clmm_close_position { event_types.push(EventType::RaydiumClmmClosePosition); }
276 if self.events.raydium_clmm_increase_liquidity { event_types.push(EventType::RaydiumClmmIncreaseLiquidity); }
277 if self.events.raydium_clmm_decrease_liquidity { event_types.push(EventType::RaydiumClmmDecreaseLiquidity); }
278 if self.events.raydium_clmm_open_position_with_token_ext_nft { event_types.push(EventType::RaydiumClmmOpenPositionWithTokenExtNft); }
279 if self.events.raydium_clmm_collect_fee { event_types.push(EventType::RaydiumClmmCollectFee); }
280 if self.events.raydium_amm_v4_swap { event_types.push(EventType::RaydiumAmmV4Swap); }
281 if self.events.raydium_amm_v4_deposit { event_types.push(EventType::RaydiumAmmV4Deposit); }
282 if self.events.raydium_amm_v4_withdraw { event_types.push(EventType::RaydiumAmmV4Withdraw); }
283 if self.events.raydium_amm_v4_initialize2 { event_types.push(EventType::RaydiumAmmV4Initialize2); }
284 if self.events.raydium_amm_v4_withdraw_pnl { event_types.push(EventType::RaydiumAmmV4WithdrawPnl); }
285 if self.events.orca_whirlpool_swap { event_types.push(EventType::OrcaWhirlpoolSwap); }
286 if self.events.orca_whirlpool_liquidity_increased { event_types.push(EventType::OrcaWhirlpoolLiquidityIncreased); }
287 if self.events.orca_whirlpool_liquidity_decreased { event_types.push(EventType::OrcaWhirlpoolLiquidityDecreased); }
288 if self.events.orca_whirlpool_pool_initialized { event_types.push(EventType::OrcaWhirlpoolPoolInitialized); }
289 if self.events.meteora_pools_swap { event_types.push(EventType::MeteoraPoolsSwap); }
290 if self.events.meteora_pools_add_liquidity { event_types.push(EventType::MeteoraPoolsAddLiquidity); }
291 if self.events.meteora_pools_remove_liquidity { event_types.push(EventType::MeteoraPoolsRemoveLiquidity); }
292 if self.events.meteora_pools_bootstrap_liquidity { event_types.push(EventType::MeteoraPoolsBootstrapLiquidity); }
293 if self.events.meteora_pools_pool_created { event_types.push(EventType::MeteoraPoolsPoolCreated); }
294 if self.events.meteora_pools_set_pool_fees { event_types.push(EventType::MeteoraPoolsSetPoolFees); }
295 if self.events.meteora_damm_v2_swap { event_types.push(EventType::MeteoraDammV2Swap); }
296 if self.events.meteora_damm_v2_add_liquidity { event_types.push(EventType::MeteoraDammV2AddLiquidity); }
297 if self.events.meteora_damm_v2_remove_liquidity { event_types.push(EventType::MeteoraDammV2RemoveLiquidity); }
298 if self.events.meteora_damm_v2_initialize_pool { event_types.push(EventType::MeteoraDammV2InitializePool); }
299 if self.events.meteora_damm_v2_create_position { event_types.push(EventType::MeteoraDammV2CreatePosition); }
300 if self.events.meteora_damm_v2_close_position { event_types.push(EventType::MeteoraDammV2ClosePosition); }
301 if self.events.meteora_damm_v2_claim_position_fee { event_types.push(EventType::MeteoraDammV2ClaimPositionFee); }
302 if self.events.meteora_damm_v2_initialize_reward { event_types.push(EventType::MeteoraDammV2InitializeReward); }
303 if self.events.meteora_damm_v2_fund_reward { event_types.push(EventType::MeteoraDammV2FundReward); }
304 if self.events.meteora_damm_v2_claim_reward { event_types.push(EventType::MeteoraDammV2ClaimReward); }
305 if self.events.token_account { event_types.push(EventType::TokenAccount); }
306 if self.events.nonce_account { event_types.push(EventType::NonceAccount); }
307 if self.events.token_info { event_types.push(EventType::TokenInfo); }
308
309 event_types
310 }
311}