From 26dc717cd7cbbd25cbfffbe8febf185c6a40cc7a Mon Sep 17 00:00:00 2001 From: ysq Date: Wed, 10 Sep 2025 23:39:16 +0800 Subject: [PATCH] docs: Update Readme --- README.md | 1 + README_CN.md | 1 + .../mint_all_ata_account_listen_example.rs | 117 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 examples/mint_all_ata_account_listen_example.rs diff --git a/README.md b/README.md index 19a1667..00df866 100755 --- a/README.md +++ b/README.md @@ -189,6 +189,7 @@ let config = StreamClientConfig { | Token Balance Monitoring | `token_balance_listen_example` | Monitor specific token account balance changes | `cargo run --example token_balance_listen_example` | [examples/token_balance_listen_example.rs](examples/token_balance_listen_example.rs) | | Nonce Account Monitoring | `nonce_listen_example` | Track nonce account state changes | `cargo run --example nonce_listen_example` | [examples/nonce_listen_example.rs](examples/nonce_listen_example.rs) | | PumpSwap Pool Account Monitoring | `pumpswap_pool_account_listen_example` | Monitor PumpSwap pool accounts using memcmp filters | `cargo run --example pumpswap_pool_account_listen_example` | [examples/pumpswap_pool_account_listen_example.rs](examples/pumpswap_pool_account_listen_example.rs) | +| Mint ATA Account Monitoring | `mint_all_ata_account_listen_example` | Monitor all associated token accounts for specific mints using memcmp filters | `cargo run --example mint_all_ata_account_listen_example` | [examples/mint_all_ata_account_listen_example.rs](examples/mint_all_ata_account_listen_example.rs) | ### Event Filtering diff --git a/README_CN.md b/README_CN.md index 9c81429..939f3eb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -189,6 +189,7 @@ let config = StreamClientConfig { | 代币余额监控 | `token_balance_listen_example` | 监控特定代币账户余额变化 | `cargo run --example token_balance_listen_example` | [examples/token_balance_listen_example.rs](examples/token_balance_listen_example.rs) | | Nonce 账户监控 | `nonce_listen_example` | 跟踪 nonce 账户状态变化 | `cargo run --example nonce_listen_example` | [examples/nonce_listen_example.rs](examples/nonce_listen_example.rs) | | PumpSwap 池账户监控 | `pumpswap_pool_account_listen_example` | 使用 memcmp 过滤器监控 PumpSwap 池账户 | `cargo run --example pumpswap_pool_account_listen_example` | [examples/pumpswap_pool_account_listen_example.rs](examples/pumpswap_pool_account_listen_example.rs) | +| Mint 相关账户监控 | `mint_all_ata_account_listen_example` | 使用 memcmp 过滤器监控特定代币的所有关联代币账户 | `cargo run --example mint_all_ata_account_listen_example` | [examples/mint_all_ata_account_listen_example.rs](examples/mint_all_ata_account_listen_example.rs) | ### 事件过滤 diff --git a/examples/mint_all_ata_account_listen_example.rs b/examples/mint_all_ata_account_listen_example.rs new file mode 100644 index 0000000..b0fc11d --- /dev/null +++ b/examples/mint_all_ata_account_listen_example.rs @@ -0,0 +1,117 @@ +use std::str::FromStr; + +use solana_sdk::pubkey::Pubkey; +use solana_streamer_sdk::{ + match_event, + streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventType}, + core::account_event_parser::TokenAccountEvent, + UnifiedEvent, + }, + grpc::ClientConfig, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + YellowstoneGrpc, + }, +}; +use yellowstone_grpc_proto::geyser::{ + subscribe_request_filter_accounts_filter::Filter, + subscribe_request_filter_accounts_filter_memcmp::Data, SubscribeRequestFilterAccountsFilter, + SubscribeRequestFilterAccountsFilterMemcmp, +}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Starting Yellowstone gRPC Streamer..."); + test_grpc().await?; + Ok(()) +} + +async fn test_grpc() -> Result<(), Box> { + println!("Subscribing to Yellowstone gRPC events..."); + // Create low-latency configuration + let mut config: ClientConfig = ClientConfig::low_latency(); + // Enable performance monitoring, has performance overhead, disabled by default + config.enable_metrics = true; + let grpc = YellowstoneGrpc::new_with_config( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + config, + )?; + println!("GRPC client created successfully"); + let callback = create_event_callback(); + // Will try to parse corresponding protocol events from transactions + let protocols = vec![]; + println!("Protocols to monitor: {:?}", protocols); + // Filter accounts + let account_include = vec![]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = + TransactionFilter { account_include, account_exclude, account_required }; + + let pump = Pubkey::from_str("pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn").unwrap(); + let usdc = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap(); + + let all_pump_ata = AccountFilter { + account: vec![], + owner: vec![], + filters: vec![SubscribeRequestFilterAccountsFilter { + filter: Some(Filter::Memcmp(SubscribeRequestFilterAccountsFilterMemcmp { + offset: 0, + data: Some(Data::Bytes(pump.to_bytes().to_vec())), + })), + }], + }; + let all_usdc_ata = AccountFilter { + account: vec![], + owner: vec![], + filters: vec![SubscribeRequestFilterAccountsFilter { + filter: Some(Filter::Memcmp(SubscribeRequestFilterAccountsFilterMemcmp { + offset: 0, + data: Some(Data::Bytes(usdc.to_bytes().to_vec())), + })), + }], + }; + + // Event filtering + let event_type_filter = Some(EventTypeFilter { include: vec![EventType::TokenAccount] }); + + println!("Starting to listen for events, press Ctrl+C to stop..."); + println!("Starting subscription..."); + + grpc.subscribe_events_immediate( + protocols.clone(), + None, + vec![transaction_filter.clone()], + vec![all_pump_ata.clone(), all_usdc_ata.clone()], + event_type_filter.clone(), + None, + callback, + ) + .await?; + + // 支持 stop 方法,测试代码 - 异步1000秒之后停止 + let grpc_clone = grpc.clone(); + tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_secs(1000)).await; + grpc_clone.stop().await; + }); + + println!("Waiting for Ctrl+C to stop..."); + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + TokenAccountEvent => |e: TokenAccountEvent| { + println!("TokenAccount: {:?} amount: {:?}", e.pubkey, e.amount); + }, + }); + } +}