add tokens_subscription
This commit is contained in:
@@ -13,51 +13,11 @@ This repository is forked from [https://github.com/nhuxhr/pumpfun-rs](https://gi
|
|||||||
5. Add `logs_events` to define the event of the logs.
|
5. Add `logs_events` to define the event of the logs.
|
||||||
6. Add `logs_parser` to parse the logs.
|
6. Add `logs_parser` to parse the logs.
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Crates](#crates)
|
|
||||||
- [Features](#features)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [API Reference](#api-reference)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
|
|
||||||
## Crates
|
|
||||||
|
|
||||||
| Name | Description | Version |
|
|
||||||
| ------------------------------------- | ---------------------------------------------------------------------------------- | ------- |
|
|
||||||
| [`pumpfun`](./crates/pumpfun) | Main client library for interacting with the PumpFun program | 2.2.2 |
|
|
||||||
| [`pumpfun-cpi`](./crates/pumpfun-cpi) | CPI (Cross-Program Invocation) interfaces for integrating with the PumpFun program | 1.1.0 |
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Easy-to-use API**: Simplified interfaces for interacting with the PumpFun Solana program.
|
|
||||||
- **Cross-Program Invocation**: Seamless integration with other Solana programs.
|
|
||||||
- **Comprehensive Documentation**: Detailed guides and API references for all functionalities.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Add the following to your `Cargo.toml`:
|
Add the following to your `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mai3-pumpfun-sdk = "2.2.5"
|
mai3-pumpfun-sdk = "2.3.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
For detailed usage instructions, please refer to the documentation of each crate.
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
For detailed API documentation, run:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo doc --open
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate and open the API documentation in your default web browser.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
We welcome contributions to the PumpFun Rust SDK! Please see our [Contributing Guide](CONTRIBUTING.md) for more details on how to get started.
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mai3-pumpfun-sdk"
|
name = "mai3-pumpfun-sdk"
|
||||||
version = "2.2.5"
|
version = "2.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["William <william@mai3.io>"]
|
authors = ["William <william@mai3.io>"]
|
||||||
repository = "https://github.com/MiracleAI-Labs/pumpfun-sdk"
|
repository = "https://github.com/MiracleAI-Labs/pumpfun-sdk"
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DexInstruction {
|
||||||
|
CreateToken(CreateTokenInfo),
|
||||||
|
Trade(TradeInfo),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
// 添加新的数据结构
|
// 添加新的数据结构
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct CreateTokenInfo {
|
pub struct CreateTokenInfo {
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
use crate::instruction::logs_data::{CreateTokenInfo, TradeInfo};
|
use crate::instruction::logs_data::{CreateTokenInfo, TradeInfo};
|
||||||
use crate::instruction::logs_parser::{parse_create_token_data, parse_trade_data};
|
use crate::instruction::logs_parser::{parse_create_token_data, parse_trade_data};
|
||||||
use crate::error::ClientResult;
|
use crate::error::ClientResult;
|
||||||
|
use crate::instruction::logs_data::DexInstruction;
|
||||||
pub struct LogFilter;
|
pub struct LogFilter;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum DexInstruction {
|
|
||||||
CreateToken(CreateTokenInfo),
|
|
||||||
Trade(TradeInfo),
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogFilter {
|
impl LogFilter {
|
||||||
const PROGRAM_ID: &'static str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
const PROGRAM_ID: &'static str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
||||||
|
|
||||||
/// 解析交易日志并返回具体的指令类型和数据
|
/// Parse transaction logs and return instruction type and data
|
||||||
pub fn parse_instruction(logs: &[String]) -> ClientResult<Vec<DexInstruction>> {
|
pub fn parse_instruction(logs: &[String]) -> ClientResult<Vec<DexInstruction>> {
|
||||||
let mut current_instruction = None;
|
let mut current_instruction = None;
|
||||||
let mut program_data = String::new();
|
let mut program_data = String::new();
|
||||||
@@ -22,11 +15,10 @@ impl LogFilter {
|
|||||||
let mut last_data_len = 0;
|
let mut last_data_len = 0;
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
for log in logs {
|
for log in logs {
|
||||||
// println!("log: {:?}", log);
|
// Check program invocation
|
||||||
// 检查程序调用
|
|
||||||
if log.contains(&format!("Program {} invoke", Self::PROGRAM_ID)) {
|
if log.contains(&format!("Program {} invoke", Self::PROGRAM_ID)) {
|
||||||
invoke_depth += 1;
|
invoke_depth += 1;
|
||||||
if invoke_depth == 1 { // 只在顶层调用时重置状态
|
if invoke_depth == 1 { // Only reset state at top level call
|
||||||
current_instruction = None;
|
current_instruction = None;
|
||||||
program_data.clear();
|
program_data.clear();
|
||||||
last_data_len = 0;
|
last_data_len = 0;
|
||||||
@@ -34,12 +26,12 @@ impl LogFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果不在我们的程序中,跳过
|
// Skip if not in our program
|
||||||
if invoke_depth == 0 {
|
if invoke_depth == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 识别指令类型(只在顶层调用时)
|
// Identify instruction type (only at top level)
|
||||||
if invoke_depth == 1 && log.contains("Program log: Instruction:") {
|
if invoke_depth == 1 && log.contains("Program log: Instruction:") {
|
||||||
if log.contains("Create") {
|
if log.contains("Create") {
|
||||||
current_instruction = Some("create");
|
current_instruction = Some("create");
|
||||||
@@ -49,7 +41,7 @@ impl LogFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收集 Program data
|
// Collect Program data
|
||||||
if log.starts_with("Program data: ") {
|
if log.starts_with("Program data: ") {
|
||||||
let data = log.trim_start_matches("Program data: ");
|
let data = log.trim_start_matches("Program data: ");
|
||||||
if data.len() > last_data_len {
|
if data.len() > last_data_len {
|
||||||
@@ -58,10 +50,10 @@ impl LogFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查程序是否结束
|
// Check if program ends
|
||||||
if log.contains(&format!("Program {} success", Self::PROGRAM_ID)) {
|
if log.contains(&format!("Program {} success", Self::PROGRAM_ID)) {
|
||||||
invoke_depth -= 1;
|
invoke_depth -= 1;
|
||||||
if invoke_depth == 0 { // 只在顶层程序结束时处理数据
|
if invoke_depth == 0 { // Only process data when top level program ends
|
||||||
if let Some(instruction_type) = current_instruction {
|
if let Some(instruction_type) = current_instruction {
|
||||||
if !program_data.is_empty() {
|
if !program_data.is_empty() {
|
||||||
match instruction_type {
|
match instruction_type {
|
||||||
@@ -85,4 +77,4 @@ impl LogFilter {
|
|||||||
|
|
||||||
Ok(instructions)
|
Ok(instructions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,10 @@ use serde::{Serialize, Deserialize};
|
|||||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||||
|
|
||||||
use crate::error::{ClientError, ClientResult};
|
use crate::error::{ClientError, ClientResult};
|
||||||
use crate::instruction::{logs_data::*, logs_filters::{LogFilter, DexInstruction}};
|
use crate::instruction::{
|
||||||
|
logs_data::{DexInstruction, CreateTokenInfo, TradeInfo},
|
||||||
|
logs_filters::LogFilter
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn process_logs<F>(
|
pub async fn process_logs<F>(
|
||||||
signature: &str,
|
signature: &str,
|
||||||
@@ -20,16 +23,16 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加解析函数
|
// Add parsing function
|
||||||
pub fn parse_create_token_data(data: &str) -> ClientResult<CreateTokenInfo> {
|
pub fn parse_create_token_data(data: &str) -> ClientResult<CreateTokenInfo> {
|
||||||
// 首先进行 base64 解码
|
// First do base64 decoding
|
||||||
let decoded = BASE64.decode(data)
|
let decoded = BASE64.decode(data)
|
||||||
.map_err(|e| ClientError::Other(format!("Failed to decode base64: {}", e)))?;
|
.map_err(|e| ClientError::Other(format!("Failed to decode base64: {}", e)))?;
|
||||||
|
|
||||||
// 跳过前缀字节(如果有的话)
|
// Skip prefix bytes (if any)
|
||||||
let mut cursor = if decoded.len() > 8 { 8 } else { 0 };
|
let mut cursor = if decoded.len() > 8 { 8 } else { 0 };
|
||||||
|
|
||||||
// 读取名称长度和名称
|
// Read name length and name
|
||||||
if cursor + 4 > decoded.len() {
|
if cursor + 4 > decoded.len() {
|
||||||
return Err(ClientError::Other("Data too short for name length".to_string()));
|
return Err(ClientError::Other("Data too short for name length".to_string()));
|
||||||
}
|
}
|
||||||
@@ -43,7 +46,7 @@ pub fn parse_create_token_data(data: &str) -> ClientResult<CreateTokenInfo> {
|
|||||||
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in name: {}", e)))?;
|
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in name: {}", e)))?;
|
||||||
cursor += name_len;
|
cursor += name_len;
|
||||||
|
|
||||||
// 读取符号长度和符号
|
// Read symbol length and symbol
|
||||||
if cursor + 4 > decoded.len() {
|
if cursor + 4 > decoded.len() {
|
||||||
return Err(ClientError::Other("Data too short for symbol length".to_string()));
|
return Err(ClientError::Other("Data too short for symbol length".to_string()));
|
||||||
}
|
}
|
||||||
@@ -57,7 +60,7 @@ pub fn parse_create_token_data(data: &str) -> ClientResult<CreateTokenInfo> {
|
|||||||
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in symbol: {}", e)))?;
|
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in symbol: {}", e)))?;
|
||||||
cursor += symbol_len;
|
cursor += symbol_len;
|
||||||
|
|
||||||
// 读取 URI 长度和 URI
|
// Read URI length and URI
|
||||||
if cursor + 4 > decoded.len() {
|
if cursor + 4 > decoded.len() {
|
||||||
return Err(ClientError::Other("Data too short for URI length".to_string()));
|
return Err(ClientError::Other("Data too short for URI length".to_string()));
|
||||||
}
|
}
|
||||||
@@ -71,20 +74,20 @@ pub fn parse_create_token_data(data: &str) -> ClientResult<CreateTokenInfo> {
|
|||||||
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in uri: {}", e)))?;
|
.map_err(|e| ClientError::Other(format!("Invalid UTF-8 in uri: {}", e)))?;
|
||||||
cursor += uri_len;
|
cursor += uri_len;
|
||||||
|
|
||||||
// 确保还有足够的数据来读取公钥
|
// Make sure there is enough data to read public keys
|
||||||
if cursor + 32 * 3 > decoded.len() {
|
if cursor + 32 * 3 > decoded.len() {
|
||||||
return Err(ClientError::Other("Data too short for public keys".to_string()));
|
return Err(ClientError::Other("Data too short for public keys".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 Mint Public Key
|
// Parse Mint Public Key
|
||||||
let mint = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
let mint = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
||||||
cursor += 32;
|
cursor += 32;
|
||||||
|
|
||||||
// 解析 Bonding Curve Public Key
|
// Parse Bonding Curve Public Key
|
||||||
let bonding_curve = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
let bonding_curve = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
||||||
cursor += 32;
|
cursor += 32;
|
||||||
|
|
||||||
// 解析 User Public Key
|
// Parse User Public Key
|
||||||
let user = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
let user = bs58::encode(&decoded[cursor..cursor+32]).into_string();
|
||||||
|
|
||||||
Ok(CreateTokenInfo {
|
Ok(CreateTokenInfo {
|
||||||
@@ -113,7 +116,7 @@ pub fn parse_trade_data(data: &str) -> ClientResult<TradeInfo> {
|
|||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut cursor = 8; // 跳过前缀
|
let mut cursor = 8; // Skip prefix
|
||||||
|
|
||||||
// 1. Mint (32 bytes)
|
// 1. Mint (32 bytes)
|
||||||
let mint = bs58::encode(&decoded[cursor..cursor + 32]).into_string();
|
let mint = bs58::encode(&decoded[cursor..cursor + 32]).into_string();
|
||||||
|
|||||||
@@ -8,9 +8,14 @@ use anchor_client::solana_sdk::commitment_config::CommitmentConfig;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use futures::{future::BoxFuture, Future, StreamExt};
|
use futures::StreamExt;
|
||||||
|
use crate::instruction::{
|
||||||
|
logs_events::DexEvent,
|
||||||
|
logs_data::DexInstruction,
|
||||||
|
logs_filters::LogFilter
|
||||||
|
};
|
||||||
|
|
||||||
/// 订阅结果,包含订阅任务和取消订阅逻辑
|
/// Subscription handle containing task and unsubscribe logic
|
||||||
pub struct SubscriptionHandle {
|
pub struct SubscriptionHandle {
|
||||||
pub task: JoinHandle<()>,
|
pub task: JoinHandle<()>,
|
||||||
pub unsub_fn: Box<dyn Fn() + Send>,
|
pub unsub_fn: Box<dyn Fn() + Send>,
|
||||||
@@ -28,14 +33,14 @@ pub async fn create_pubsub_client(ws_url: &str) -> PubsubClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 启动订阅
|
/// 启动订阅
|
||||||
pub async fn start_subscription<F>(
|
pub async fn tokens_subscription<F>(
|
||||||
ws_url: &str,
|
ws_url: &str,
|
||||||
program_address: &str,
|
program_address: &str,
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
subscription_callback: F,
|
callback: F,
|
||||||
) -> Result<SubscriptionHandle, Box<dyn std::error::Error>>
|
) -> Result<SubscriptionHandle, Box<dyn std::error::Error>>
|
||||||
where
|
where
|
||||||
F: Fn(&str, Vec<String>) + Send + Sync + 'static,
|
F: Fn(DexEvent) + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let logs_filter = RpcTransactionLogsFilter::Mentions(vec![program_address.to_string()]);
|
let logs_filter = RpcTransactionLogsFilter::Mentions(vec![program_address.to_string()]);
|
||||||
|
|
||||||
@@ -43,17 +48,17 @@ where
|
|||||||
commitment: Some(commitment),
|
commitment: Some(commitment),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建 PubsubClient
|
// Create PubsubClient
|
||||||
let sub_client = Arc::new(PubsubClient::new(ws_url).await.unwrap());
|
let sub_client = Arc::new(PubsubClient::new(ws_url).await.unwrap());
|
||||||
|
|
||||||
let sub_client_clone = Arc::clone(&sub_client);
|
let sub_client_clone = Arc::clone(&sub_client);
|
||||||
|
|
||||||
// 创建一个通道用于取消订阅
|
// Create channel for unsubscribe
|
||||||
let (unsub_tx, mut unsub_rx) = mpsc::channel(1);
|
let (unsub_tx, _) = mpsc::channel(1);
|
||||||
|
|
||||||
// 启动订阅任务
|
// Start subscription task
|
||||||
let task = tokio::spawn(async move {
|
let task = tokio::spawn(async move {
|
||||||
let (mut stream, unsub) = sub_client_clone.logs_subscribe(logs_filter, logs_config).await.unwrap();
|
let (mut stream, _) = sub_client_clone.logs_subscribe(logs_filter, logs_config).await.unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let msg = stream.next().await;
|
let msg = stream.next().await;
|
||||||
@@ -62,22 +67,32 @@ where
|
|||||||
if let Some(_err) = msg.value.err {
|
if let Some(_err) = msg.value.err {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription_callback(&msg.value.signature.as_str(), msg.value.logs);
|
let instructions = LogFilter::parse_instruction(&msg.value.logs).unwrap();
|
||||||
|
for instruction in instructions {
|
||||||
|
match instruction {
|
||||||
|
DexInstruction::CreateToken(token_info) => {
|
||||||
|
callback(DexEvent::NewToken(token_info));
|
||||||
|
}
|
||||||
|
DexInstruction::Trade(trade_info) => {
|
||||||
|
callback(DexEvent::NewTrade(trade_info));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("Token subscription stream ended");
|
println!("Token subscription stream ended");
|
||||||
// break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返回订阅句柄和取消逻辑
|
// Return subscription handle and unsubscribe logic
|
||||||
Ok(SubscriptionHandle {
|
Ok(SubscriptionHandle {
|
||||||
task,
|
task,
|
||||||
unsub_fn: Box::new(move || {
|
unsub_fn: Box::new(move || {
|
||||||
let _ = unsub_tx.try_send(()); // 发送取消信号
|
let _ = unsub_tx.try_send(());
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-25
@@ -493,7 +493,7 @@ impl PumpFun {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::instruction::logs_subscribe::{start_subscription, stop_subscription, SubscriptionHandle};
|
// use crate::instruction::logs_subscribe::{start_subscription, stop_subscription, SubscriptionHandle};
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -522,33 +522,33 @@ mod tests {
|
|||||||
assert!(metadata_pda != Pubkey::default());
|
assert!(metadata_pda != Pubkey::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
// #[tokio::test]
|
||||||
async fn test_logs_subscription() {
|
// async fn test_logs_subscription() {
|
||||||
let ws_url = "wss://api.mainnet-beta.solana.com";
|
// let ws_url = "wss://api.mainnet-beta.solana.com";
|
||||||
let program_address = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
// let program_address = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
||||||
let commitment = CommitmentConfig::confirmed();
|
// let commitment = CommitmentConfig::confirmed();
|
||||||
|
|
||||||
let process_logs_callback = |signature: String, logs: Vec<String>| {
|
// let process_logs_callback = |signature: String, logs: Vec<String>| {
|
||||||
println!("Signature: {}", signature);
|
// println!("Signature: {}", signature);
|
||||||
for log in logs {
|
// for log in logs {
|
||||||
println!("Log: {}", log);
|
// println!("Log: {}", log);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
let subscription = start_subscription(
|
// let subscription = start_subscription(
|
||||||
ws_url,
|
// ws_url,
|
||||||
program_address,
|
// program_address,
|
||||||
commitment,
|
// commitment,
|
||||||
process_logs_callback,
|
// process_logs_callback,
|
||||||
)
|
// )
|
||||||
.await.unwrap();
|
// .await.unwrap();
|
||||||
|
|
||||||
// 模拟运行5秒后关闭订阅
|
// // 模拟运行5秒后关闭订阅
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
// tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||||
|
|
||||||
(subscription.unsub_fn)(); // 调用取消逻辑
|
// (subscription.unsub_fn)(); // 调用取消逻辑
|
||||||
subscription.task.await.unwrap();
|
// subscription.task.await.unwrap();
|
||||||
|
|
||||||
println!("Subscription closed.");
|
// println!("Subscription closed.");
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-38
@@ -1,26 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use mai3_pumpfun_sdk::instruction::logs_subscribe::tokens_subscription;
|
||||||
use anchor_client::solana_client::nonblocking::rpc_client::RpcClient;
|
use mai3_pumpfun_sdk::instruction::logs_events::DexEvent;
|
||||||
use anchor_spl::associated_token::get_associated_token_address;
|
use mai3_pumpfun_sdk::instruction::logs_subscribe::stop_subscription;
|
||||||
use anchor_client::{
|
use anchor_client::solana_sdk::commitment_config::CommitmentConfig;
|
||||||
solana_sdk::{
|
|
||||||
native_token::LAMPORTS_PER_SOL,
|
|
||||||
instruction::Instruction,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::Keypair,
|
|
||||||
signer::Signer,
|
|
||||||
transaction::Transaction,
|
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
compute_budget::ComputeBudgetInstruction,
|
|
||||||
system_program::ID as SYSTEM_PROGRAM_ID,
|
|
||||||
sysvar::rent::ID as RENT_ID,
|
|
||||||
instruction::AccountMeta,
|
|
||||||
program_error::ProgramError,
|
|
||||||
program_pack::Pack,
|
|
||||||
},
|
|
||||||
Cluster,
|
|
||||||
};
|
|
||||||
use mai3_pumpfun_sdk::constants::accounts::PUMPFUN;
|
|
||||||
use mai3_pumpfun_sdk::instruction::logs_subscribe;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tokio::signal;
|
use tokio::signal;
|
||||||
|
|
||||||
@@ -35,30 +17,41 @@ pub async fn start_token_subscription() -> Result<(), Box<dyn std::error::Error>
|
|||||||
println!("Starting token subscription\n");
|
println!("Starting token subscription\n");
|
||||||
|
|
||||||
let ws_url = "wss://api.mainnet-beta.solana.com";
|
let ws_url = "wss://api.mainnet-beta.solana.com";
|
||||||
|
|
||||||
|
// Program address
|
||||||
|
let program_address = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
||||||
|
|
||||||
|
// Set commitment
|
||||||
let commitment = CommitmentConfig::confirmed();
|
let commitment = CommitmentConfig::confirmed();
|
||||||
|
|
||||||
println!("program_address: {}", PUMPFUN);
|
// Define callback function
|
||||||
|
let callback = |event: DexEvent| {
|
||||||
let subscription_callback = |signature: &str, logs: Vec<String>| {
|
match event {
|
||||||
println!("=======Signature: {}=============", signature);
|
DexEvent::NewToken(token_info) => {
|
||||||
for log in logs {
|
println!("Received new token event: {:?}", token_info);
|
||||||
println!("============Log: {}============", log);
|
},
|
||||||
|
DexEvent::NewTrade(trade_info) => {
|
||||||
|
println!("Received new trade event: {:?}", trade_info);
|
||||||
|
},
|
||||||
|
DexEvent::Error(err) => {
|
||||||
|
println!("Received error: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let subscription = logs_subscribe::start_subscription(
|
// Start subscription
|
||||||
|
let subscription = tokens_subscription(
|
||||||
ws_url,
|
ws_url,
|
||||||
&PUMPFUN.to_string(),
|
program_address,
|
||||||
commitment,
|
commitment,
|
||||||
subscription_callback,
|
callback
|
||||||
).await.unwrap();
|
).await.unwrap();
|
||||||
|
|
||||||
subscription.task.await.unwrap();
|
// Wait for a while to receive events
|
||||||
// stop_subscription(subscription);
|
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
|
||||||
|
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(1000)).await;
|
// Stop subscription
|
||||||
|
stop_subscription(subscription).await;
|
||||||
println!("Subscription closed.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user