1
0
mirror of https://github.com/mortdeus/legacy-cc.git synced 2026-10-10 12:52:27 +00:00
This commit is contained in:
cryptoking1106
2025-09-18 18:12:50 -03:00
parent 936e12cfc7
commit 086ec82844
141 changed files with 67491 additions and 6883 deletions

View File

@@ -0,0 +1 @@
**/target

View File

@@ -0,0 +1,80 @@
[package]
name = "grpc-raydium-pool-monitoring-rust"
version = "3.0.0"
authors = ["Mephisto"]
edition = "2021"
license = "Apache-2.0"
[dependencies]
anyhow = "1.0.62"
backoff = { version = "0.4.0", features = ["tokio"] }
bincode = "1.3.3"
borsh = { version = "1.5.3" }
bs58 = "0.5.1"
chrono = "0.4.39"
clap = { version = "4.3.0", features = ["derive"] }
env_logger = "0.11.3"
futures = "0.3.24"
hex = "0.4.3"
log = "0.4.17"
maplit = "1.0.2"
serde_json = "1.0.135"
solana-sdk = "2.1.7"
solana-entry = "=2.1.7"
solana-transaction-status = "2.1.7"
solana-program = "2.1.7"
solana-account-decoder-client-types = "2.1.7"
tokio = { version = "1.21.2", features = ["full", "rt-multi-thread", "fs"] }
openssl = { version = "0.10", features = ["vendored"] }
reqwest = { version = "0.11.27", features = ["json", "socks", "native-tls", "blocking"] }
tonic = "0.12.1"
yellowstone-grpc-client = "4.0.0"
yellowstone-grpc-proto = { version = "4.0.0", default-features = false ,features = ["plugin"] }
yellowstone-vixen-core = { git = "https://github.com/rpcpool/yellowstone-vixen" }
yellowstone-vixen-parser = { git = "https://github.com/rpcpool/yellowstone-vixen", features = ["raydium"] }
indicatif = "0.17.9"
pump_interface = { path = "./parsers/pump_interface", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_with = "3.0"
postgres = "0.19"
solana-client = "2.1.7"
spl-associated-token-account = { version = "4.0.0", features = ["no-entrypoint"] }
dotenv = "0.15"
lazy_static = "1.5.0"
shared_state = { path = "./shared_state" }
sdk = "0.1.0"
once_cell = "1.21.3"
uuid = { version = "1", features = ["v4"] }
solana-account-decoder = "=2.1.7"
spl-token = { version = "7.0.0", features = ["no-entrypoint"] }
spl-token-2022 = { version = "7.0.0", features = ["no-entrypoint"] }
spl-token-client = "=0.14.0"
jito-json-rpc-client = { path = "./json-rpc-client", package = "jito-block-engine-json-rpc-client" }
anchor-lang = "=0.31.0"
bytemuck = "1.21.0"
rand = "0.8.5"
tracing = "0.1.40"
futures-util = "0.3.30"
tokio-tungstenite = { version = "0.26.1", features = ["native-tls"] }
tokio-stream = "0.1.17"
borsh-derive = "1.5.3"
solana-transaction-status-client-types = "=2.1.7"
url = "2.3.1"
base64 = "0.22.1"
jito-protos = { path = "/home/ubuntu/shreds_sniper/shredstream-proxy/jito_protos" }
[workspace]
members = [
"json-rpc-client",
"shared_state",
]
[build-dependencies]
tonic-build = "0.10"
protobuf-src = "1.0"

View File

@@ -0,0 +1,29 @@
version: '3.9'
services:
rustapp:
container_name: rustapp
image: francescoxx/rustapp:1.0.0
build:
context: .
dockerfile: Dockerfile
args:
DATABASE_URL: postgres://postgres:postgres@db:5432/postgres
ports:
- '8080:8080'
depends_on:
- db
db:
container_name: db
image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: {}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,845 @@
{
"version": "3.3.0",
"name": "spl_token",
"instructions": [
{
"name": "initializeMint",
"accounts": [
{
"name": "mint",
"isMut": true,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "decimals",
"type": "u8"
},
{
"name": "mintAuthority",
"type": "publicKey"
},
{
"name": "freezeAuthority",
"type": {
"option": "publicKey"
}
}
]
},
{
"name": "initializeAccount",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": []
},
{
"name": "initializeMultisig",
"accounts": [
{
"name": "multisig",
"isMut": true,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "m",
"type": "u8"
}
]
},
{
"name": "transfer",
"accounts": [
{
"name": "source",
"isMut": true,
"isSigner": false
},
{
"name": "destination",
"isMut": true,
"isSigner": false
},
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "approve",
"accounts": [
{
"name": "source",
"isMut": true,
"isSigner": false
},
{
"name": "delegate",
"isMut": false,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "revoke",
"accounts": [
{
"name": "source",
"isMut": true,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "setAuthority",
"accounts": [
{
"name": "owned",
"isMut": true,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
},
{
"name": "signer",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "authorityType",
"type": {
"defined": "AuthorityType"
}
},
{
"name": "newAuthority",
"type": {
"option": "publicKey"
}
}
]
},
{
"name": "mintTo",
"accounts": [
{
"name": "mint",
"isMut": true,
"isSigner": false
},
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "burn",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": true,
"isSigner": false
},
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "closeAccount",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "destination",
"isMut": true,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "freezeAccount",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "thawAccount",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "transferChecked",
"accounts": [
{
"name": "source",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "destination",
"isMut": true,
"isSigner": false
},
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
},
{
"name": "decimals",
"type": "u8"
}
]
},
{
"name": "approveChecked",
"accounts": [
{
"name": "source",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "delegate",
"isMut": false,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
},
{
"name": "decimals",
"type": "u8"
}
]
},
{
"name": "mintToChecked",
"accounts": [
{
"name": "mint",
"isMut": true,
"isSigner": false
},
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
},
{
"name": "decimals",
"type": "u8"
}
]
},
{
"name": "burnChecked",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": true,
"isSigner": false
},
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "amount",
"type": "u64"
},
{
"name": "decimals",
"type": "u8"
}
]
},
{
"name": "initializeAccount2",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "owner",
"type": "publicKey"
}
]
},
{
"name": "syncNative",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
}
],
"args": []
},
{
"name": "initializeAccount3",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
},
{
"name": "mint",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "owner",
"type": "publicKey"
}
]
},
{
"name": "initializeMultisig2",
"accounts": [
{
"name": "multisig",
"isMut": true,
"isSigner": false
},
{
"name": "signer",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "m",
"type": "u8"
}
]
},
{
"name": "initializeMint2",
"accounts": [
{
"name": "mint",
"isMut": true,
"isSigner": false
}
],
"args": [
{
"name": "decimals",
"type": "u8"
},
{
"name": "mintAuthority",
"type": "publicKey"
},
{
"name": "freezeAuthority",
"type": {
"option": "publicKey"
}
}
]
},
{
"name": "getAccountDataSize",
"accounts": [
{
"name": "mint",
"isMut": false,
"isSigner": false
}
],
"args": []
},
{
"name": "initializeImmutableOwner",
"accounts": [
{
"name": "account",
"isMut": true,
"isSigner": false
}
],
"args": []
},
{
"name": "amountToUiAmount",
"accounts": [
{
"name": "mint",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "uiAmountToAmount",
"accounts": [
{
"name": "mint",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "uiAmount",
"type": "u64"
}
]
}
],
"accounts": [
{
"name": "Mint",
"type": {
"kind": "struct",
"fields": [
{
"name": "mintAuthority",
"type": {
"option": "publicKey"
}
},
{
"name": "supply",
"type": "u64"
},
{
"name": "decimals",
"type": "u8"
},
{
"name": "isInitialized",
"type": "bool"
},
{
"name": "freezeAuthority",
"type": {
"option": "publicKey"
}
}
]
}
},
{
"name": "Account",
"type": {
"kind": "struct",
"fields": [
{
"name": "mint",
"type": "publicKey"
},
{
"name": "owner",
"type": "publicKey"
},
{
"name": "amount",
"type": "u64"
},
{
"name": "delegate",
"type": {
"option": "publicKey"
}
},
{
"name": "state",
"type": {
"defined": "AccountState"
}
},
{
"name": "isNative",
"type": {
"option": "u64"
}
},
{
"name": "delegatedAmount",
"type": "u64"
},
{
"name": "closeAuthority",
"type": {
"option": "publicKey"
}
}
]
}
},
{
"name": "Multisig",
"type": {
"kind": "struct",
"fields": [
{
"name": "m",
"type": "u8"
},
{
"name": "n",
"type": "u8"
},
{
"name": "isInitialized",
"type": "bool"
},
{
"name": "signers",
"type": {
"array": [
"publicKey",
11
]
}
}
]
}
}
],
"types": [
{
"name": "AccountState",
"type": {
"kind": "enum",
"variants": [
{
"name": "Uninitialized"
},
{
"name": "Initialized"
},
{
"name": "Frozen"
}
]
}
},
{
"name": "AuthorityType",
"type": {
"kind": "enum",
"variants": [
{
"name": "MintTokens"
},
{
"name": "FreezeAccount"
},
{
"name": "AccountOwner"
},
{
"name": "CloseAccount"
}
]
}
}
],
"errors": [
{
"code": 0,
"name": "NotRentExempt",
"msg": "Lamport balance below rent-exempt threshold"
},
{
"code": 1,
"name": "InsufficientFunds",
"msg": "Insufficient funds"
},
{
"code": 2,
"name": "InvalidMint",
"msg": "Invalid Mint"
},
{
"code": 3,
"name": "MintMismatch",
"msg": "Account not associated with this Mint"
},
{
"code": 4,
"name": "OwnerMismatch",
"msg": "Owner does not match"
},
{
"code": 5,
"name": "FixedSupply",
"msg": "Fixed supply"
},
{
"code": 6,
"name": "AlreadyInUse",
"msg": "Already in use"
},
{
"code": 7,
"name": "InvalidNumberOfProvidedSigners",
"msg": "Invalid number of provided signers"
},
{
"code": 8,
"name": "InvalidNumberOfRequiredSigners",
"msg": "Invalid number of required signers"
},
{
"code": 9,
"name": "UninitializedState",
"msg": "State is unititialized"
},
{
"code": 10,
"name": "NativeNotSupported",
"msg": "Instruction does not support native tokens"
},
{
"code": 11,
"name": "NonNativeHasBalance",
"msg": "Non-native account can only be closed if its balance is zero"
},
{
"code": 12,
"name": "InvalidInstruction",
"msg": "Invalid instruction"
},
{
"code": 13,
"name": "InvalidState",
"msg": "State is invalid for requested operation"
},
{
"code": 14,
"name": "Overflow",
"msg": "Operation overflowed"
},
{
"code": 15,
"name": "AuthorityTypeNotSupported",
"msg": "Account does not support specified authority type"
},
{
"code": 16,
"name": "MintCannotFreeze",
"msg": "This token mint cannot freeze accounts"
},
{
"code": 17,
"name": "AccountFrozen",
"msg": "Account is frozen"
},
{
"code": 18,
"name": "MintDecimalsMismatch",
"msg": "The provided decimals value different from the Mint decimals"
},
{
"code": 19,
"name": "NonNativeNotSupported",
"msg": "Instruction does not support non-native tokens"
}
],
"metadata": {
"address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
[package]
name = "jito-block-engine-json-rpc-client"
version = "0.1.0"
edition = "2021"
description = "A sample rpc client to generate and send requests to jito block engine server"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = "0.1.68"
bincode = "1.3.3"
log = "0.4.17"
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0.189", features = ["derive"] }
serde_json = "1.0.107"
solana-rpc-client = "=2.1.7"
solana-rpc-client-api = "=2.1.7"
solana-sdk = "=2.1.7"
solana-transaction-status = "=2.1.7"
thiserror = "1.0.40"
tokio = { version = "1", features = ["rt-multi-thread"] }
[dev-dependencies]
solana-program = "=2.1.7"

View File

@@ -0,0 +1,3 @@
# json-rpc-client
This repository contains code to communicate with Jito's Block-Engine.

View File

@@ -0,0 +1,5 @@
edition = "2021" # required by rust-analyzer
imports_granularity="Crate"
format_code_in_doc_comments = true
error_on_unformatted = true
group_imports = "StdExternalCrate"

View File

@@ -0,0 +1,125 @@
pub use reqwest;
use solana_rpc_client_api::{client_error::ErrorKind, request};
use solana_sdk::{
signature::SignerError, transaction::TransactionError, transport::TransportError,
};
use thiserror::Error as ThisError;
use crate::jsonrpc_client::request::RpcRequest;
#[derive(ThisError, Debug)]
#[error("{kind}")]
pub struct Error {
pub request: Option<RpcRequest>,
#[source]
pub kind: ErrorKind,
}
impl Error {
pub fn new_with_request(kind: ErrorKind, request: RpcRequest) -> Self {
Self {
request: Some(request),
kind,
}
}
pub fn into_with_request(self, request: RpcRequest) -> Self {
Self {
request: Some(request),
..self
}
}
pub fn request(&self) -> Option<&RpcRequest> {
self.request.as_ref()
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn get_transaction_error(&self) -> Option<TransactionError> {
self.kind.get_transaction_error()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self {
request: None,
kind,
}
}
}
impl From<TransportError> for Error {
fn from(err: TransportError) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<Error> for TransportError {
fn from(client_error: Error) -> Self {
client_error.kind.into()
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<request::RpcError> for Error {
fn from(err: request::RpcError) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<serde_json::error::Error> for Error {
fn from(err: serde_json::error::Error) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<SignerError> for Error {
fn from(err: SignerError) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Self {
Self {
request: None,
kind: err.into(),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -0,0 +1,218 @@
use std::{
sync::{
atomic::{AtomicU64, Ordering},
Arc, RwLock,
},
time::{Duration, Instant},
};
use async_trait::async_trait;
use log::debug;
use reqwest::{
self,
header::{CONTENT_TYPE, RETRY_AFTER},
StatusCode,
};
use solana_rpc_client_api::{
custom_error,
error_object::RpcErrorObject,
request::{RpcError, RpcResponseErrorData},
response::RpcSimulateTransactionResult,
};
use tokio::time::sleep;
use crate::jsonrpc_client::{client_error::Result, request::RpcRequest, rpc_sender::RpcSender};
pub struct HttpSender {
client: Arc<reqwest::Client>,
url: String,
request_id: AtomicU64,
stats: RwLock<solana_rpc_client::rpc_sender::RpcTransportStats>,
}
/// Nonblocking [`RpcSender`] over HTTP.
impl HttpSender {
/// Create an HTTP RPC sender.
///
/// The URL is an HTTP URL, usually for port 8899, as in
/// "http://localhost:8899". The sender has a default timeout of 30 seconds.
pub fn new<U: ToString>(url: U) -> Self {
Self::new_with_timeout(url, Duration::from_secs(30))
}
/// Create an HTTP RPC sender.
///
/// The URL is an HTTP URL, usually for port 8899.
pub fn new_with_timeout<U: ToString>(url: U, timeout: Duration) -> Self {
let client = Arc::new(
reqwest::Client::builder()
.timeout(timeout)
.pool_idle_timeout(timeout)
.build()
.expect("build rpc client"),
);
Self {
client,
url: url.to_string(),
request_id: AtomicU64::new(0),
stats: RwLock::new(solana_rpc_client::rpc_sender::RpcTransportStats::default()),
}
}
}
struct StatsUpdater<'a> {
stats: &'a RwLock<solana_rpc_client::rpc_sender::RpcTransportStats>,
request_start_time: Instant,
rate_limited_time: Duration,
}
impl<'a> StatsUpdater<'a> {
fn new(stats: &'a RwLock<solana_rpc_client::rpc_sender::RpcTransportStats>) -> Self {
Self {
stats,
request_start_time: Instant::now(),
rate_limited_time: Duration::default(),
}
}
fn add_rate_limited_time(&mut self, duration: Duration) {
self.rate_limited_time += duration;
}
}
impl Drop for StatsUpdater<'_> {
fn drop(&mut self) {
let mut stats = self.stats.write().unwrap();
stats.request_count += 1;
stats.elapsed_time += Instant::now().duration_since(self.request_start_time);
stats.rate_limited_time += self.rate_limited_time;
}
}
#[async_trait]
impl RpcSender for HttpSender {
fn get_transport_stats(&self) -> solana_rpc_client::rpc_sender::RpcTransportStats {
self.stats.read().unwrap().clone()
}
async fn send(
&self,
request: RpcRequest,
params: serde_json::Value,
) -> Result<serde_json::Value> {
let mut stats_updater = StatsUpdater::new(&self.stats);
let request_id = self.request_id.fetch_add(1, Ordering::Relaxed);
let request_json = request.build_request_json(request_id, params).to_string();
let mut too_many_requests_retries = 5;
loop {
let response = {
let client = self.client.clone();
let request_json = request_json.clone();
client
.post(&self.url)
.header(CONTENT_TYPE, "application/json")
.body(request_json)
.send()
.await
}?;
if !response.status().is_success() {
if response.status() == StatusCode::TOO_MANY_REQUESTS
&& too_many_requests_retries > 0
{
let mut duration = Duration::from_millis(500);
if let Some(retry_after) = response.headers().get(RETRY_AFTER) {
if let Ok(retry_after) = retry_after.to_str() {
if let Ok(retry_after) = retry_after.parse::<u64>() {
if retry_after < 120 {
duration = Duration::from_secs(retry_after);
}
}
}
}
too_many_requests_retries -= 1;
debug!(
"Too many requests: server responded with {:?}, {} retries left, pausing for {:?}",
response, too_many_requests_retries, duration
);
sleep(duration).await;
stats_updater.add_rate_limited_time(duration);
continue;
}
return Err(response.error_for_status().unwrap_err().into());
}
let mut json = response.json::<serde_json::Value>().await?;
if json["error"].is_object() {
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone()) {
Ok(rpc_error_object) => {
let data = match rpc_error_object.code {
solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => {
match serde_json::from_value::<RpcSimulateTransactionResult>(json["error"]["data"].clone()) {
Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data),
Err(err) => {
debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err);
RpcResponseErrorData::Empty
}
}
},
custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => {
match serde_json::from_value::<custom_error::NodeUnhealthyErrorData>(json["error"]["data"].clone()) {
Ok(custom_error::NodeUnhealthyErrorData {num_slots_behind}) => RpcResponseErrorData::NodeUnhealthy {num_slots_behind},
Err(_err) => {
RpcResponseErrorData::Empty
}
}
},
_ => RpcResponseErrorData::Empty
};
Err(RpcError::RpcResponseError {
code: rpc_error_object.code,
message: rpc_error_object.message,
data,
}
.into())
}
Err(err) => Err(RpcError::RpcRequestError(format!(
"Failed to deserialize RPC error response: {} [{}]",
serde_json::to_string(&json["error"]).unwrap(),
err
))
.into()),
};
}
return Ok(json["result"].take());
}
}
fn url(&self) -> String {
self.url.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn http_sender_on_tokio_multi_thread() {
let http_sender = HttpSender::new("http://localhost:1234".to_string());
let _ = http_sender
.send(RpcRequest::GetTipAccounts, serde_json::Value::Null)
.await;
}
#[tokio::test(flavor = "current_thread")]
async fn http_sender_on_tokio_current_thread() {
let http_sender = HttpSender::new("http://localhost:1234".to_string());
let _ = http_sender
.send(RpcRequest::GetTipAccounts, serde_json::Value::Null)
.await;
}
}

View File

@@ -0,0 +1,5 @@
pub mod client_error;
pub mod http_sender;
pub mod request;
pub mod rpc_client;
pub mod rpc_sender;

View File

@@ -0,0 +1,55 @@
use std::fmt;
use serde_json::{json, Value};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum RpcRequest {
Custom { method: &'static str },
GetBundlesStatuses,
GetTipAccounts,
SendBundle,
}
impl fmt::Display for RpcRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let method = match self {
RpcRequest::Custom { method } => method,
RpcRequest::GetBundlesStatuses => "getBundleStatuses",
RpcRequest::GetTipAccounts => "getTipAccounts",
RpcRequest::SendBundle => "sendBundle",
};
write!(f, "{method}")
}
}
impl RpcRequest {
pub fn build_request_json(self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";
json!({
"jsonrpc": jsonrpc,
"id": id,
"method": format!("{self}"),
"params": params,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_request_json() {
let test_request = RpcRequest::GetTipAccounts;
let request = test_request.build_request_json(1, json!([]));
assert_eq!(request["method"], "getTipAccounts");
assert_eq!(request["params"], json!([]));
let test_request = RpcRequest::GetBundlesStatuses;
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
let request = test_request.build_request_json(1, json!([addr]));
assert_eq!(request["method"], "getBundleStatuses");
assert_eq!(request["params"], json!([addr]));
}
}

View File

@@ -0,0 +1,406 @@
use std::time::Duration;
use bincode::serialize;
use log::*;
use serde_json::{json, Value};
use solana_rpc_client::{
rpc_client::{RpcClientConfig, SerializableTransaction},
rpc_sender::RpcTransportStats,
};
use solana_rpc_client_api::{
client_error::ErrorKind as ClientErrorKind, request::RpcError, response::Response,
};
use solana_sdk::{bs58, commitment_config::CommitmentConfig};
use solana_transaction_status::UiTransactionEncoding;
use crate::jsonrpc_client::{
client_error,
client_error::{Error as ClientError, Result as ClientResult},
http_sender::HttpSender,
request::RpcRequest,
rpc_sender::*,
};
pub type RpcResult<T> = client_error::Result<Response<T>>;
/// A client of a remote Jito block engine node.
///
/// `RpcClient` communicates with a block engine node over [JSON-RPC]
/// It is the primary Rust interface for querying and transacting with the network
/// from external programs.
///
/// This is modeled very closely with the solan RpcClient with similar error types.
/// You can treat the client similar to the Solana RpcClient with the difference being
/// the RpcClient supports the block engine apis.
///
/// The client can be used with jito block engine proxy server for authentication
/// The client can be used as is to send requests unauthenticated to the jito block engine as well
///
/// Please note that the commitment level is not used at the moment. Support will be added
/// later on to specify and use the commitment levels.
pub struct RpcClient {
sender: Box<dyn RpcSender + Send + Sync + 'static>,
config: RpcClientConfig,
}
impl RpcClient {
/// Create an `RpcClient` from an [`RpcSender`] and an [`RpcClientConfig`].
///
/// This is the basic constructor, allowing construction with any type of
/// `RpcSender`. Most applications should use one of the other constructors,
/// such as [`RpcClient::new`], [`RpcClient::new_with_commitment`] or
/// [`RpcClient::new_with_timeout`].
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(
sender: T,
config: RpcClientConfig,
) -> Self {
Self {
sender: Box::new(sender),
config,
}
}
/// Create an HTTP `RpcClient`.
///
/// The URL is an HTTP URL, usually for port 8899, as in
/// "http://localhost:8899".
///
/// The client has a default timeout of 30 seconds, and a default [commitment
/// level][cl] of [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// # Examples
///
/// ```
/// # use jito_block_engine_json_rpc_client::jsonrpc_client::rpc_client::RpcClient;
/// let url = "http://localhost:8899".to_string();
/// let client = RpcClient::new(url);
/// ```
pub fn new(url: String) -> Self {
Self::new_with_commitment(url, CommitmentConfig::default())
}
/// Create an HTTP `RpcClient` with specified [commitment level][cl].
///
/// Please note the client is not currently implemented to support commitment level configs
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// The URL is an HTTP URL, usually for port 8899, as in
/// "http://localhost:8899".
///
/// The client has a default timeout of 30 seconds, and a user-specified
/// [`CommitmentLevel`] via [`CommitmentConfig`].
///
/// # Examples
///
/// # use solana_sdk::commitment_config::CommitmentConfig;
/// # use jito_block_engine_json_rpc_client::jsonrpc_client::rpc_client::RpcClient;
/// let url = "http://localhost:8899".to_string();
/// let commitment_config = CommitmentConfig::processed();
/// let client = RpcClient::new_with_commitment(url, commitment_config);
fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
Self::new_sender(
HttpSender::new(url),
RpcClientConfig::with_commitment(commitment_config),
)
}
/// Create an HTTP `RpcClient` with specified timeout.
///
/// The URL is an HTTP URL, usually for port 8899, as in
/// "http://localhost:8899".
///
/// The client has and a default [commitment level][cl] of
/// [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// # Examples
///
/// ```
/// # use std::time::Duration;
/// # use jito_block_engine_json_rpc_client::jsonrpc_client::rpc_client::RpcClient;
/// let url = "http://localhost::8899".to_string();
/// let timeout = Duration::from_secs(1);
/// let client = RpcClient::new_with_timeout(url, timeout);
/// ```
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
Self::new_sender(
HttpSender::new_with_timeout(url, timeout),
RpcClientConfig::with_commitment(CommitmentConfig::default()),
)
}
/// Get the configured url of the client's sender
pub fn url(&self) -> String {
self.sender.url()
}
/// Get the configured default [commitment level][cl].
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// The commitment config may be specified during construction, and
/// determines how thoroughly committed a transaction must be when waiting
/// for its confirmation or otherwise checking for confirmation. If not
/// specified, the default commitment level is
/// [`Finalized`](CommitmentLevel::Finalized).
///
/// The default commitment level is overridden when calling methods that
/// explicitly provide a [`CommitmentConfig`], like
/// [`RpcClient::confirm_transaction_with_commitment`].
pub fn commitment(&self) -> CommitmentConfig {
self.config.commitment_config
}
/// Submits a bundle of signed transactions to the network.
///
/// This returns a bundle_id on success and will return an error
/// code on failure. The error code will only be on the basic
/// validation of the bundle and publishing the bundle to the mempool
/// For the bundle status regarding whether it landed or not, get_bundle_statuses
/// should be used with the bundle id.
///
/// Example excerpt can be as below
///
/// use jito_block_engine_json_rpc_client::jsonrpc_client::rpc_client::RpcClient;
/// use solana_program::hash::Hash;
/// use solana_sdk::{pubkey::Pubkey, signer::keypair::Keypair};
///
/// let base = 0;
/// let MAX_BUNDLE_LEN = 5;
/// let searcher_keypair = Keypair::new();
/// let recent_blockhash = Hash::new_unique();
///
/// let mut bundle: Vec<_> = (0..(MAX_BUNDLE_LEN) as u64)
/// .map(|amount| {
/// VersionedTransaction::from(system_transaction::transfer(
/// &searcher_keypair,
/// &searcher_keypair.pubkey(),
/// base + amount,
/// recent_blockhash,
/// ))
/// })
/// .collect();
///
/// let rpc_client = RpcClient::new(SERVER_URL.to_owned());
/// let response = rpc_client.send_bundle(&bundle).await;
pub async fn send_bundle(
&self,
transactions: &[impl SerializableTransaction],
) -> ClientResult<String> {
let mut serialized_encoded: Vec<String> = Vec::with_capacity(transactions.len());
for transaction in transactions {
let encoding = self.default_cluster_transaction_encoding().await?;
serialized_encoded.push(serialize_and_encode(transaction, encoding)?);
}
//The bundle may or may
// not have been submitted to the cluster, so callers should verify the success of
// the correct transaction signature independently.
match self
.send(RpcRequest::SendBundle, json!([serialized_encoded]))
.await
{
Ok(signature_base58_str) => ClientResult::Ok(signature_base58_str),
Err(err) => {
if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
code, message, ..
}) = &err.kind
{
debug!("{} {}", code, message);
}
Err(err)
}
}
}
async fn default_cluster_transaction_encoding(
&self,
) -> Result<UiTransactionEncoding, RpcError> {
Ok(UiTransactionEncoding::Base58)
}
/// Gets the statuses of a list of bundle ids.
///
/// Returns the statuses of a list of signatures. Each signature must be a bundle_id.
/// bundle ids are sha256 hashes of their tx signatures (we get it after a sendBundle)
/// This method currently will provide information regarding whether the bundle
/// landed or not.
/// The behavior is similar to the solana rpc method getSignatureStatuses
/// https://docs.solana.com/api/http#getsignaturestatuses
///
/// If the bundle_id is not found or the all of the transactions in the bundle has not landed,
/// we return null. If found and landed, we return the context information including the slot
/// at which the request was made and result with the bundle_id(s) and the transactions with the
/// slot and confirmation status. At this point, its assumed that all transactions within a bundle
/// will have the same slot number and confirmation status.
///
/// The confirmation status of a bundle is the confirmation status of the transactions.
/// This api does not provide a commitment level to configure, but will return the commitment level
/// as returned by the rpc. The rpc used to fetch bulk transaction status does not provide a commitment
/// level configuration option either.
///
/// Example excerpt can be as below
///
/// use jito_block_engine_json_rpc_client::jsonrpc_client::rpc_client::RpcClient;
///
/// let SERVER_URL = "http://localhost:8899";
/// let bundle_id = "bundle_id".to_owned();
///
/// let rpc_client = RpcClient::new(SERVER_URL.to_owned());
/// let response = rpc_client.get_bundle_statuses(&[bundle_id.clone()]).await;
pub async fn get_bundle_statuses(
&self,
signatures: &[String],
) -> RpcResult<Vec<serde_json::Value>> {
self.send(RpcRequest::GetBundlesStatuses, json!([signatures]))
.await
}
/// Returns the tip accounts to be used for tip payments.
pub async fn get_tip_accounts(&self) -> ClientResult<Vec<String>> {
self.send(RpcRequest::GetTipAccounts, Value::Null).await
}
pub async fn send<T>(&self, request: RpcRequest, params: Value) -> ClientResult<T>
where
T: serde::de::DeserializeOwned,
{
assert!(params.is_array() || params.is_null());
let response = self
.sender
.send(request, params)
.await
.map_err(|err| err.into_with_request(request))?;
serde_json::from_value(response)
.map_err(|err| ClientError::new_with_request(err.into(), request))
}
pub fn get_transport_stats(&self) -> RpcTransportStats {
self.sender.get_transport_stats()
}
}
fn serialize_and_encode<T>(input: &T, encoding: UiTransactionEncoding) -> ClientResult<String>
where
T: serde::ser::Serialize,
{
let serialized = serialize(input)
.map_err(|e| ClientErrorKind::Custom(format!("Serialization failed: {e}")))?;
let encoded = match encoding {
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
_ => {
return Err(ClientErrorKind::Custom(format!(
"unsupported encoding: {encoding}. Supported encodings: base58"
))
.into())
}
};
Ok(encoded)
}
// Sample tests. Can use these as a reference point on how to use the api and what to expect
#[cfg(test)]
mod rpc_client_tests {
use solana_program::hash::Hash;
use solana_sdk::{
pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, system_transaction,
transaction::VersionedTransaction,
};
use crate::jsonrpc_client::rpc_client::RpcClient;
// Use the proxy server url here.
const SERVER_URL: &str = "http://0.0.0.0:8080/api/v1/bundles";
#[tokio::test]
pub async fn get_tip_accounts() {
// Let's try the same with the rpc client
let rpc_client = RpcClient::new(SERVER_URL.to_owned());
let tip_accounts = rpc_client.get_tip_accounts().await;
// Sample output. Pick only randomly to not have contention
// ["9ttgPBBhRYFuQccdR1DSnb7hydsWANoDsV3P9kaGMCEh",
// "EoW3SUQap7ZeynXQ2QJ847aerhxbPVr843uMeTfc9dxM",
// "4xgEmT58RwTNsF5xm2RMYCnR1EVukdK8a1i2qFjnJFu3",
// "B1mrQSpdeMU9gCvkJ6VsXVVoYjRGkNA7TtjMyqxrhecH",
// "aTtUk2DHgLhKZRDjePq6eiHRKC1XXFMBiSUfQ2JNDbN",
// "9n3d1K5YD2vECAbRFhFFGYNNjiXtHXJWn9F31t89vsAV",
// "ARTtviJkLLt6cHGQDydfo1Wyk6M4VGZdKZ2ZhdnJL336",
// "E2eSqe33tuhAHKTrwky5uEjaVqnb2T9ns6nHHUrN8588"]
println!("{:?}", tip_accounts);
}
#[tokio::test]
pub async fn send_bundle() {
let rpc_client = RpcClient::new(SERVER_URL.to_owned());
// Use your own keypair to sign
let signer_keypair = Keypair::new();
// Get the latest blockhash from solana cluster. Can use https://docs.solana.com/api/http#getlatestblockhash
let recent_blockhash = Hash::new_unique();
// Use the get_tip_accounts to randomly select a tip account to send tips to
let tip_account = Pubkey::try_from("DCN82qDxJAQuSqHhv2BJuAgi41SPeKZB5ioBCTMNDrCC").unwrap();
let mut bundle: Vec<_> = vec![VersionedTransaction::from(system_transaction::transfer(
&signer_keypair,
&signer_keypair.pubkey(),
10000,
recent_blockhash,
))];
// Add the tip
bundle.push(VersionedTransaction::from(system_transaction::transfer(
&signer_keypair,
&tip_account,
10000,
recent_blockhash,
)));
let response = rpc_client.send_bundle(&bundle).await;
// If successful, the bundle_id can be retrieved. Else, an error code will be provided
println!("{:?}", response);
}
#[tokio::test]
pub async fn get_bundle_statuses() {
let rpc_client = RpcClient::new(SERVER_URL.to_owned());
// Use the bundle id you got from send_bundle
let bundle_id =
"6e4b90284778a40633b56e4289202ea79e62d2296bb3d45398bb93f6c9ec083d".to_owned();
let response = rpc_client.get_bundle_statuses(&[bundle_id]).await;
// Sample success output:
// Response {
// context: RpcResponseContext {
// slot: 0, api_version: None },
// value: [Object {
// "bundle_id": String("6e4b90284778a40633b56e4289202ea79e62d2296bb3d45398bb93f6c9ec083d"),
// "transactions": Array [String("4DGCuaKc2oue4Z8YC6mBwyg3oPAFG64BfxDtMbqDU3Du9zr26oVSuZcjSnJqTnHnKYFJ4AdPuq5kUrWKwTFLKtW6"),
// String("srrgfKABYeaKazZjBmpuPKySJ8qgqezYaCdDnB9nhED5CFhviZ1wgcs5vEKnAK9L2ytRauWG9czGoKRxajpZ1YR")],
// "slot": Number(240632575),
// "confirmation_status": String("finalized"),
// "err": Object {"Ok": Null}}] }
//
// Sample retryable error output:
// Response {
// context: RpcResponseContext {
// slot: 0, api_version: None },
// value: [Object {
// "bundle_id": String("6e4b90284778a40633b56e4289202ea79e62d2296bb3d45398bb93f6c9ec083d"),
// "transactions": Array [String("4DGCuaKc2oue4Z8YC6mBwyg3oPAFG64BfxDtMbqDU3Du9zr26oVSuZcjSnJqTnHnKYFJ4AdPuq5kUrWKwTFLKtW6"),
// String("srrgfKABYeaKazZjBmpuPKySJ8qgqezYaCdDnB9nhED5CFhviZ1wgcs5vEKnAK9L2ytRauWG9czGoKRxajpZ1YR")],
// "slot": Number(612529),
// "confirmation_status": Null,
// "err": Object {"Err": Object {"Retryable": String("Failed to retrieve information from solana cluster")}}}] }
// If unknown bundle, the response would be null
println!("{:?}", response);
}
}

View File

@@ -0,0 +1,21 @@
use async_trait::async_trait;
use solana_rpc_client::rpc_sender::RpcTransportStats;
use crate::jsonrpc_client::{client_error::Result, request::RpcRequest};
/// A transport for RPC calls.
///
/// `RpcSender` implements the underlying transport of requests to, and
/// responses from, a Solana node, and is used primarily by [`RpcClient`].
///
/// [`RpcClient`]: crate::rpc_client::RpcClient
#[async_trait]
pub trait RpcSender {
async fn send(
&self,
request: RpcRequest,
params: serde_json::Value,
) -> Result<serde_json::Value>;
fn get_transport_stats(&self) -> RpcTransportStats;
fn url(&self) -> String;
}

View File

@@ -0,0 +1 @@
pub mod jsonrpc_client;

View File

@@ -0,0 +1 @@
{"rustc_fingerprint":16894786775884202872,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.1 (4eb161250 2025-03-15)\nbinary: rustc\ncommit-hash: 4eb161250e340c8f48f66e2b929ef4a5bed7c181\ncommit-date: 2025-03-15\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.1\nLLVM version: 19.1.7\n","stderr":""},"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}

View File

@@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/

View File

@@ -0,0 +1 @@
/pumpfun_indexer/json-rpc-client/target/release/libjito_block_engine_json_rpc_client.rlib: /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/client_error.rs /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/http_sender.rs /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/mod.rs /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/request.rs /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/rpc_client.rs /pumpfun_indexer/json-rpc-client/src/jsonrpc_client/rpc_sender.rs /pumpfun_indexer/json-rpc-client/src/lib.rs

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
[package]
name = "pump_interface"
version = "0.1.0"
edition = "2021"
[dependencies.borsh]
version = "^0.10"
[dependencies.num-derive]
version = "^0.3"
[dependencies.num-traits]
version = "^0.2"
[dependencies.serde]
optional = true
version = "^1.0"
[dependencies.solana-program]
version = "^2.1.7"
[dependencies.thiserror]
version = "^1.0"
[dependencies.strum]
version = "0.26.3"
[dependencies.strum_macros]
version = "0.26.4"
[dependencies.Inflector]
version = "=0.11.4"

View File

@@ -0,0 +1,84 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;
pub const GLOBAL_ACCOUNT_DISCM: [u8; 8] = [167, 232, 232, 177, 200, 108, 114, 127];
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Global {
pub initialized: bool,
pub authority: Pubkey,
pub fee_recipient: Pubkey,
pub initial_virtual_token_reserves: u64,
pub initial_virtual_sol_reserves: u64,
pub initial_real_token_reserves: u64,
pub token_total_supply: u64,
pub fee_basis_points: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GlobalAccount(pub Global);
impl GlobalAccount {
pub fn deserialize(buf: &[u8]) -> std::io::Result<Self> {
use std::io::Read;
let mut reader = buf;
let mut maybe_discm = [0u8; 8];
reader.read_exact(&mut maybe_discm)?;
if maybe_discm != GLOBAL_ACCOUNT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
GLOBAL_ACCOUNT_DISCM, maybe_discm
),
));
}
Ok(Self(Global::deserialize(&mut reader)?))
}
pub fn serialize<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(&GLOBAL_ACCOUNT_DISCM)?;
self.0.serialize(&mut writer)
}
pub fn try_to_vec(&self) -> std::io::Result<Vec<u8>> {
let mut data = Vec::new();
self.serialize(&mut data)?;
Ok(data)
}
}
pub const BONDING_CURVE_ACCOUNT_DISCM: [u8; 8] = [23, 183, 248, 55, 96, 216, 172, 96];
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BondingCurve {
pub virtual_token_reserves: u64,
pub virtual_sol_reserves: u64,
pub real_token_reserves: u64,
pub real_sol_reserves: u64,
pub token_total_supply: u64,
pub complete: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BondingCurveAccount(pub BondingCurve);
impl BondingCurveAccount {
pub fn deserialize(buf: &[u8]) -> std::io::Result<Self> {
use std::io::Read;
let mut reader = buf;
let mut maybe_discm = [0u8; 8];
reader.read_exact(&mut maybe_discm)?;
if maybe_discm != BONDING_CURVE_ACCOUNT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
BONDING_CURVE_ACCOUNT_DISCM, maybe_discm
),
));
}
Ok(Self(BondingCurve::deserialize(&mut reader)?))
}
pub fn serialize<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(&BONDING_CURVE_ACCOUNT_DISCM)?;
self.0.serialize(&mut writer)
}
pub fn try_to_vec(&self) -> std::io::Result<Vec<u8>> {
let mut data = Vec::new();
self.serialize(&mut data)?;
Ok(data)
}
}

View File

@@ -0,0 +1,48 @@
#![allow(non_local_definitions)]
use solana_program::{
decode_error::DecodeError,
msg,
program_error::{PrintProgramError, ProgramError},
};
use thiserror::Error;
#[derive(Clone, Copy, Debug, Eq, Error, num_derive::FromPrimitive, PartialEq)]
pub enum PumpError {
#[error("The given account is not authorized to execute this instruction.")]
NotAuthorized = 6000,
#[error("The program is already initialized.")]
AlreadyInitialized = 6001,
#[error("slippage: Too much SOL required to buy the given amount of tokens.")]
TooMuchSolRequired = 6002,
#[error("slippage: Too little SOL received to sell the given amount of tokens.")]
TooLittleSolReceived = 6003,
#[error("The mint does not match the bonding curve.")]
MintDoesNotMatchBondingCurve = 6004,
#[error("The bonding curve has completed and liquidity migrated to raydium.")]
BondingCurveComplete = 6005,
#[error("The bonding curve has not completed.")]
BondingCurveNotComplete = 6006,
#[error("The program is not initialized.")]
NotInitialized = 6007,
}
impl From<PumpError> for ProgramError {
fn from(e: PumpError) -> Self {
ProgramError::Custom(e as u32)
}
}
impl<T> DecodeError<T> for PumpError {
fn type_of() -> &'static str {
"PumpError"
}
}
impl PrintProgramError for PumpError {
fn print<E>(&self)
where
E: 'static
+ std::error::Error
+ DecodeError<E>
+ PrintProgramError
+ num_traits::FromPrimitive,
{
msg!(&self.to_string());
}
}

View File

@@ -0,0 +1,134 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;
pub const CREATE_EVENT_EVENT_DISCM: [u8; 8] = [27, 114, 169, 77, 222, 235, 99, 118];
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct CreateEvent {
name: String,
symbol: String,
uri: String,
mint: Pubkey,
bonding_curve: Pubkey,
user: Pubkey,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CreateEventEvent(pub CreateEvent);
impl BorshSerialize for CreateEventEvent {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
CREATE_EVENT_EVENT_DISCM.serialize(writer)?;
self.0.serialize(writer)
}
}
impl CreateEventEvent {
pub fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
let maybe_discm = <[u8; 8]>::deserialize(buf)?;
if maybe_discm != CREATE_EVENT_EVENT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
CREATE_EVENT_EVENT_DISCM, maybe_discm
),
));
}
Ok(Self(CreateEvent::deserialize(buf)?))
}
}
pub const TRADE_EVENT_EVENT_DISCM: [u8; 8] = [189, 219, 127, 211, 78, 230, 97, 238];
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct TradeEvent {
mint: Pubkey,
sol_amount: u64,
token_amount: u64,
is_buy: bool,
user: Pubkey,
timestamp: i64,
virtual_sol_reserves: u64,
virtual_token_reserves: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TradeEventEvent(pub TradeEvent);
impl BorshSerialize for TradeEventEvent {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
TRADE_EVENT_EVENT_DISCM.serialize(writer)?;
self.0.serialize(writer)
}
}
impl TradeEventEvent {
pub fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
let maybe_discm = <[u8; 8]>::deserialize(buf)?;
if maybe_discm != TRADE_EVENT_EVENT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
TRADE_EVENT_EVENT_DISCM, maybe_discm
),
));
}
Ok(Self(TradeEvent::deserialize(buf)?))
}
}
pub const COMPLETE_EVENT_EVENT_DISCM: [u8; 8] = [95, 114, 97, 156, 212, 46, 152, 8];
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct CompleteEvent {
user: Pubkey,
mint: Pubkey,
bonding_curve: Pubkey,
timestamp: i64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CompleteEventEvent(pub CompleteEvent);
impl BorshSerialize for CompleteEventEvent {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
COMPLETE_EVENT_EVENT_DISCM.serialize(writer)?;
self.0.serialize(writer)
}
}
impl CompleteEventEvent {
pub fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
let maybe_discm = <[u8; 8]>::deserialize(buf)?;
if maybe_discm != COMPLETE_EVENT_EVENT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
COMPLETE_EVENT_EVENT_DISCM, maybe_discm
),
));
}
Ok(Self(CompleteEvent::deserialize(buf)?))
}
}
pub const SET_PARAMS_EVENT_EVENT_DISCM: [u8; 8] = [223, 195, 159, 246, 62, 48, 143, 131];
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct SetParamsEvent {
fee_recipient: Pubkey,
initial_virtual_token_reserves: u64,
initial_virtual_sol_reserves: u64,
initial_real_token_reserves: u64,
token_total_supply: u64,
fee_basis_points: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SetParamsEventEvent(pub SetParamsEvent);
impl BorshSerialize for SetParamsEventEvent {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
SET_PARAMS_EVENT_EVENT_DISCM.serialize(writer)?;
self.0.serialize(writer)
}
}
impl SetParamsEventEvent {
pub fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
let maybe_discm = <[u8; 8]>::deserialize(buf)?;
if maybe_discm != SET_PARAMS_EVENT_EVENT_DISCM {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"discm does not match. Expected: {:?}. Received: {:?}",
SET_PARAMS_EVENT_EVENT_DISCM, maybe_discm
),
));
}
Ok(Self(SetParamsEvent::deserialize(buf)?))
}
}

View File

@@ -0,0 +1,9 @@
solana_program::declare_id!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
pub mod accounts;
pub use accounts::*;
pub mod instructions;
pub use instructions::*;
pub mod errors;
pub use errors::*;
pub mod events;
pub use events::*;

View File

@@ -0,0 +1,37 @@
// src/serializer.rs
#[cfg(feature = "serde")]
use serde::Deserialize;
#[cfg(feature = "serde")]
pub fn serialize_u128_as_string<S>(value: &u128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&value.to_string())
}
#[cfg(feature = "serde")]
pub fn deserialize_u128_as_string<'de, D>(deserializer: D) -> Result<u128, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse::<u128>().map_err(serde::de::Error::custom)
}
#[cfg(feature = "serde")]
pub fn serialize_i128_as_string<S>(value: &i128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&value.to_string())
}
#[cfg(feature = "serde")]
pub fn deserialize_i128_as_string<'de, D>(deserializer: D) -> Result<i128, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse::<i128>().map_err(serde::de::Error::custom)
}

View File

@@ -0,0 +1,140 @@
#[cfg(feature = "serde")]
use crate::serializer::{
deserialize_i128_as_string, deserialize_u128_as_string, serialize_i128_as_string,
serialize_u128_as_string,
};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;
#[derive(Default, Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OpenPositionBumps {
pub position_bump: u8,
}
#[derive(Default, Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OpenPositionWithMetadataBumps {
pub position_bump: u8,
pub metadata_bump: u8,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PositionRewardInfo {
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub growth_inside_checkpoint: u128,
pub amount_owed: u64,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tick {
pub initialized: bool,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_i128_as_string",
deserialize_with = "deserialize_i128_as_string"
)
)]
pub liquidity_net: i128,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub liquidity_gross: u128,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub fee_growth_outside_a: u128,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub fee_growth_outside_b: u128,
pub reward_growths_outside: [u128; 3],
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WhirlpoolRewardInfo {
pub mint: Pubkey,
pub vault: Pubkey,
pub authority: Pubkey,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub emissions_per_second_x64: u128,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serialize_u128_as_string",
deserialize_with = "deserialize_u128_as_string"
)
)]
pub growth_global_x64: u128,
}
#[derive(Default, Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WhirlpoolBumps {
pub whirlpool_bump: u8,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RemainingAccountsSlice {
pub accounts_type: AccountsType,
pub length: u8,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RemainingAccountsInfo {
pub slices: Vec<RemainingAccountsSlice>,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CurrIndex {
Below,
Inside,
Above,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TickLabel {
Upper,
Lower,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Direction {
Left,
Right,
}
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AccountsType {
TransferHookA,
TransferHookB,
TransferHookReward,
TransferHookInput,
TransferHookIntermediate,
TransferHookOutput,
SupplementalTickArrays,
SupplementalTickArraysOne,
SupplementalTickArraysTwo,
}

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
# ─── 1) jump to script’s own dir ───
cd "$(dirname "$0")"
# ─── 2) build in release (skips if up-to-date) ───
cargo build --release
# ─── 3) where our binary really lives ───
BIN="$(pwd)/target/release/grpc-raydium-pool-monitoring-rust"
# ─── 4) if PIN_CORES is set, pin there; otherwise run normally ───
if [[ -n "${PIN_CORES:-}" ]]; then
echo "📌 pinning to cores: $PIN_CORES"
exec taskset -c "$PIN_CORES" "$BIN" "$@"
else
echo "▶️ running on all available cores"
exec "$BIN" "$@"
fi

View File

@@ -0,0 +1,15 @@
[package]
name = "shared_state"
version = "0.1.0"
authors = ["Mephisto"]
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["sync"] }
lazy_static = "1.4"
serde = { version = "1.0", features = ["derive"] }
once_cell = "1.21.3"
solana-sdk = "2.1.7"
[lib]
path = "src/lib.rs"

View File

@@ -0,0 +1 @@
pub mod state;

View File

@@ -0,0 +1,269 @@
// shared_state/src/lib.rs
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::AtomicUsize;
use std::time::Instant;
use tokio::sync::Mutex;
/// A request to sell a mint for a given amount, marking if it's urgent.
#[derive(Debug, Clone)]
pub struct SellOrder {
pub mint: String,
pub amount: u64,
pub use_jito: bool, // Indicates if Jito should be used for this sell
pub urgent: bool,
}
#[derive(Debug, Clone)]
pub struct BuyOrder {
pub mint: String,
pub creator: Pubkey,
pub use_jito: bool,
pub urgent: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BoughtTokenInfo {
mint: String,
ata: Option<String>,
amount: u64,
fallback_amount: u64,
timestamp: Instant,
last_activity: Instant,
signature: Option<String>,
follow_up_buys: u32,
sell_triggered: bool,
first_sell_detected: bool,
sol_inflow: f64,
sol_outflow: f64,
creator_vault: Option<Pubkey>,
unique_buyers: std::collections::HashSet<String>,
suspected_creator: Option<String>,
mint_detected_at: Instant,
buy_executed_at: Option<Instant>,
sell_triggered_at: Option<Instant>,
ata_balance_zeroed: bool,
pub sell_issued_slot: u64,
sell_confirmed: bool,
pub buy_slot: u64,
pub sell_slot: Option<u64>,
pub sell_signature: Option<String>,
sell_executed_at: Option<std::time::Instant>,
sell_retry_count: u8,
}
impl BoughtTokenInfo {
pub fn new(
mint: String,
ata: Option<String>,
fallback_amount: u64,
timestamp: Instant,
signature: Option<String>,
buy_slot: u64,
creator_vault: Option<Pubkey>, // new param
) -> Self {
Self {
mint,
ata,
amount: fallback_amount,
fallback_amount,
timestamp,
last_activity: timestamp,
signature,
follow_up_buys: 0,
sell_triggered: false,
first_sell_detected: false,
sol_inflow: 0.0,
sol_outflow: 0.0,
unique_buyers: std::collections::HashSet::new(),
suspected_creator: None,
mint_detected_at: timestamp,
buy_executed_at: None,
sell_triggered_at: None,
ata_balance_zeroed: false,
buy_slot,
sell_slot: None,
sell_signature: None,
sell_issued_slot: 0,
sell_confirmed: false,
creator_vault, // initialize the creator vault
sell_executed_at: None,
sell_retry_count: 0,
}
}
pub fn creator_vault(&self) -> Option<&Pubkey> {
self.creator_vault.as_ref()
}
pub fn mint(&self) -> &String {
&self.mint
}
pub fn buy_slot(&self) -> u64 {
self.buy_slot
}
pub fn set_buy_slot(&mut self, slot: u64) {
self.buy_slot = slot;
}
pub fn sell_slot(&self) -> Option<u64> {
self.sell_slot
}
pub fn set_sell_slot(&mut self, slot: u64) {
self.sell_slot = Some(slot);
}
pub fn set_buy_executed_at(&mut self, when: Instant) {
self.buy_executed_at = Some(when);
}
pub fn buy_executed_at(&self) -> Option<Instant> {
self.buy_executed_at
}
pub fn record_sell(&mut self, sig: String, slot: u64) {
self.sell_signature = Some(sig);
self.sell_issued_slot = slot;
self.sell_slot = Some(slot);
self.sell_confirmed = false;
}
pub fn confirm_sell(&mut self) {
self.sell_confirmed = true;
}
pub fn sell_confirmed(&self) -> bool {
self.sell_confirmed
}
pub fn sell_issued_slot(&self) -> u64 {
self.sell_issued_slot
}
pub fn set_sell_triggered_at(&mut self, when: Instant) {
self.sell_triggered_at = Some(when);
}
pub fn sell_triggered_at(&self) -> Option<Instant> {
self.sell_triggered_at
}
pub fn set_suspected_creator(&mut self, creator: String) {
self.suspected_creator = Some(creator);
}
pub fn suspected_creator(&self) -> Option<&String> {
self.suspected_creator.as_ref()
}
pub fn ata_balance_zeroed(&self) -> bool {
self.ata_balance_zeroed
}
pub fn set_ata_balance_zeroed(&mut self, value: bool) {
self.ata_balance_zeroed = value;
}
pub fn sol_inflow(&self) -> f64 {
self.sol_inflow
}
pub fn add_sol_inflow(&mut self, amount: f64) {
self.sol_inflow += amount;
self.update_last_activity();
}
pub fn sol_outflow(&self) -> f64 {
self.sol_outflow
}
pub fn add_sol_outflow(&mut self, amount: f64) {
self.sol_outflow += amount;
self.update_last_activity();
}
pub fn ata(&self) -> Option<&String> {
self.ata.as_ref()
}
pub fn amount(&self) -> u64 {
self.amount
}
pub fn set_amount(&mut self, amt: u64) {
self.amount = amt;
}
pub fn fallback_amount(&self) -> u64 {
self.fallback_amount
}
pub fn timestamp(&self) -> Instant {
self.timestamp
}
pub fn last_activity(&self) -> Instant {
self.last_activity
}
pub fn update_last_activity(&mut self) {
self.last_activity = Instant::now();
}
pub fn signature(&self) -> Option<&String> {
self.signature.as_ref()
}
pub fn set_sell_signature(&mut self, sig: String) {
self.sell_signature = Some(sig);
}
pub fn sell_signature(&self) -> Option<&String> {
self.sell_signature.as_ref()
}
pub fn follow_up_buys(&self) -> u32 {
self.follow_up_buys
}
pub fn increment_follow_up_buys(&mut self) {
self.follow_up_buys += 1;
self.update_last_activity();
}
pub fn sell_triggered(&self) -> bool {
self.sell_triggered
}
pub fn set_sell_triggered(&mut self, v: bool) {
self.sell_triggered = v;
}
pub fn first_sell_detected(&self) -> bool {
self.first_sell_detected
}
pub fn mark_first_sell_detected(&mut self) {
self.first_sell_detected = true;
self.update_last_activity();
}
pub fn zero_amount(&mut self) {
self.amount = 0;
}
pub fn unique_buyers(&self) -> &std::collections::HashSet<String> {
&self.unique_buyers
}
pub fn add_unique_buyer(&mut self, addr: String) -> bool {
let ins = self.unique_buyers.insert(addr);
if ins {
self.update_last_activity();
}
ins
}
pub fn sell_executed_at(&self) -> Option<Instant> {
self.sell_executed_at
}
pub fn set_sell_executed_at(&mut self, t: Instant) {
self.sell_executed_at = Some(t);
}
pub fn retry_count(&self) -> u8 {
self.sell_retry_count
}
pub fn increment_retry_count(&mut self) {
self.sell_retry_count = self.sell_retry_count.saturating_add(1);
}
pub fn reset_retry_count(&mut self) {
self.sell_retry_count = 0;
}
}
lazy_static! {
pub static ref BOUGHT_TOKENS: Mutex<HashMap<String, BoughtTokenInfo>> =
Mutex::new(HashMap::new());
}
pub static CURRENT_SLOT: Lazy<AtomicU64> = Lazy::new(|| AtomicU64::new(0));
pub static PENDING_MINTS: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));

View File

@@ -0,0 +1,21 @@
// shared_state/src/lib.rs
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tokio::sync::Mutex;
use lazy_static::lazy_static;
#[derive(Debug, Clone)]
pub struct TokenTrackingState {
pub mint: String,
pub first_buyer: Option<String>,
pub first_buy_amount: f64,
pub suspected_creators: HashSet<String>,
pub sell_detected: bool,
pub sell_split_ratio: usize,
}
lazy_static! {
pub static ref TRACKED_TOKENS: Arc<Mutex<HashMap<String, TokenTrackingState>>> =
Arc::new(Mutex::new(HashMap::new()));
}

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# 2) Set any necessary environment variables
export RUST_LOG=info
export BLOCK_ENGINE_URL="https://mainnet.block-engine.jito.wtf"
# Path to your approved Jito auth keypair
export AUTH_KEYPAIR="/home/ubuntu/shreds_sniper/shredstream-proxy/validator-keypair.json"
# Comma-separated Jito regions
export DESIRED_REGIONS="tokyo, frankfurt, amsterdam, ny, london, singapore, slc"
# Comma-separated list of destination IP:UDP_PORT entries
# (example: forward to localhost:8001 and a remote at 10.0.1.5:8002)
export DEST_IP_PORTS="127.0.0.1:8001"
# 3) Run the proxy binary
# If you built with `cargo build --release`, the binary lives under `target/release/`
exec /home/ubuntu/shreds_sniper/shredstream-proxy/target/release/jito-shredstream-proxy shredstream \
--block-engine-url "${BLOCK_ENGINE_URL}" \
--auth-keypair "${AUTH_KEYPAIR}" \
--desired-regions "${DESIRED_REGIONS}" \
--dest-ip-ports "${DEST_IP_PORTS}" \
--grpc-service-port 50051 \

View File

@@ -0,0 +1,86 @@
struct TradeDispatcher {
db: Database,
bot: AutoSend<Bot>,
user_ids: Vec<i64>, // all whitelisted telegram IDs
next_idx: AtomicUsize, // for round-robin
}
impl TradeDispatcher {
pub async fn new(db: Database, bot: AutoSend<Bot>) -> Self {
let ids = db
.users
.find(doc! { "is_whitelisted": true }, None)
.await
.unwrap()
.try_collect::<Vec<UserRecord>>()
.await
.unwrap()
.into_iter()
.map(|u| u.telegram_id)
.collect();
TradeDispatcher { db, bot, user_ids: ids, next_idx: AtomicUsize::new(0) }
}
/// Round-robin pick your next telegram user
fn pop_next_user(&self) -> i64 {
let len = self.user_ids.len();
let i = self.next_idx.fetch_add(1, Ordering::Relaxed) % len;
self.user_ids[i]
}
pub async fn dispatch_buy(&self, buy: BuyOrder) {
// 1) pick user
let telegram_id = self.pop_next_user();
let user = self.db.get_user(telegram_id).await.unwrap()
.expect("should exist and be whitelisted");
// 2) decrypt & load their Keypair
let keypair = decrypt_keypair(&user
.wallets
.iter()
.find(|w| w.id == user.trading_wallet_id.unwrap())
.unwrap()
.private_key_enc
);
// 3) build per-user SwapConfig
let mut cfg = SwapConfig::default();
if let Some(sl) = user.slippage_pct { cfg.slippage_bps = (sl * 100.0) as u16 }
if let Some(tip) = user.tip_amount { cfg.use_priority_tip = tip > 0 }
if let Some(min_tok) = user.min_tokens_to_buy { cfg.min_tokens = Some(min_tok as u64) }
// 4) hand off to pump_swap
let input = /* build your SwapInput exactly as before */;
let result = pump_swap(
AppState {
rpc_client: /* shared */,
rpc_nonblocking_client: /* shared */,
wallet: Arc::new(keypair),
},
input,
"buy",
buy.use_jito,
buy.urgent,
)
.await;
// 5) notify on Telegram
let msg = match result {
Ok(sigs) => format!("✅ [{}] buy for `{}` succeeded: {}", telegram_id, buy.mint, sigs[0]),
Err(e) => format!("❌ [{}] buy for `{}` failed: {}", telegram_id, buy.mint, e),
};
let _ = self.bot.send_message(telegram_id, msg).await;
}
pub async fn dispatch_sell(&self, sell: SellOrder) {
// Look up who originally bought this mint
let user_id = {
let tokens = shared_state::BOUGHT_TOKENS.lock().await;
let info = tokens.get(&sell.mint).unwrap();
info.telegram_id().unwrap()
};
// same as buy: fetch user, decrypt, build cfg, call pump_swap("sell", ...) and notify
/* … */
}
}

View File

@@ -0,0 +1,239 @@
// src/commands.rs
use teloxide::prelude::*;
use teloxide::utils::command::BotCommand;
use mongodb::bson::oid::ObjectId;
use crate::db::Database;
use anyhow::Result;
#[derive(BotCommand, Clone)]
#[command(rename = "lowercase", description = "These commands are supported:")]
pub enum Command {
#[command(description = "display this text.")]
Help,
#[command(description = "start the bot.")]
Start,
#[command(description = "create a new wallet: /create_wallet <name> <private_key_or_mnemonic>")]
CreateWallet { name: String, key: String },
#[command(description = "list your wallets.")]
ListWallets,
#[command(description = "remove a wallet: /remove_wallet <wallet_id>")]
RemoveWallet { wallet_id: String },
#[command(description = "set trading wallet: /set_trading_wallet <wallet_id>")]
SetTradingWallet { wallet_id: String },
#[command(description = "set min tokens to buy: /set_min_tokens <amount>")]
SetMinTokens { amount: f64 },
#[command(description = "set slippage percent: /set_slippage <percent>")]
SetSlippage { percent: f64 },
#[command(description = "set tip amount: /set_tip <amount>")]
SetTip { amount: u64 },
#[command(description = "withdraw: /withdraw <wallet_id> <dest_address> <amount>")]
Withdraw { wallet_id: String, dest: String, amount: f64 },
#[command(description = "transfer: /transfer <wallet_id> <dest_address> <amount>")]
Transfer { wallet_id: String, dest: String, amount: f64 },
// Admin only:
#[command(description = "whitelist a user: /whitelist <telegram_id>")]
Whitelist { telegram_id: i64 },
#[command(description = "remove from whitelist: /unwhitelist <telegram_id>")]
Unwhitelist { telegram_id: i64 },
}
pub async fn handle_command(
bot: Bot,
msg: Message,
cmd: Command,
db: Database,
admin_ids: Vec<i64>,
) -> Result<()> {
let chat_id = msg.chat.id;
let user_id = msg.from().map(|u| u.id.0 as i64).unwrap_or(0);
match cmd {
Command::Help => {
bot.send_message(chat_id, Command::descriptions()).await?;
}
Command::Start => {
// Check whitelist
let is_whitelisted = db.is_whitelisted(user_id).await?;
if !is_whitelisted {
bot.send_message(chat_id, "You are not whitelisted. Please contact admin.").await?;
} else {
bot.send_message(chat_id, "Welcome! Use /help to see commands.").await?;
}
}
Command::CreateWallet { name, key } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
// TODO: encrypt `key` before storing, e.g. AES with ENCRYPTION_KEY
let private_enc = encrypt_key(&key)?;
match db.add_wallet(user_id, name.clone(), derive_address(&key)?, private_enc).await {
Ok(entry) => {
bot.send_message(chat_id, format!("Wallet created with ID: {}", entry.id)).await?;
}
Err(e) => {
bot.send_message(chat_id, format!("Error creating wallet: {}", e)).await?;
}
}
}
Command::ListWallets => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
let wallets = db.list_wallets(user_id).await?;
if wallets.is_empty() {
bot.send_message(chat_id, "No wallets. Use /create_wallet.").await?;
} else {
let mut text = String::from("Your wallets:\n");
for w in wallets {
text.push_str(&format!("- ID: {}, name: {}, address: {}\n", w.id, w.name, w.address));
}
bot.send_message(chat_id, text).await?;
}
}
Command::RemoveWallet { wallet_id } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
match ObjectId::parse_str(&wallet_id) {
Ok(oid) => {
if let Err(e) = db.remove_wallet(user_id, oid).await {
bot.send_message(chat_id, format!("Error removing wallet: {}", e)).await?;
} else {
bot.send_message(chat_id, "Wallet removed.").await?;
}
}
Err(_) => {
bot.send_message(chat_id, "Invalid wallet ID format.").await?;
}
}
}
Command::SetTradingWallet { wallet_id } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
match ObjectId::parse_str(&wallet_id) {
Ok(oid) => {
if let Err(e) = db.set_trading_wallet(user_id, oid).await {
bot.send_message(chat_id, format!("Error: {}", e)).await?;
} else {
bot.send_message(chat_id, "Trading wallet set.").await?;
}
}
Err(_) => {
bot.send_message(chat_id, "Invalid wallet ID.").await?;
}
}
}
Command::SetMinTokens { amount } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
if let Err(e) = db.update_setting_min_tokens(user_id, amount).await {
bot.send_message(chat_id, format!("Error: {}", e)).await?;
} else {
bot.send_message(chat_id, format!("min_tokens_to_buy set to {}", amount)).await?;
}
}
Command::SetSlippage { percent } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
if let Err(e) = db.update_setting_slippage(user_id, percent).await {
bot.send_message(chat_id, format!("Error: {}", e)).await?;
} else {
bot.send_message(chat_id, format!("slippage_pct set to {}%", percent)).await?;
}
}
Command::SetTip { amount } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
if let Err(e) = db.update_setting_tip(user_id, amount).await {
bot.send_message(chat_id, format!("Error: {}", e)).await?;
} else {
bot.send_message(chat_id, format!("tip_amount set to {}", amount)).await?;
}
}
Command::Withdraw { wallet_id, dest, amount } |
Command::Transfer { wallet_id, dest, amount } => {
if !db.is_whitelisted(user_id).await? {
bot.send_message(chat_id, "Not whitelisted.").await?;
return Ok(());
}
// Parse wallet ID
let oid = match ObjectId::parse_str(&wallet_id) {
Ok(o) => o,
Err(_) => {
bot.send_message(chat_id, "Invalid wallet ID.").await?;
return Ok(());
}
};
// Fetch user & wallet
if let Some(user) = db.get_user(user_id).await? {
if let Some(wallet) = user.wallets.iter().find(|w| w.id == oid) {
// Decrypt private key:
let private_key = decrypt_key(&wallet.private_key_enc)?;
// Then implement withdraw/transfer logic, e.g., call chain RPC:
// withdraw: send transaction moving `amount` tokens from wallet.address to dest.
// Here just stub:
bot.send_message(chat_id, format!(
"{} from wallet {} to {} of amount {}: not implemented yet",
if matches!(cmd, Command::Withdraw {..}) { "Withdraw" } else { "Transfer" },
wallet.name, dest, amount
)).await?;
} else {
bot.send_message(chat_id, "Wallet not found.").await?;
}
} else {
bot.send_message(chat_id, "User not found.").await?;
}
}
Command::Whitelist { telegram_id: target } => {
if !admin_ids.contains(&user_id) {
bot.send_message(chat_id, "You are not admin.").await?;
return Ok(());
}
if let Err(e) = db.set_whitelist(target, true).await {
bot.send_message(chat_id, format!("Error whitelisting: {}", e)).await?;
} else {
bot.send_message(chat_id, format!("User {} whitelisted.", target)).await?;
}
}
Command::Unwhitelist { telegram_id: target } => {
if !admin_ids.contains(&user_id) {
bot.send_message(chat_id, "You are not admin.").await?;
return Ok(());
}
if let Err(e) = db.set_whitelist(target, false).await {
bot.send_message(chat_id, format!("Error removing whitelist: {}", e)).await?;
} else {
bot.send_message(chat_id, format!("User {} unwhitelisted.", target)).await?;
}
}
}
Ok(())
}
// Placeholder encryption/decryption. In real use, implement AES or other.
fn encrypt_key(raw: &str) -> Result<String> {
// e.g. base64 or AES-encrypt with env key
Ok(base64::encode(raw))
}
fn decrypt_key(enc: &str) -> Result<String> {
let bytes = base64::decode(enc)?;
Ok(String::from_utf8(bytes)?)
}
// Placeholder derive address from private key/mnemonic: implement per-chain.
fn derive_address(key: &str) -> Result<String> {
// stub: in Solana, parse keypair and extract pubkey:
// ...
Ok("public_address_stub".to_string())
}

View File

@@ -0,0 +1,90 @@
use chrono::Local;
use serde_json::json;
use std::{
fs::OpenOptions,
io::Write,
sync::{Arc, Mutex},
};
const LOG_LEVEL: &str = "LOG";
#[derive(Clone)]
pub struct Logger {
prefix: String,
date_format: String,
file: Arc<Mutex<std::fs::File>>,
}
impl Logger {
// Constructor to create a new logger that writes to "logs.json"
pub fn new(prefix: String) -> Self {
let file = OpenOptions::new()
.create(true)
.append(true)
.open("logs.json")
.expect("Failed to open logs.json for writing");
Logger {
prefix,
date_format: String::from("%Y-%m-%d %H:%M:%S"),
file: Arc::new(Mutex::new(file)),
}
}
pub fn log(&self, message: String) -> String {
let full_log = format!("{} {}", self.prefix_with_date(), message);
println!("{}", full_log);
self.write_json("INFO", &message);
full_log
}
pub fn debug(&self, message: String) -> String {
let full_log = format!("{} [{}] {}", self.prefix_with_date(), "DEBUG", message);
if LogLevel::new().is_debug() {
println!("{}", full_log);
self.write_json("DEBUG", &message);
}
full_log
}
pub fn error(&self, message: String) -> String {
let full_log = format!("{} [{}] {}", self.prefix_with_date(), "ERROR", message);
println!("{}", full_log);
self.write_json("ERROR", &message);
full_log
}
fn prefix_with_date(&self) -> String {
let date = Local::now();
format!("[{}] {}", date.format(&self.date_format), self.prefix)
}
fn write_json(&self, level: &str, message: &str) {
let now = Local::now();
let entry = json!({
"timestamp": now.to_rfc3339(),
"level": level,
"prefix": self.prefix,
"message": message,
});
if let Ok(mut file) = self.file.lock() {
if let Err(e) = writeln!(file, "{}", entry) {
eprintln!("Failed to write to log file: {}", e);
}
}
}
}
struct LogLevel<'a> {
level: &'a str,
}
impl LogLevel<'_> {
fn new() -> Self {
LogLevel { level: LOG_LEVEL }
}
fn is_debug(&self) -> bool {
self.level.to_lowercase() == "debug"
}
}

View File

@@ -0,0 +1,3 @@
pub mod logger;
pub mod rpc;
pub mod utils;

View File

@@ -0,0 +1,131 @@
use anchor_lang::AccountDeserialize;
use anyhow::Result;
use base64::{prelude::BASE64_STANDARD, Engine};
use solana_account_decoder::UiAccountEncoding;
use solana_client::{
rpc_client::RpcClient,
rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig},
rpc_filter::RpcFilterType,
rpc_request::RpcRequest,
rpc_response::{RpcResult, RpcSimulateTransactionResult},
};
use solana_sdk::{
account::Account, commitment_config::CommitmentConfig, instruction::Instruction,
message::Message, pubkey::Pubkey, signature::Signature, signer::signers::Signers,
transaction::Transaction,
};
use solana_transaction_status::UiTransactionEncoding;
pub fn build_txn(
client: &RpcClient,
instructions: &[Instruction],
fee_payer: &Pubkey,
signing_keypairs: &dyn Signers,
) -> Result<Transaction> {
let blockhash = client.get_latest_blockhash().unwrap();
let message = Message::new_with_blockhash(instructions, Some(fee_payer), &blockhash);
let mut transaction = Transaction::new_unsigned(message);
transaction
.try_partial_sign(signing_keypairs, blockhash)
.unwrap();
Ok(transaction)
}
pub fn send_txn(client: &RpcClient, txn: &Transaction, skip_preflight: bool) -> Result<Signature> {
Ok(client.send_and_confirm_transaction_with_spinner_and_config(
txn,
CommitmentConfig::confirmed(),
RpcSendTransactionConfig {
skip_preflight,
..RpcSendTransactionConfig::default()
},
)?)
}
pub fn simulate_transaction(
client: &RpcClient,
transaction: &Transaction,
sig_verify: bool,
cfg: CommitmentConfig,
) -> RpcResult<RpcSimulateTransactionResult> {
let serialized = bincode::serialize(transaction)
.map_err(|e| (format!("Serialization failed: {e}")))
.unwrap();
let serialized_encoded = BASE64_STANDARD.encode(serialized);
println!("{}", serialized_encoded);
client.send(
RpcRequest::SimulateTransaction,
serde_json::json!([serialized_encoded, {
"sigVerify": sig_verify, "commitment": cfg.commitment, "encoding": Some(UiTransactionEncoding::Base64)
}]),
)
}
pub fn send_without_confirm_txn(client: &RpcClient, txn: &Transaction) -> Result<Signature> {
Ok(client.send_transaction_with_config(
txn,
RpcSendTransactionConfig {
skip_preflight: true,
..RpcSendTransactionConfig::default()
},
)?)
}
pub fn get_account(client: &RpcClient, addr: &Pubkey) -> Result<Option<Vec<u8>>> {
if let Some(account) = client
.get_account_with_commitment(addr, CommitmentConfig::processed())?
.value
{
let account_data = account.data;
Ok(Some(account_data))
} else {
Ok(None)
}
}
pub fn get_anchor_account<T: AccountDeserialize>(
client: &RpcClient,
addr: &Pubkey,
) -> Result<Option<T>> {
if let Some(account) = client
.get_account_with_commitment(addr, CommitmentConfig::processed())?
.value
{
let mut data: &[u8] = &account.data;
let ret = T::try_deserialize(&mut data).unwrap();
Ok(Some(ret))
} else {
Ok(None)
}
}
pub fn get_multiple_accounts(
client: &RpcClient,
pubkeys: &[Pubkey],
) -> Result<Vec<Option<Account>>> {
Ok(client.get_multiple_accounts(pubkeys)?)
}
pub fn get_program_accounts_with_filters(
client: &RpcClient,
program: Pubkey,
filters: Option<Vec<RpcFilterType>>,
) -> Result<Vec<(Pubkey, Account)>> {
let accounts = client
.get_program_accounts_with_config(
&program,
RpcProgramAccountsConfig {
filters,
account_config: RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64Zstd),
..RpcAccountInfoConfig::default()
},
with_context: Some(false),
sort_results: None, // <-- add this line
},
)
.unwrap();
Ok(accounts)
}

View File

@@ -0,0 +1,107 @@
// utils.rs
use crate::engine::swap::SwapDirection;
use anyhow::Result;
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair};
use std::{env, sync::Arc};
#[derive(Clone)]
pub struct AppState {
pub rpc_client: Arc<solana_client::rpc_client::RpcClient>,
pub rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
pub wallet: Arc<Keypair>,
}
pub struct ParseTx {
pub type_tx: String,
pub direction: Option<String>,
pub amount_in: f64,
pub amount_out: f64,
pub mint: String,
}
pub fn import_env_var(key: &str) -> String {
env::var(key).unwrap_or_else(|_| panic!("Environment variable {} is not set", key))
}
pub fn create_rpc_client() -> Result<Arc<solana_client::rpc_client::RpcClient>> {
let rpc_https = import_env_var("RPC_ENDPOINT");
let rpc_client = solana_client::rpc_client::RpcClient::new_with_commitment(
rpc_https,
CommitmentConfig::processed(),
);
Ok(Arc::new(rpc_client))
}
pub async fn create_nonblocking_rpc_client(
) -> Result<Arc<solana_client::nonblocking::rpc_client::RpcClient>> {
let rpc_https = import_env_var("RPC_ENDPOINT");
let rpc_client = solana_client::nonblocking::rpc_client::RpcClient::new_with_commitment(
rpc_https,
CommitmentConfig::processed(),
);
Ok(Arc::new(rpc_client))
}
pub fn import_wallet() -> Result<Arc<Keypair>> {
let priv_key = import_env_var("PRIVATE_KEY");
let wallet: Keypair = Keypair::from_base58_string(priv_key.as_str());
Ok(Arc::new(wallet))
}
#[derive(Copy, Clone, Debug, Default)]
pub enum ComputeUnitLimits {
#[default]
Dynamic,
Fixed(u64),
}
#[derive(Copy, Clone, Debug)]
pub enum PriorityFeeConfig {
DynamicMultiplier(u64),
FixedCuPrice(u64),
JitoTip(u64),
}
#[derive(Clone, Debug)]
pub struct SwapConfig {
pub priority_fee: Option<PriorityFeeConfig>,
pub cu_limits: Option<ComputeUnitLimits>,
pub wrap_and_unwrap_sol: Option<bool>,
pub as_legacy_transaction: Option<bool>,
pub slippage: u64,
pub swap_direction: SwapDirection,
pub use_jito: bool,
pub use_priority_tip: bool,
}
#[derive(Clone, Debug, Default)]
pub struct SwapConfigOverrides {
pub priority_fee: Option<PriorityFeeConfig>,
pub cu_limits: Option<ComputeUnitLimits>,
pub wrap_and_unwrap_sol: Option<bool>,
pub destination_token_account: Option<Pubkey>,
pub as_legacy_transaction: Option<bool>,
}
#[derive(Copy, Clone, Debug)]
pub struct SwapInput {
pub input_token_mint: Pubkey,
pub output_token_mint: Pubkey,
pub slippage_bps: u16,
pub amount: u64,
pub mode: SwapExecutionMode,
pub market: Option<Pubkey>,
pub creator_vault: Option<Pubkey>,
}
#[derive(Copy, Clone, Debug)]
pub enum SwapExecutionMode {
ExactIn,
ExactOut,
}
impl SwapExecutionMode {
pub fn amount_specified_is_input(&self) -> bool {
matches!(self, SwapExecutionMode::ExactIn)
}
}

View File

@@ -0,0 +1,2 @@
pub mod token;
pub mod tx;

View File

@@ -0,0 +1,117 @@
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use spl_token_2022::{
extension::StateWithExtensionsOwned,
state::{Account, Mint},
};
use spl_token_client::{
client::{ProgramClient, ProgramRpcClient, ProgramRpcClientSendTransaction},
token::{Token, TokenError, TokenResult},
};
use std::sync::Arc;
use tracing::info;
pub async fn get_token_balance(
client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
keypair: Arc<Keypair>,
mint: &Pubkey,
owner: &Pubkey,
) -> Result<u64, TokenError> {
let ata = get_associated_token_address(client.clone(), keypair.clone(), mint, owner);
info!(
"🔎 Fetching token balance for ATA: {} | Mint: {} | Owner: {}",
ata, mint, owner
);
match get_account_info(client.clone(), keypair, mint, &ata).await {
Ok(account_info) => {
info!(
"✅ Token balance found: {} tokens (raw amount)",
account_info.base.amount
);
Ok(account_info.base.amount)
}
Err(e) => {
info!("❌ Failed to fetch token balance for ATA {}: {:?}", ata, e);
Err(e)
}
}
}
pub fn get_associated_token_address(
client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
keypair: Arc<Keypair>,
address: &Pubkey,
owner: &Pubkey,
) -> Pubkey {
let token_client = Token::new(
Arc::new(ProgramRpcClient::new(
client.clone(),
ProgramRpcClientSendTransaction,
)),
&spl_token::ID,
address,
None,
Arc::new(Keypair::from_bytes(&keypair.to_bytes()).expect("failed to copy keypair")),
);
token_client.get_associated_token_address(owner)
}
pub async fn get_account_info(
client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
_keypair: Arc<Keypair>,
address: &Pubkey,
account: &Pubkey,
) -> TokenResult<StateWithExtensionsOwned<Account>> {
let program_client = Arc::new(ProgramRpcClient::new(
client.clone(),
ProgramRpcClientSendTransaction,
));
let account = program_client
.get_account(*account)
.await
.map_err(TokenError::Client)?
.ok_or(TokenError::AccountNotFound)
.inspect_err(|err| println!("get_account_info: {} {}: mint {}", account, err, address))?;
if account.owner != spl_token::ID {
return Err(TokenError::AccountInvalidOwner);
}
let account = StateWithExtensionsOwned::<Account>::unpack(account.data)?;
if account.base.mint != *address {
return Err(TokenError::AccountInvalidMint);
}
Ok(account)
}
pub async fn get_mint_info(
client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
_keypair: Arc<Keypair>,
address: &Pubkey,
) -> TokenResult<StateWithExtensionsOwned<Mint>> {
let program_client = Arc::new(ProgramRpcClient::new(
client.clone(),
ProgramRpcClientSendTransaction,
));
let account = program_client
.get_account(*address)
.await
.map_err(TokenError::Client)?
.ok_or(TokenError::AccountNotFound)
.inspect_err(|err| println!("{} {}: mint {}", address, err, address))?;
if account.owner != spl_token::ID {
return Err(TokenError::AccountInvalidOwner);
}
let mint_result = StateWithExtensionsOwned::<Mint>::unpack(account.data).map_err(Into::into);
let decimals: Option<u8> = None;
if let (Ok(mint), Some(decimals)) = (&mint_result, decimals) {
if decimals != mint.base.decimals {
return Err(TokenError::InvalidDecimals);
}
}
mint_result
}

View File

@@ -0,0 +1,224 @@
// tx.rs
use crate::{
common::{logger::Logger, rpc},
services::{jito, nextblock},
};
use anyhow::{anyhow, Result};
use base64;
use once_cell::sync::Lazy;
use reqwest::Client as HttpClient;
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
compute_budget::ComputeBudgetInstruction, hash::Hash, instruction::Instruction,
signature::Keypair, signer::Signer, system_instruction, transaction::Transaction,
};
use spl_token::ui_amount_to_amount;
use std::{env, sync::Arc, time::Duration};
use tokio::{spawn, sync::Mutex, time::Instant};
//——————————————————————————————————————————————————
// 1) One global HTTP client for all block‐engine calls
//——————————————————————————————————————————————————
static HTTP_CLIENT: Lazy<HttpClient> = Lazy::new(|| {
HttpClient::builder()
.pool_max_idle_per_host(0)
.build()
.expect("failed to build HTTP client")
});
//——————————————————————————————————————————————————
// 2) Cached blockhash, refreshed every 500 ms in the background
//——————————————————————————————————————————————————
pub static LATEST_BLOCKHASH: Lazy<Mutex<Hash>> = Lazy::new(|| Mutex::new(Hash::default()));
/// Spawn this once at startup to keep our blockhash fresh.
pub fn spawn_blockhash_refresher(rpc: Arc<RpcClient>) {
spawn(async move {
loop {
if let Ok(hash) = rpc.get_latest_blockhash() {
*LATEST_BLOCKHASH.lock().await = hash;
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
});
}
//——————————————————————————————————————————————————
// 3) One-time init of tip account & tip value (Jito vs NextBlock)
//——————————————————————————————————————————————————
static TIP_ACCOUNT: Lazy<Mutex<solana_sdk::pubkey::Pubkey>> =
Lazy::new(|| Mutex::new(solana_sdk::pubkey::Pubkey::default()));
static TIP_VALUE: Lazy<Mutex<f64>> = Lazy::new(|| Mutex::new(0.0));
/// Call this **once** at startup (before any swaps).
/// `use_nextblock` comes from e.g. `env("BLOCK_ENGINE_PROVIDER") == "nextblock"`,
/// `use_priority_tip` from your config.
pub async fn init_tip_state(use_nextblock: bool, use_priority_tip: bool) -> Result<()> {
if use_nextblock {
nextblock::init_tip_accounts().await?;
let base = nextblock::get_tip_value().await?;
let mut acct = TIP_ACCOUNT.lock().await;
let mut val = TIP_VALUE.lock().await;
*acct = nextblock::get_tip_account().await?;
*val = if use_priority_tip { base * 2.0 } else { base };
} else {
jito::init_tip_accounts().await?;
let base = jito::get_tip_value().await?;
let mut acct = TIP_ACCOUNT.lock().await;
let mut val = TIP_VALUE.lock().await;
*acct = jito::get_tip_account().await?;
*val = if use_priority_tip { base * 5.0 } else { base };
}
Ok(())
}
//——————————————————————————————————————————————————
// 4) Pre-built compute-budget instructions
//——————————————————————————————————————————————————
fn unit_price() -> u64 {
env::var("UNIT_PRICE")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(1)
}
fn unit_limit() -> u32 {
env::var("UNIT_LIMIT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(300_000)
}
static BUDGET_INSTRUCTIONS: Lazy<Vec<Instruction>> = Lazy::new(|| {
vec![
ComputeBudgetInstruction::set_compute_unit_price(unit_price()),
ComputeBudgetInstruction::set_compute_unit_limit(unit_limit()),
]
});
//——————————————————————————————————————————————————
// 5) The refactored send‐tx function (handles RPC, Jito & NextBlock)
//——————————————————————————————————————————————————
#[allow(deprecated, unused_variables)]
pub async fn new_signed_and_send(
client: &RpcClient,
keypair: &Keypair,
mut instructions: Vec<Instruction>,
use_priority_engine: bool, // e.g. swap_config.use_jito
use_priority_tip: bool, // e.g. swap_config.use_priority_tip
logger: &Logger,
) -> Result<Vec<String>> {
// a) If *not* using a block‐engine, prepend compute‐budget ixs
if !use_priority_engine {
instructions.splice(0..0, BUDGET_INSTRUCTIONS.iter().cloned());
}
// b) Grab our cached blockhash
let blockhash = *LATEST_BLOCKHASH.lock().await;
// c) Dispatch
let start = Instant::now();
let mut sigs = Vec::new();
if use_priority_engine {
// i) build tip‐transfer ix
let tip_account = *TIP_ACCOUNT.lock().await;
let tip_value = *TIP_VALUE.lock().await;
let lamports = ui_amount_to_amount(tip_value.min(0.1), spl_token::native_mint::DECIMALS);
let tip_ix = system_instruction::transfer(&keypair.pubkey(), &tip_account, lamports);
// ii) weave it into a fresh txn
let mut pe_ixs = Vec::with_capacity(instructions.len() + 1);
pe_ixs.push(tip_ix);
pe_ixs.extend(instructions);
let pe_tx = Transaction::new_signed_with_payer(
&pe_ixs,
Some(&keypair.pubkey()),
&[keypair],
blockhash,
);
// iii) encode + HTTP‐POST to NextBlock or Jito
let encoded = base64::encode(bincode::serialize(&pe_tx)?);
let payload = json!({
"transaction": { "content": encoded },
"frontRunningProtection": true,
});
let engine_is_nextblock = env::var("BLOCK_ENGINE_PROVIDER")
.unwrap_or_default()
.to_lowercase()
== "nextblock";
if engine_is_nextblock {
// — NextBlock —
let auth = env::var("NEXTBLOCK_AUTH").unwrap_or_default();
let resp = HTTP_CLIENT
.post("https://ny.nextblock.io/api/v2/submit")
.header("Content-Type", "application/json")
.header("Authorization", auth)
.json(&payload)
.send()
.await?;
if !resp.status().is_success() {
return Err(anyhow!(
"NextBlock error {}: {}",
resp.status(),
resp.text().await?
));
}
let body = resp.json::<serde_json::Value>().await?;
if let Some(sig) = body.get("signature").and_then(|v| v.as_str()) {
sigs.push(sig.to_string());
} else {
return Err(anyhow!("Missing signature in NextBlock response"));
}
} else {
// — Jito —
let url = format!("{}/api/v1/transactions", *jito::BLOCK_ENGINE_URL);
let rpc_resp = HTTP_CLIENT
.post(&url)
.header("Content-Type", "application/json")
.json(&json!({
"id": 1,
"jsonrpc": "2.0",
"method": "sendTransaction",
"params": [encoded, { "encoding": "base64" }]
}))
.send()
.await?;
if !rpc_resp.status().is_success() {
return Err(anyhow!(
"Jito error {}: {}",
rpc_resp.status(),
rpc_resp.text().await?
));
}
let body = rpc_resp.json::<serde_json::Value>().await?;
if let Some(sig) = body.get("result").and_then(|v| v.as_str()) {
sigs.push(sig.to_string());
} else {
return Err(anyhow!("Missing signature in Jito response"));
}
}
logger.log(format!("✅ Sent via block‐engine in {:?}", start.elapsed()));
} else {
// — standard RPC path —
let std_tx = Transaction::new_signed_with_payer(
&instructions,
Some(&keypair.pubkey()),
&[keypair],
blockhash,
);
let sig = rpc::send_txn(client, &std_tx, true)?;
sigs.push(sig.to_string());
logger.log("✅ Sent via standard RPC".into());
}
Ok(sigs)
}

View File

@@ -0,0 +1,170 @@
// src/db.rs
use mongodb::{Client, Collection, options::ClientOptions};
use mongodb::bson::{doc, oid::ObjectId};
use crate::models::{UserRecord, WalletEntry};
use anyhow::{Result, Context};
use futures::TryStreamExt;
pub struct Database {
users: Collection<UserRecord>,
}
impl Database {
/// Initialize MongoDB client and get `users` collection.
pub async fn new(mongo_uri: &str, db_name: &str) -> Result<Self> {
let mut client_options = ClientOptions::parse(mongo_uri).await?;
client_options.app_name = Some("TelegramWalletBot".to_string());
let client = Client::with_options(client_options)?;
let db = client.database(db_name);
let users = db.collection::<UserRecord>("users");
// Ensure index on telegram_id
users.create_index(
mongodb::IndexModel::builder()
.keys(doc! { "telegram_id": 1 })
.options(Some(mongodb::options::IndexOptions::builder().unique(true).build()))
.build(),
None
).await?;
Ok(Self { users })
}
/// Fetch or create a user record. If not exist, create with is_whitelisted=false.
pub async fn get_or_create_user(&self, telegram_id: i64) -> Result<UserRecord> {
if let Some(user) = self.users
.find_one(doc! { "telegram_id": telegram_id }, None)
.await?
{
Ok(user)
} else {
let new = UserRecord {
id: ObjectId::new(),
telegram_id,
is_whitelisted: false,
wallets: Vec::new(),
trading_wallet_id: None,
min_tokens_to_buy: None,
slippage_pct: None,
tip_amount: None,
};
self.users.insert_one(&new, None).await?;
Ok(new)
}
}
/// Check whitelist
pub async fn is_whitelisted(&self, telegram_id: i64) -> Result<bool> {
if let Some(user) = self.users.find_one(doc! { "telegram_id": telegram_id }, None).await? {
Ok(user.is_whitelisted)
} else {
Ok(false)
}
}
/// Set whitelist status (admin only).
pub async fn set_whitelist(&self, telegram_id: i64, status: bool) -> Result<()> {
let filter = doc! { "telegram_id": telegram_id };
let update = doc! { "$set": { "is_whitelisted": status } };
self.users.update_one(filter, update, None).await?;
Ok(())
}
/// Add a wallet (limit 5). Returns error if >5.
pub async fn add_wallet(&self, telegram_id: i64, name: String, address: String, private_enc: String) -> Result<WalletEntry> {
// Load user
let mut user = self.get_or_create_user(telegram_id).await?;
if user.wallets.len() >= 5 {
anyhow::bail!("wallet limit reached (5)");
}
// Create entry
let entry = WalletEntry {
id: ObjectId::new(),
name,
address,
private_key_enc: private_enc,
};
user.wallets.push(entry.clone());
// Update in DB
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$set": { "wallets": bson::to_bson(&user.wallets)? } },
None
).await?;
Ok(entry)
}
/// List wallets
pub async fn list_wallets(&self, telegram_id: i64) -> Result<Vec<WalletEntry>> {
if let Some(user) = self.users.find_one(doc! { "telegram_id": telegram_id }, None).await? {
Ok(user.wallets)
} else {
Ok(Vec::new())
}
}
/// Set trading wallet by wallet id
pub async fn set_trading_wallet(&self, telegram_id: i64, wallet_id: ObjectId) -> Result<()> {
// Verify wallet belongs to user
if let Some(user) = self.users.find_one(doc! { "telegram_id": telegram_id }, None).await? {
if user.wallets.iter().any(|w| w.id == wallet_id) {
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$set": { "trading_wallet_id": wallet_id } },
None
).await?;
Ok(())
} else {
anyhow::bail!("wallet not found");
}
} else {
anyhow::bail!("user not found");
}
}
/// Update a setting (min_tokens_to_buy, slippage_pct, tip_amount)
pub async fn update_setting_min_tokens(&self, telegram_id: i64, min_tokens: f64) -> Result<()> {
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$set": { "min_tokens_to_buy": min_tokens } },
None
).await?;
Ok(())
}
pub async fn update_setting_slippage(&self, telegram_id: i64, slippage_pct: f64) -> Result<()> {
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$set": { "slippage_pct": slippage_pct } },
None
).await?;
Ok(())
}
pub async fn update_setting_tip(&self, telegram_id: i64, tip: u64) -> Result<()> {
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$set": { "tip_amount": tip } },
None
).await?;
Ok(())
}
/// Remove wallet by id
pub async fn remove_wallet(&self, telegram_id: i64, wallet_id: ObjectId) -> Result<()> {
// Pull from array
self.users.update_one(
doc! { "telegram_id": telegram_id },
doc! { "$pull": { "wallets": { "id": wallet_id } } },
None
).await?;
// If trading_wallet_id was this, clear it
self.users.update_one(
doc! { "telegram_id": telegram_id, "trading_wallet_id": wallet_id },
doc! { "$set": { "trading_wallet_id": bson::Bson::Null } },
None
).await?;
Ok(())
}
/// Fetch user record
pub async fn get_user(&self, telegram_id: i64) -> Result<Option<UserRecord>> {
Ok(self.users.find_one(doc! { "telegram_id": telegram_id }, None).await?)
}
}

View File

@@ -0,0 +1,146 @@
pub const TEN_THOUSAND: u64 = 10000;
pub const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
pub const RENT_PROGRAM: &str = "SysvarRent111111111111111111111111111111111";
pub const ASSOCIATED_TOKEN_PROGRAM: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
pub const PUMP_GLOBAL: &str = "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf";
pub const PUMP_FEE_RECIPIENT: &str = "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM";
pub const PUMP_PROGRAM: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
// pub const PUMP_FUN_MINT_AUTHORITY: &str = "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM";
pub const PUMP_ACCOUNT: &str = "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1";
pub const PUMP_BUY_METHOD: u64 = 16927863322537952870;
pub const PUMP_SELL_METHOD: u64 = 12502976635542562355;
pub struct Pump {
pub rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
pub keypair: Arc<Keypair>,
pub rpc_client: Option<Arc<solana_client::rpc_client::RpcClient>>,
}
impl Pump {
pub fn new(
rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
keypair: Arc<Keypair>,
) -> Self {
Self {
rpc_nonblocking_client,
keypair,
rpc_client: Some(rpc_client),
}
}
pub async fn swap(&self, mint: &str, swap_config: SwapConfig) -> Result<Vec<String>> {
let logger = Logger::new("[SWAP IN PUMP.FUN] => ".to_string());
let slippage_bps = swap_config.slippage * 100;
let owner = self.keypair.pubkey();
let mint =
Pubkey::from_str(mint).map_err(|e| anyhow!("failed to parse mint pubkey: {}", e))?;
let program_id = spl_token::ID;
let native_mint = spl_token::native_mint::ID;
let (token_in, token_out, pump_method) = match swap_config.swap_direction {
SwapDirection::Buy => (native_mint, mint, PUMP_BUY_METHOD),
SwapDirection::Sell => (mint, native_mint, PUMP_SELL_METHOD),
};
let pump_program = Pubkey::from_str(PUMP_PROGRAM)?;
let (bonding_curve, associated_bonding_curve, bonding_curve_account) =
get_bonding_curve_account(self.rpc_client.clone().unwrap(), &mint, &pump_program)
.await?;
let in_ata = token::get_associated_token_address(
self.rpc_nonblocking_client.clone(),
self.keypair.clone(),
&token_in,
&owner,
);
let out_ata = token::get_associated_token_address(
self.rpc_nonblocking_client.clone(),
self.keypair.clone(),
&token_out,
&owner,
);
let mut create_instruction = None;
let mut close_instruction = None;
tx::new_signed_and_send(
&client,
&self.keypair,
instructions,
swap_config.use_jito,
&logger,
)
.await
}
}
impl Raydium {
pub fn new(
rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
keypair: Arc<Keypair>,
) -> Self {
Self {
rpc_nonblocking_client,
keypair,
rpc_client: Some(rpc_client),
pool_id: None,
}
}
pub async fn swap(
&self,
swap_config: SwapConfig,
amm_pool_id: Pubkey,
pool_state: AmmInfo,
) -> Result<Vec<String>> {
let logger = Logger::new(format!(
"[SWAP IN RAYDIUM]({}) => ",
chrono::Utc::now().timestamp()
));
let slippage_bps = swap_config.slippage * 100;
let owner = self.keypair.pubkey();
let program_id = spl_token::ID;
let native_mint = spl_token::native_mint::ID;
let mint = pool_state.coin_vault_mint;
let (token_in, token_out, user_input_token, swap_base_in) = match (
swap_config.swap_direction.clone(),
pool_state.coin_vault_mint == native_mint,
) {
(SwapDirection::Buy, true) => (native_mint, mint, pool_state.coin_vault, true),
(SwapDirection::Buy, false) => (native_mint, mint, pool_state.pc_vault, true),
(SwapDirection::Sell, true) => (mint, native_mint, pool_state.pc_vault, true),
(SwapDirection::Sell, false) => (mint, native_mint, pool_state.coin_vault, true),
};
logger.log(format!(
"token_in:{}, token_out:{}, user_input_token:{}, swap_base_in:{}",
token_in, token_out, user_input_token, swap_base_in
));
let in_ata = get_associated_token_address(
self.rpc_nonblocking_client.clone(),
self.keypair.clone(),
&token_in,
&owner,
);
let out_ata = get_associated_token_address(
self.rpc_nonblocking_client.clone(),
self.keypair.clone(),
&token_out,
&owner,
);
let mut create_instruction = None;
let mut close_instruction = None;
tx::new_signed_and_send(
&client,
&self.keypair,
instructions,
swap_config.use_jito,
&logger,
)
.await
}
}

View File

@@ -0,0 +1 @@
pub mod pump_fun;

View File

@@ -0,0 +1,470 @@
// pump_fun.rs
use crate::common::utils::SwapInput;
use crate::{
common::{logger::Logger, utils::SwapConfig},
core::tx,
engine::swap::SwapDirection,
};
use anyhow::{anyhow, Result};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use shared_state::BoughtTokenInfo;
use shared_state::BOUGHT_TOKENS;
use shared_state::CURRENT_SLOT;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
system_program,
};
use spl_associated_token_account::get_associated_token_address;
use spl_associated_token_account::instruction::create_associated_token_account as create_ata;
use std::collections::HashSet;
use std::sync::atomic::Ordering;
use std::time::Instant;
use std::{str::FromStr, sync::Arc, time::Duration};
use tokio::sync::Mutex;
use tracing::info;
// Define the 8-byte discriminator for the pump swap instruction.
// (These bytes must match what the on‑chain program expects.)
const PUMP_SWAP_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
pub const TEN_THOUSAND: u64 = 10000;
pub const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
pub const RENT_PROGRAM: &str = "SysvarRent111111111111111111111111111111111";
pub const ASSOCIATED_TOKEN_PROGRAM: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
pub const PUMP_GLOBAL: &str = "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf";
pub const PUMP_FEE_RECIPIENT: &str = "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM";
pub const PUMP_PROGRAM: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
// pub const PUMP_FUN_MINT_AUTHORITY: &str = "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM";
pub const PUMP_ACCOUNT: &str = "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1";
pub const PUMP_BUY_METHOD: u64 = 16927863322537952870;
pub const PUMP_SELL_METHOD: u64 = 12502976635542562355;
/// This struct will be serialized (after the discriminator) as the instruction data.
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct PumpBuyInstruction {
/// The minimum number of tokens the buyer expects to receive.
pub amount: u64,
/// The maximum SOL (in lamports) the buyer is willing to spend.
pub max_sol_cost: u64,
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct PumpSellInstruction {
/// Amount of tokens to sell
pub amount: u64,
/// Minimum SOL to receive
pub min_sol_output: u64,
}
pub struct Pump {
pub rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
pub keypair: Arc<Keypair>,
pub rpc_client: Option<Arc<solana_client::rpc_client::RpcClient>>,
created_atas: Arc<Mutex<HashSet<Pubkey>>>,
}
impl Pump {
pub fn new(
rpc_nonblocking_client: Arc<solana_client::nonblocking::rpc_client::RpcClient>,
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
keypair: Arc<Keypair>,
) -> Self {
Self {
rpc_nonblocking_client,
keypair,
rpc_client: Some(rpc_client),
created_atas: Arc::new(Mutex::new(HashSet::new())),
}
}
pub async fn swap(
&self,
mint: &str,
swap_config: SwapConfig,
swap_input: SwapInput,
amount_in_lamports: u64,
) -> Result<Vec<String>> {
match swap_config.swap_direction {
SwapDirection::Buy => {
self.swap_buy(mint, swap_config, &swap_input, amount_in_lamports)
.await
}
SwapDirection::Sell => self.swap_sell(mint, swap_config).await,
}
}
async fn swap_buy(
&self,
_mint: &str,
swap_config: SwapConfig,
swap_input: &SwapInput,
amount_in_lamports: u64,
) -> Result<Vec<String>> {
let creator_vault = swap_input
.creator_vault
.ok_or_else(|| anyhow!("creator_vault not provided in SwapInput"))?;
// still need a Logger for the lower‐level TX helper
let logger = Logger::new("[BUY SWAP] => ".to_string());
let owner = self.keypair.pubkey();
let mint_pubkey = swap_input.output_token_mint;
let pump_program = Pubkey::from_str(PUMP_PROGRAM)?;
// derive PDAs
let bonding_curve = get_pda(&mint_pubkey, &pump_program)?;
let associated_bonding_curve = get_associated_token_address(&bonding_curve, &mint_pubkey);
// derive ATA locally
let out_ata =
spl_associated_token_account::get_associated_token_address(&owner, &mint_pubkey);
// 1) ensure user ATA exists via cache
let mut instructions = Vec::new();
{
let cache = self.created_atas.lock().await;
if !cache.contains(&out_ata) {
instructions.push(create_ata(&owner, &owner, &mint_pubkey, &spl_token::ID));
}
}
// 2) build the buy instruction data
let min_tokens: u64 = std::env::var("MIN_TOKENS_EXPECTED")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8_000_000_000_000);
let mut data = PUMP_SWAP_DISCRIMINATOR.to_vec();
data.extend_from_slice(&to_vec(&PumpBuyInstruction {
amount: min_tokens,
max_sol_cost: amount_in_lamports,
})?);
// 3) assemble accounts in *exact* IDL order
let global = Pubkey::find_program_address(&[b"global"], &pump_program).0;
let fee_recipient = Pubkey::from_str(PUMP_FEE_RECIPIENT)?;
let event_authority = Pubkey::from_str(PUMP_ACCOUNT)?;
let accounts = vec![
AccountMeta::new_readonly(global, false), // 1
AccountMeta::new(fee_recipient, false), // 2
AccountMeta::new_readonly(mint_pubkey, false), // 3
AccountMeta::new(bonding_curve, false), // 4
AccountMeta::new(associated_bonding_curve, false), // 5
AccountMeta::new(out_ata, false), // 6
AccountMeta::new(owner, true), // 7
AccountMeta::new_readonly(system_program::ID, false), // 8
AccountMeta::new_readonly(spl_token::ID, false), // 9
AccountMeta::new(creator_vault, false), // 10
AccountMeta::new_readonly(event_authority, false), // 11
AccountMeta::new_readonly(pump_program, false), // 12
];
info!(
"⛓ Preparing BUY for {}: min_tokens={}, max_sol={}; accounts={:?}",
mint_pubkey,
min_tokens,
amount_in_lamports,
accounts
.iter()
.map(|m| m.pubkey.to_string())
.collect::<Vec<_>>()
);
// 4) push your swap instruction
instructions.push(Instruction {
program_id: pump_program,
accounts,
data,
});
// 5) dispatch
let tx_result = tx::new_signed_and_send(
self.rpc_client.as_ref().unwrap(),
&self.keypair,
instructions,
swap_config.use_jito,
swap_config.use_priority_tip,
&logger,
)
.await;
// 6) record on success
if let Ok(sigs) = &tx_result {
let buy_slot = CURRENT_SLOT.load(std::sync::atomic::Ordering::Relaxed);
let mut tokens = BOUGHT_TOKENS.lock().await;
let mut info = BoughtTokenInfo::new(
mint_pubkey.to_string(),
Some(out_ata.to_string()),
min_tokens,
Instant::now(),
sigs.first().cloned(),
buy_slot,
Some(creator_vault),
);
info.set_buy_executed_at(Instant::now());
tokens.insert(mint_pubkey.to_string(), info);
info!(
"✅ BUY sent for {}: sigs={:?} @ slot={}",
mint_pubkey, sigs, buy_slot
);
}
tx_result
}
async fn swap_sell(&self, mint: &str, swap_config: SwapConfig) -> Result<Vec<String>> {
// logger still used for the helper
let logger = Logger::new("[SELL SWAP] => ".to_string());
let owner = self.keypair.pubkey();
let mint_pubkey = Pubkey::from_str(mint)?;
let pump_program = Pubkey::from_str(PUMP_PROGRAM)?;
// derive PDAs
let global_pda = Pubkey::find_program_address(&[b"global"], &pump_program).0;
let fee_recipient = Pubkey::from_str(PUMP_FEE_RECIPIENT)?;
let bonding_curve = get_pda(&mint_pubkey, &pump_program)?;
let associated_bonding_curve = get_associated_token_address(&bonding_curve, &mint_pubkey);
let event_authority = Pubkey::from_str(PUMP_ACCOUNT)?;
// fetch stored info including creator_vault
let (in_ata, sell_amount, creator_vault) = {
let tokens = BOUGHT_TOKENS.lock().await;
let info = tokens
.get(mint)
.ok_or_else(|| anyhow!("Missing buy record for {}", mint))?;
let ata_str = info
.ata()
.ok_or_else(|| anyhow!("Missing ATA for {}", mint))?;
let tracked = info.amount();
let fallback = info.fallback_amount();
let sell_amount = tracked.max(fallback);
let vault = *info
.creator_vault()
.ok_or_else(|| anyhow!("Missing stored creator_vault for {}", mint))?;
(Pubkey::from_str(ata_str)?, sell_amount, vault)
};
// build instruction data
const PUMP_SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
let mut data = PUMP_SELL_DISCRIMINATOR.to_vec();
data.extend_from_slice(&to_vec(&PumpSellInstruction {
amount: sell_amount,
min_sol_output: 1_000,
})?);
// assemble accounts in IDL order
let accounts = vec![
AccountMeta::new_readonly(global_pda, false),
AccountMeta::new(fee_recipient, false),
AccountMeta::new_readonly(mint_pubkey, false),
AccountMeta::new(bonding_curve, false),
AccountMeta::new(associated_bonding_curve, false),
AccountMeta::new(in_ata, false),
AccountMeta::new(owner, true),
AccountMeta::new_readonly(system_program::ID, false),
AccountMeta::new(creator_vault, false),
AccountMeta::new_readonly(Pubkey::from_str(TOKEN_PROGRAM)?, false),
AccountMeta::new_readonly(event_authority, false),
AccountMeta::new_readonly(pump_program, false),
];
info!(
"⛓ Preparing SELL for {}: amount={}, creator_vault={}; accounts={:?}",
mint_pubkey,
sell_amount,
creator_vault,
accounts
.iter()
.map(|m| m.pubkey.to_string())
.collect::<Vec<_>>()
);
let ix = Instruction {
program_id: pump_program,
accounts,
data,
};
let mut last: Result<Vec<String>> = Err(anyhow!("no attempts"));
for attempt in 1..=3 {
let res = tx::new_signed_and_send(
self.rpc_client.as_ref().unwrap(),
&self.keypair,
vec![ix.clone()],
swap_config.use_jito,
swap_config.use_priority_tip,
&logger,
)
.await;
match res {
Ok(sigs) => {
let sell_slot = CURRENT_SLOT.load(Ordering::Relaxed);
info!(
"💰 SELL succeeded for {}: sigs={:?} @ slot={}",
mint_pubkey, sigs, sell_slot
);
// ** record the signature so your gRPC‐stream confirmation can see it **
let mut tokens = BOUGHT_TOKENS.lock().await;
if let Some(entry) = tokens.get_mut(&mint_pubkey.to_string()) {
entry.set_sell_signature(sigs[0].clone());
}
last = Ok(sigs);
break;
}
Err(err) => {
info!(
"❌ SELL attempt {} failed for {}: {}",
attempt, mint_pubkey, err
);
if attempt < 3 {
tokio::time::sleep(Duration::from_millis(600 * attempt)).await;
}
last = Err(err);
}
}
}
last
}
}
pub async fn wait_for_bonding_curve_account(
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
mint: &Pubkey,
program_id: &Pubkey,
max_retries: usize,
delay_ms: u64,
) -> Result<(Pubkey, Pubkey, BondingCurveAccount)> {
let bonding_curve = get_pda(mint, program_id)?;
let associated_bonding_curve = get_associated_token_address(&bonding_curve, mint);
for attempt in 0..max_retries {
match rpc_client.get_account_data(&bonding_curve) {
Ok(data) => {
let bonding_curve_account: BondingCurveAccount =
from_slice::<BondingCurveAccount>(&data).map_err(|e| {
anyhow!("Failed to deserialize bonding curve account: {}", e)
})?;
return Ok((
bonding_curve,
associated_bonding_curve,
bonding_curve_account,
));
}
Err(err) => {
if attempt == max_retries - 1 {
return Err(anyhow!(
"Bonding curve account not found after retries: {}",
err
));
}
tokio::time::sleep(Duration::from_millis(delay_ms)).await;
}
}
}
Err(anyhow!(
"Bonding curve account retry logic failed unexpectedly"
))
}
/// Derives the bonding curve PDA and its associated token account for a given mint.
pub async fn get_bonding_curve_account(
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
mint: &Pubkey,
program_id: &Pubkey,
) -> Result<(Pubkey, Pubkey, BondingCurveAccount)> {
let bonding_curve = get_pda(mint, program_id)?;
let associated_bonding_curve =
spl_associated_token_account::get_associated_token_address(&bonding_curve, mint);
let bonding_curve_data = rpc_client
.get_account_data(&bonding_curve)
.inspect_err(|err| {
println!(
"Failed to get bonding curve account data: {}, err: {}",
bonding_curve, err
);
})?;
let bonding_curve_account =
from_slice::<BondingCurveAccount>(&bonding_curve_data).map_err(|e| {
anyhow!(
"Failed to deserialize bonding curve account: {}",
e.to_string()
)
})?;
Ok((
bonding_curve,
associated_bonding_curve,
bonding_curve_account,
))
}
/// Derives the PDA for the bonding curve from the mint and program id.
pub fn get_pda(mint: &Pubkey, program_id: &Pubkey) -> Result<Pubkey> {
let seeds = [b"bonding-curve".as_ref(), mint.as_ref()];
let (bonding_curve, _bump) = Pubkey::find_program_address(&seeds, program_id);
Ok(bonding_curve)
}
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct BondingCurveAccount {
pub discriminator: u64,
pub virtual_token_reserves: u64,
pub virtual_sol_reserves: u64,
pub real_token_reserves: u64,
pub real_sol_reserves: u64,
pub token_total_supply: u64,
pub complete: bool,
pub creator: Pubkey,
}
/// Retrieves pump info for a given token mint.
pub async fn get_pump_info(
rpc_client: Arc<solana_client::rpc_client::RpcClient>,
mint: &str,
) -> Result<PumpInfo> {
let mint = Pubkey::from_str(mint)?;
let program_id = Pubkey::from_str(PUMP_PROGRAM)?;
let (bonding_curve, associated_bonding_curve, bonding_curve_account) =
get_bonding_curve_account(rpc_client, &mint, &program_id).await?;
let pump_info = PumpInfo {
mint: mint.to_string(),
bonding_curve: bonding_curve.to_string(),
associated_bonding_curve: associated_bonding_curve.to_string(),
raydium_pool: None,
raydium_info: None,
complete: bonding_curve_account.complete,
virtual_sol_reserves: bonding_curve_account.virtual_sol_reserves,
virtual_token_reserves: bonding_curve_account.virtual_token_reserves,
total_supply: bonding_curve_account.token_total_supply,
};
Ok(pump_info)
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RaydiumInfo {
pub base: f64,
pub quote: f64,
pub price: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PumpInfo {
pub mint: String,
pub bonding_curve: String,
pub associated_bonding_curve: String,
pub raydium_pool: Option<String>,
pub raydium_info: Option<RaydiumInfo>,
pub complete: bool,
pub virtual_sol_reserves: u64,
pub virtual_token_reserves: u64,
pub total_supply: u64,
}

View File

@@ -0,0 +1 @@
pub mod swap;

View File

@@ -0,0 +1,85 @@
// swap.rs
use crate::common::utils::{AppState, SwapConfig, SwapInput};
use crate::dex::pump_fun::Pump;
use anyhow::Result;
use clap::ValueEnum;
use serde::Deserialize;
#[derive(ValueEnum, Copy, Debug, Clone, Deserialize, PartialEq)]
pub enum SwapDirection {
#[serde(rename = "buy")]
Buy,
#[serde(rename = "sell")]
Sell,
}
impl From<SwapDirection> for u8 {
fn from(value: SwapDirection) -> Self {
match value {
SwapDirection::Buy => 0,
SwapDirection::Sell => 1,
}
}
}
#[derive(ValueEnum, Debug, Clone, Deserialize)]
pub enum SwapInType {
#[serde(rename = "qty")]
Qty,
#[serde(rename = "pct")]
Pct,
}
pub async fn pump_swap(
state: AppState,
swap_input: SwapInput,
swap_direction: &str,
use_jito: bool,
urgent: bool,
) -> Result<Vec<String>> {
if swap_input.amount == 0 {
return Err(anyhow::anyhow!(
"Skipping swap: provided amount is 0 lamports"
));
}
// Convert the provided swap_direction string to our enum.
let swap_direction_enum = match swap_direction {
"buy" => SwapDirection::Buy,
"sell" => SwapDirection::Sell,
_ => anyhow::bail!("Invalid swap direction"),
};
// Default to the provided slippage...
let mut slippage = swap_input.slippage_bps as u64;
// 🔧 Add more slippage for sells to prevent 6003 errors
if swap_direction_enum == SwapDirection::Sell {
slippage += 50; // Add 10bps extra
}
let swap_config = SwapConfig {
slippage,
swap_direction: swap_direction_enum,
use_jito,
use_priority_tip: urgent,
cu_limits: None,
priority_fee: None,
wrap_and_unwrap_sol: Some(true),
as_legacy_transaction: Some(false),
};
let swapx = Pump::new(state.rpc_nonblocking_client, state.rpc_client, state.wallet);
let mint = if swap_direction_enum == SwapDirection::Buy {
swap_input.output_token_mint.to_string()
} else {
swap_input.input_token_mint.to_string()
};
let amount_in = swap_input.amount;
let res = swapx
.swap(&mint, swap_config, swap_input, amount_in)
.await?;
Ok(res)
}

View File

@@ -0,0 +1,151 @@
pub async fn handle_buy_and_sell_logic(
txn: &ConfirmedTransactionWithStatusMeta,
sell_sender: &Sender<(String, u64)>,
) {
let logger = Logger::new("[BUY/SELL LOGIC] => ".to_string());
let our_pubkey = env::var("WALLET_PUBKEY").unwrap_or_default();
let min_inflow: f64 = env::var("MIN_SOL_INFLOW_TRIGGER").unwrap_or("0.5".to_string()).parse().unwrap_or(0.5);
let min_outflow: f64 = env::var("MIN_SOL_OUTFLOW_TRIGGER").unwrap_or("0.5".to_string()).parse().unwrap_or(0.5);
// Extract our on-chain slot for comparisons
let current_slot = CURRENT_SLOT.load(std::sync::atomic::Ordering::Relaxed);
let TransactionWithStatusMeta::Complete(vtx) = &txn.tx_with_meta else { return };
let Some(post_balances) = &vtx.meta.post_token_balances else { return };
let Some(pre_balances) = &vtx.meta.pre_token_balances else { return };
let mut tokens = BOUGHT_TOKENS.lock().await;
for (i, post_tb) in post_balances.iter().enumerate() {
if i >= pre_balances.len() { continue; }
let pre_tb = &pre_balances[i];
let mint = &post_tb.mint;
let owner = post_tb.owner.clone();
let ata = post_tb.account_index.to_string();
let pre_amount = pre_tb.ui_token_amount.ui_amount.unwrap_or(0.0);
let post_amount = post_tb.ui_token_amount.ui_amount.unwrap_or(0.0);
let delta = post_amount - pre_amount;
if let Some(entry) = tokens.get_mut(mint) {
if entry.buy_slot() == 0 {
continue;
}
if entry.sell_triggered_at().is_some() {
continue;
}
let is_from_our_ata = Some(ata.clone()) == entry.ata().cloned();
let is_owner_ours = owner == our_pubkey;
// Time to slot delta instead of wall-clock
let slot_delta = current_slot.saturating_sub(entry.buy_slot());
if delta > 0.0 && !is_owner_ours && !is_from_our_ata {
if entry.add_unique_buyer(owner.clone()) {
logger.log(format!(
"🧍 Unique buyer for {}: {} | Total: {}",
mint,
owner,
entry.unique_buyers().len()
));
if entry.unique_buyers().len() == 1 && entry.suspected_creator().is_none() {
entry.set_suspected_creator(owner.clone());
info!("{}", format!("🧠 Suspected creator flagged: {} on {}", owner, mint));
}
}
// Early rug detection: <2 buyers after X slots
if entry.unique_buyers().len() < 10 && slot_delta > 12 {
entry.set_sell_triggered(true);
entry.set_sell_triggered_at(Instant::now());
info!("{}", format!("🛑 Rug suspected — Forcing sell on {} after {} slots", mint, slot_delta));
if sell_sender.send((mint.clone(), entry.amount())).await.is_ok() {
info!("{}", format!("📤 Sell sent for {}", mint));
}
continue;
}
// Exhausted follow-ups
if entry.unique_buyers().len() >= 5 {
entry.set_sell_triggered(true);
entry.set_sell_triggered_at(Instant::now());
info!("{}", format!("📉 Follow-up exhaustion — Selling {} after 3 buyers", mint));
if sell_sender.send((mint.clone(), entry.amount())).await.is_ok() {
info!("{}", format!("📤 Sell sent for {}", mint));
}
continue;
}
}
// SOL inflow/outflow logic (unchanged)
if let Ok(pid) = Pubkey::from_str(PUMP_PROGRAM) {
if let Ok(bonding_curve) = get_pda(&Pubkey::from_str(mint).unwrap(), &pid) {
let message = &vtx.transaction.message;
let mut keys = match message {
VersionedMessage::Legacy(m) => m.account_keys.clone(),
VersionedMessage::V0(v0) => {
let mut k = v0.account_keys.clone();
k.extend(vtx.meta.loaded_addresses.writable.clone());
k.extend(vtx.meta.loaded_addresses.readonly.clone());
k
}
};
if let Some(idx) = keys.iter().position(|k| k == &bonding_curve) {
let pre_sol = vtx.meta.pre_balances[idx];
let post_sol = vtx.meta.post_balances[idx];
let delta_sol = (post_sol as i64 - pre_sol as i64) as f64 / 1e9;
logger.log(format!(
"🔍 Mint: {} | ΔSOL: {:.6} | Slot: {}", mint, delta_sol, txn.slot
));
if delta_sol > 0.0 {
entry.add_sol_inflow(delta_sol);
} else if delta_sol < 0.0 {
entry.add_sol_outflow(-delta_sol);
if slot_delta <= 1 {
entry.set_sell_triggered(true);
entry.set_sell_triggered_at(Instant::now());
info!("{}", format!("💣 Early SOL drain! Fast sell on {}", mint));
if sell_sender.send((mint.clone(), entry.amount())).await.is_ok() {
info!("{}", format!("📤 Sell sent for {}", mint));
}
continue;
}
}
if delta_sol < -min_outflow {
entry.set_sell_triggered(true);
entry.set_sell_triggered_at(Instant::now());
info!("{}", format!("💣 Outflow sell on {} | ΔSOL: {:.6}", mint, delta_sol));
if sell_sender.send((mint.clone(), entry.amount())).await.is_ok() {
info!("{}", format!("📤 Sell sent for {}", mint));
}
continue;
}
if entry.sol_inflow() >= min_inflow {
entry.set_sell_triggered(true);
entry.set_sell_triggered_at(Instant::now());
info!("{}", format!("🚀 Inflow sell on {} | Total {:.6} SOL", mint, entry.sol_inflow()));
let mint_c = mint.clone(); let amt = entry.amount(); let tx_s = sell_sender.clone();
let target_slot = current_slot + 1;
tokio::spawn(async move {
while CURRENT_SLOT.load(Ordering::Relaxed) < target_slot {
tokio::time::sleep(Duration::from_millis(10)).await;
}
if tx_s.send((mint_c.clone(), amt)).await.is_ok() {
info!("📤 Delayed sell sent for {}", mint_c);
}
});
}
}
}
}
}
}
}

View File

@@ -0,0 +1,81 @@
use crate::serialization::serialize_pubkey;
use serde::{Deserialize, Serialize};
use solana_program::{program_error::ProgramError, pubkey::Pubkey};
use solana_sdk::instruction::AccountMeta;
#[derive(Deserialize)]
struct IdlInstruction {
name: String,
accounts: Vec<IdlAccount>,
}
#[derive(Deserialize)]
struct IdlAccount {
name: String,
#[serde(rename = "isMut")]
is_mut: bool,
#[serde(rename = "isSigner")]
is_signer: bool,
}
#[derive(Deserialize)]
pub struct Idl {
instructions: Vec<IdlInstruction>,
}
#[derive(Debug, Serialize)]
pub struct AccountMetadata {
#[serde(serialize_with = "serialize_pubkey")]
pub pubkey: Pubkey,
pub is_writable: bool,
pub is_signer: bool,
pub name: String,
}
pub trait InstructionAccountMapper<'info> {
fn map_accounts<'me>(
&self,
accounts: &[AccountMeta],
instruction_name: &str,
) -> Result<Vec<AccountMetadata>, ProgramError>;
}
impl<'info> InstructionAccountMapper<'info> for Idl {
fn map_accounts<'me>(
&self,
accounts: &[AccountMeta],
instruction_name: &str,
) -> Result<Vec<AccountMetadata>, ProgramError> {
let instruction = self
.instructions
.iter()
.find(|ix| ix.name == instruction_name)
.ok_or(ProgramError::InvalidArgument)?;
let mut account_metadata: Vec<AccountMetadata> = accounts
.iter()
.take(instruction.accounts.len())
.enumerate()
.map(|(i, account)| {
let account_info = &instruction.accounts[i];
AccountMetadata {
pubkey: account.pubkey,
is_writable: account_info.is_mut,
is_signer: account_info.is_signer,
name: account_info.name.clone(),
}
})
.collect();
for (i, account) in accounts.iter().enumerate().skip(instruction.accounts.len()) {
account_metadata.push(AccountMetadata {
pubkey: account.pubkey,
is_writable: account.is_writable,
is_signer: account.is_signer,
name: format!("Remaining accounts {}", i - instruction.accounts.len() + 1),
});
}
Ok(account_metadata)
}
}

View File

@@ -0,0 +1,3 @@
pub fn run_latency(block_time_a : i64, block_time_b: i64) -> i64 {
block_time_a - block_time_b
}

View File

@@ -0,0 +1,14 @@
use solana_client::rpc_client::RpcClient;
use solana_client::client_error::ClientError;
use anyhow::Context;
const API_KEY: &str = "api_key";
pub fn get_latest_slot() -> anyhow::Result<u64> {
let rpc_url = format!("https://rpc.va.shyft.to?api_key={}", API_KEY);
let client = RpcClient::new(rpc_url);
client.get_slot().context("Failed to fetch latest slot")
}

View File

@@ -0,0 +1,9 @@
pub mod common;
pub mod core;
pub mod dex;
pub mod engine;
pub mod services;
pub mod trading_loop;
pub use trading_loop::get_notify_handle;
pub use trading_loop::start_trading_loop;

View File

@@ -0,0 +1,90 @@
use chrono::Local;
use serde_json::json;
use std::{
fs::OpenOptions,
io::Write,
sync::{Arc, Mutex},
};
const LOG_LEVEL: &str = "LOG";
#[derive(Clone)]
pub struct Logger {
prefix: String,
date_format: String,
file: Arc<Mutex<std::fs::File>>,
}
impl Logger {
// Constructor to create a new logger that writes to "logs.json"
pub fn new(prefix: String) -> Self {
let file = OpenOptions::new()
.create(true)
.append(true)
.open("detectionlogs.json")
.expect("Failed to open logs.json for writing");
Logger {
prefix,
date_format: String::from("%Y-%m-%d %H:%M:%S"),
file: Arc::new(Mutex::new(file)),
}
}
pub fn log(&self, message: String) -> String {
let full_log = format!("{} {}", self.prefix_with_date(), message);
println!("{}", full_log);
self.write_json("INFO", &message);
full_log
}
pub fn debug(&self, message: String) -> String {
let full_log = format!("{} [{}] {}", self.prefix_with_date(), "DEBUG", message);
if LogLevel::new().is_debug() {
println!("{}", full_log);
self.write_json("DEBUG", &message);
}
full_log
}
pub fn error(&self, message: String) -> String {
let full_log = format!("{} [{}] {}", self.prefix_with_date(), "ERROR", message);
println!("{}", full_log);
self.write_json("ERROR", &message);
full_log
}
fn prefix_with_date(&self) -> String {
let date = Local::now();
format!("[{}] {}", date.format(&self.date_format), self.prefix)
}
fn write_json(&self, level: &str, message: &str) {
let now = Local::now();
let entry = json!({
"timestamp": now.to_rfc3339(),
"level": level,
"prefix": self.prefix,
"message": message,
});
if let Ok(mut file) = self.file.lock() {
if let Err(e) = writeln!(file, "{}", entry) {
eprintln!("Failed to write to log file: {}", e);
}
}
}
}
struct LogLevel<'a> {
level: &'a str,
}
impl LogLevel<'_> {
fn new() -> Self {
LogLevel { level: LOG_LEVEL }
}
fn is_debug(&self) -> bool {
self.level.to_lowercase() == "debug"
}
}

View File

@@ -0,0 +1,157 @@
#[derive(Debug, Clone, Copy)]
pub enum DetectionStrategy {
TokenCreation,
FirstBuy,
SuspectedCreatorSell,
}
pub async fn handle_detection_event(
txn: &ConfirmedTransactionWithStatusMeta,
slot: u64,
strategy: DetectionStrategy,
mint_override: Option<String>, // Only used for creation strategy
) {
match strategy {
DetectionStrategy::TokenCreation => {
if let Some(mint) = mint_override {
let creation_time = current_unix_timestamp();
let mut creations = TOKEN_CREATIONS.lock().await;
creations.insert(mint.clone(), TokenCreationInfo {
mint,
creation_time,
});
}
}
DetectionStrategy::FirstBuy => {
if TOKEN_CREATIONS.lock().await.is_empty() {
return;
}
if let TransactionWithStatusMeta::Complete(versioned_tx_with_meta) = &txn.tx_with_meta {
if let (Some(pre_token_balances), Some(post_token_balances)) =
(&versioned_tx_with_meta.meta.pre_token_balances, &versioned_tx_with_meta.meta.post_token_balances)
{
for (i, post_tb) in post_token_balances.iter().enumerate() {
if let Some(post_ui) = post_tb.ui_token_amount.ui_amount {
if i >= pre_token_balances.len() { continue; }
let pre_tb = &pre_token_balances[i];
if let Some(pre_ui) = pre_tb.ui_token_amount.ui_amount {
let delta = post_ui - pre_ui;
if delta >= 0.0 { continue; }
let buy_amount = -delta;
let mint = pre_tb.mint.clone();
let buyer = pre_tb.owner.clone();
const DEFAULT_TOTAL_SUPPLY: f64 = SUPPLY_THRESHOLD_UI;
const MIN_BUY_FRACTION: f64 = 0.000001;
const MAX_BUY_FRACTION: f64 = 0.10;
if pre_ui < DEFAULT_TOTAL_SUPPLY * 0.98 { continue; }
if buy_amount < DEFAULT_TOTAL_SUPPLY * MIN_BUY_FRACTION
|| buy_amount > DEFAULT_TOTAL_SUPPLY * MAX_BUY_FRACTION
{
continue;
}
let mut creations = TOKEN_CREATIONS.lock().await;
if !creations.contains_key(&mint) {
println!("[EVENT] No creation event for {}; skipping first buy.", mint);
continue;
}
creations.remove(&mint);
drop(creations);
let mut map = TRACKED_TOKENS.lock().await;
if let Some(entry) = map.get_mut(&mint) {
if entry.suspected_creators.insert(buyer.clone()) {
entry.sell_split_ratio = entry.suspected_creators.len();
println!(
"[FOLLOW-UP CREATOR] Mint: {} | New suspected creator: {} | Total creators: {}",
mint, buyer, entry.sell_split_ratio
);
}
} else {
let mut suspected = HashSet::new();
suspected.insert(buyer.clone());
map.insert(mint.clone(), TokenTrackingState {
mint: mint.clone(),
first_buyer: Some(buyer.clone()),
first_buy_amount: buy_amount,
suspected_creators: suspected,
sell_detected: false,
sell_split_ratio: 0,
has_bought: false,
bought_amount: None,
has_sold: false,
open_trade_permit: None,
detected_buy_slot: Some(slot),
});
println!("[NEW TOKEN TRACKED] Mint: {} | First Buyer: {} | Buy Amount: {} | Slot: {}",
mint, buyer, buy_amount, slot);
log::info!("New token tracked: {} bought by {}", mint, buyer);
let notify = get_notify_handle().await;
notify.notify_one();
}
}
}
}
}
}
}
DetectionStrategy::SuspectedCreatorSell => {
if let TransactionWithStatusMeta::Complete(versioned_tx_with_meta) = &txn.tx_with_meta {
if let (Some(pre_token_balances), Some(post_token_balances)) =
(&versioned_tx_with_meta.meta.pre_token_balances, &versioned_tx_with_meta.meta.post_token_balances)
{
for (i, post_tb) in post_token_balances.iter().enumerate() {
if let Some(post_ui) = post_tb.ui_token_amount.ui_amount {
if i < pre_token_balances.len() {
let pre_tb = &pre_token_balances[i];
if let Some(pre_ui) = pre_tb.ui_token_amount.ui_amount {
let delta = post_ui - pre_ui;
if delta >= 0.0 { continue; }
let mint = pre_tb.mint.clone();
let seller = pre_tb.owner.clone();
let mut map = TRACKED_TOKENS.lock().await;
if let Some(entry) = map.get_mut(&mint) {
if !entry.has_bought || entry.bought_amount.unwrap_or(0) == 0 {
log::info!(
"[SELL IGNORED] Mint {}: Suspected creator {} sold but we haven't bought yet",
mint, seller
);
return;
}
if entry.suspected_creators.contains(&seller) && !entry.sell_detected {
entry.sell_detected = true;
entry.sell_split_ratio = entry.suspected_creators.len();
log::info!(
"[SELL DETECTED] Mint {}: Seller {} triggered sell | Total suspected creators: {}",
mint, seller, entry.sell_split_ratio
);
let notify = get_notify_handle().await;
notify.notify_one();
}
}
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
use solana_sdk::pubkey::Pubkey;
pub fn serialize_pubkey<S>(value: &Pubkey, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&value.to_string())
}
pub fn serialize_option_pubkey<S>(value: &Option<Pubkey>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match value {
Some(pubkey) => serializer.serialize_str(&pubkey.to_string()),
None => serializer.serialize_none(),
}
}

View File

@@ -0,0 +1,180 @@
use std::{future::Future, str::FromStr, sync::LazyLock, time::Duration};
use anyhow::{anyhow, Result};
use indicatif::{ProgressBar, ProgressStyle};
use rand::{seq::IteratorRandom, thread_rng};
use serde::Deserialize;
use serde_json::Value;
use solana_sdk::pubkey::Pubkey;
use tokio::{
sync::RwLock,
time::{sleep, Instant},
};
use crate::common::utils::import_env_var;
pub static BLOCK_ENGINE_URL: LazyLock<String> =
LazyLock::new(|| import_env_var("JITO_BLOCK_ENGINE_URL"));
pub static TIP_STREAM_URL: LazyLock<String> =
LazyLock::new(|| import_env_var("JITO_TIP_STREAM_URL"));
pub static TIP_PERCENTILE: LazyLock<String> =
LazyLock::new(|| import_env_var("JITO_TIP_PERCENTILE"));
pub static TIP_ACCOUNTS: LazyLock<RwLock<Vec<String>>> = LazyLock::new(|| RwLock::new(vec![]));
#[derive(Debug)]
pub struct TipAccountResult {
pub accounts: Vec<String>,
}
pub async fn init_tip_accounts() -> Result<()> {
let accounts = TipAccountResult {
accounts: vec![
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5".to_string(),
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe".to_string(),
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY".to_string(),
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49".to_string(),
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh".to_string(),
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt".to_string(),
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL".to_string(),
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT".to_string(),
],
};
let mut tip_accounts = TIP_ACCOUNTS.write().await;
accounts
.accounts
.iter()
.for_each(|account| tip_accounts.push(account.to_string()));
Ok(())
}
pub async fn get_tip_account() -> Result<Pubkey> {
let accounts = TIP_ACCOUNTS.read().await;
let mut rng = thread_rng();
match accounts.iter().choose(&mut rng) {
Some(acc) => Ok(Pubkey::from_str(acc).inspect_err(|err| {
println!("jito: failed to parse Pubkey: {:?}", err);
})?),
None => Err(anyhow!("jito: no tip accounts available")),
}
}
// unit sol
pub async fn get_tip_value() -> Result<f64> {
// If TIP_VALUE is set, use it
if let Ok(tip_value) = std::env::var("JITO_TIP_VALUE") {
match f64::from_str(&tip_value) {
Ok(value) => Ok(value),
Err(_) => {
println!(
"Invalid JITO_TIP_VALUE in environment variable: '{}'. Falling back to percentile calculation.",
tip_value
);
Err(anyhow!("Invalid TIP_VALUE in environment variable"))
}
}
} else {
Err(anyhow!("JITO_TIP_VALUE environment variable not set"))
}
}
pub async fn fetch_bundle_status(bundle_id: String) -> Result<Vec<serde_json::Value>> {
// Example implementation using reqwest:
use reqwest::Client;
let url = format!(
"{}/bundle/{}",
crate::common::utils::import_env_var("JITO_BLOCK_ENGINE_URL"),
bundle_id
);
let client = Client::new();
let resp = client.get(&url).send().await?;
let json: Vec<serde_json::Value> = resp.json().await?;
Ok(json)
}
#[derive(Deserialize, Debug)]
pub struct BundleStatus {
pub bundle_id: String,
pub transactions: Vec<String>,
pub slot: u64,
pub confirmation_status: String,
pub err: ErrorStatus,
}
#[derive(Deserialize, Debug)]
pub struct ErrorStatus {
#[serde(rename = "Ok")]
pub ok: Option<()>,
}
pub async fn wait_for_bundle_confirmation<F, Fut>(
fetch_statuses: F,
bundle_id: String,
interval: Duration,
timeout: Duration,
) -> Result<Vec<String>>
where
F: Fn(String) -> Fut,
Fut: Future<Output = Result<Vec<Value>>>,
{
let progress_bar = new_progress_bar();
let start_time = Instant::now();
loop {
let statuses = fetch_statuses(bundle_id.clone()).await?;
if let Some(status) = statuses.first() {
let bundle_status: BundleStatus =
serde_json::from_value(status.clone()).inspect_err(|err| {
println!(
"Failed to parse JSON when get_bundle_statuses, err: {}",
err,
);
})?;
println!("{:?}", bundle_status);
match bundle_status.confirmation_status.as_str() {
"finalized" | "confirmed" => {
progress_bar.finish_and_clear();
println!(
"Finalized bundle {}: {}",
bundle_id, bundle_status.confirmation_status
);
// print tx
bundle_status
.transactions
.iter()
.for_each(|tx| println!("https://solscan.io/tx/{}", tx));
return Ok(bundle_status.transactions);
}
_ => {
progress_bar.set_message(format!(
"Finalizing bundle {}: {}",
bundle_id, bundle_status.confirmation_status
));
}
}
} else {
progress_bar.set_message(format!("Finalizing bundle {}: {}", bundle_id, "None"));
}
// check loop exceeded 1 minute,
if start_time.elapsed() > timeout {
println!("Loop exceeded {:?}, breaking out.", timeout);
return Err(anyhow!("Bundle status get timeout"));
}
// Wait for a certain duration before retrying
sleep(interval).await;
}
}
pub fn new_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
progress_bar.set_style(
ProgressStyle::default_spinner()
.template("{spinner:.green} {wide_msg}")
.expect("ProgressStyle::template direct input to be correct"),
);
progress_bar.enable_steady_tick(Duration::from_millis(100));
progress_bar
}

View File

@@ -0,0 +1,3 @@
pub mod jito;
pub mod nextblock;
pub mod zeroslot;

View File

@@ -0,0 +1,75 @@
use anyhow::{anyhow, Result};
use rand::{seq::IteratorRandom, thread_rng};
use solana_sdk::pubkey::Pubkey;
use std::{str::FromStr, sync::LazyLock};
use tokio::sync::RwLock;
use crate::common::utils::import_env_var;
// Endpoint and auth token from env
pub static NEXTBLOCK_API_URL: LazyLock<String> =
LazyLock::new(|| import_env_var("NEXTBLOCK_API_URL"));
pub static NEXTBLOCK_AUTH_HEADER: LazyLock<String> =
LazyLock::new(|| import_env_var("NEXTBLOCK_AUTH_HEADER"));
pub static NEXTBLOCK_TIP_VALUE: LazyLock<String> =
LazyLock::new(|| import_env_var("NEXTBLOCK_TIP_VALUE"));
// List of hardcoded tip accounts
pub static TIP_ACCOUNTS: LazyLock<RwLock<Vec<String>>> = LazyLock::new(|| RwLock::new(vec![]));
#[derive(Debug)]
pub struct TipAccountResult {
pub accounts: Vec<String>,
}
pub async fn init_tip_accounts() -> Result<()> {
let accounts = TipAccountResult {
accounts: vec![
"NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE".to_string(),
"NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2".to_string(),
"NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X".to_string(),
"NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb".to_string(),
"neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At".to_string(),
"nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG".to_string(),
"NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid".to_string(),
"nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc".to_string(),
],
};
let mut tip_accounts = TIP_ACCOUNTS.write().await;
accounts
.accounts
.iter()
.for_each(|account| tip_accounts.push(account.to_string()));
Ok(())
}
pub async fn get_tip_account() -> Result<Pubkey> {
let accounts = TIP_ACCOUNTS.read().await;
let mut rng = thread_rng();
match accounts.iter().choose(&mut rng) {
Some(acc) => Ok(Pubkey::from_str(acc).inspect_err(|err| {
println!("nextblock: failed to parse Pubkey: {:?}", err);
})?),
None => Err(anyhow!("nextblock: no tip accounts available")),
}
}
// unit sol
pub async fn get_tip_value() -> Result<f64> {
// If TIP_VALUE is set, use it
if let Ok(tip_value) = std::env::var("NEXTBLOCK_TIP_VALUE") {
match f64::from_str(&tip_value) {
Ok(value) => Ok(value),
Err(_) => {
println!(
"Invalid NEXTBLOCK_TIP_VALUE in environment variable: '{}'. Falling back to percentile calculation.",
tip_value
);
Err(anyhow!("Invalid TIP_VALUE in environment variable"))
}
}
} else {
Err(anyhow!("NEXTBLOCK_TIP_VALUE environment variable not set"))
}
}

View File

@@ -0,0 +1,179 @@
use std::{future::Future, str::FromStr, sync::LazyLock, time::Duration};
use anyhow::{anyhow, Result};
use indicatif::{ProgressBar, ProgressStyle};
use rand::{seq::IteratorRandom, thread_rng};
use serde::Deserialize;
use serde_json::Value;
use solana_sdk::pubkey::Pubkey;
use tokio::{
sync::RwLock,
time::{sleep, Instant},
};
use crate::common::utils::import_env_var;
pub static BLOCK_ENGINE_URL: LazyLock<String> =
LazyLock::new(|| import_env_var("ZEROSLOT_BLOCK_ENGINE_URL"));
pub static TIP_STREAM_URL: LazyLock<String> =
LazyLock::new(|| import_env_var("ZEROSLOT_TIP_STREAM_URL"));
pub static TIP_PERCENTILE: LazyLock<String> =
LazyLock::new(|| import_env_var("ZEROSLOT_TIP_PERCENTILE"));
pub static TIP_ACCOUNTS: LazyLock<RwLock<Vec<String>>> = LazyLock::new(|| RwLock::new(vec![]));
#[derive(Debug)]
pub struct TipAccountResult {
pub accounts: Vec<String>,
}
pub async fn init_tip_accounts() -> Result<()> {
let accounts = TipAccountResult {
accounts: vec![
"Eb2KpSC8uMt9GmzyAEm5Eb1AAAgTjRaXWFjKyFXHZxF3".to_string(),
"FCjUJZ1qozm1e8romw216qyfQMaaWKxWsuySnumVCCNe".to_string(),
"ENxTEjSQ1YabmUpXAdCgevnHQ9MHdLv8tzFiuiYJqa13".to_string(),
"6rYLG55Q9RpsPGvqdPNJs4z5WTxJVatMB8zV3WJhs5EK".to_string(),
"Cix2bHfqPcKcM233mzxbLk14kSggUUiz2A87fJtGivXr".to_string(),
],
};
let mut tip_accounts = TIP_ACCOUNTS.write().await;
accounts
.accounts
.iter()
.for_each(|account| tip_accounts.push(account.to_string()));
Ok(())
}
pub async fn get_tip_account() -> Result<Pubkey> {
let accounts = TIP_ACCOUNTS.read().await;
let mut rng = thread_rng();
match accounts.iter().choose(&mut rng) {
Some(acc) => Ok(Pubkey::from_str(acc).inspect_err(|err| {
println!("jito: failed to parse Pubkey: {:?}", err);
})?),
None => Err(anyhow!("jito: no tip accounts available")),
}
}
// unit sol
pub async fn get_tip_value() -> Result<f64> {
// If TIP_VALUE is set, use it
if let Ok(tip_value) = std::env::var("ZEROSLOT_TIP_VALUE") {
match f64::from_str(&tip_value) {
Ok(value) => Ok(value),
Err(_) => {
println!(
"Invalid ZEROSLOT_TIP_VALUE in environment variable: '{}'. Falling back to percentile calculation.",
tip_value
);
Err(anyhow!(
"Invalid ZEROSLOT_TIP_VALUE in environment variable"
))
}
}
} else {
Err(anyhow!("ZEROSLOT_TIP_VALUE environment variable not set"))
}
}
pub async fn fetch_bundle_status(bundle_id: String) -> Result<Vec<serde_json::Value>> {
// Example implementation using reqwest:
use reqwest::Client;
let url = format!(
"{}/bundle/{}",
crate::common::utils::import_env_var("JITO_BLOCK_ENGINE_URL"),
bundle_id
);
let client = Client::new();
let resp = client.get(&url).send().await?;
let json: Vec<serde_json::Value> = resp.json().await?;
Ok(json)
}
#[derive(Deserialize, Debug)]
pub struct BundleStatus {
pub bundle_id: String,
pub transactions: Vec<String>,
pub slot: u64,
pub confirmation_status: String,
pub err: ErrorStatus,
}
#[derive(Deserialize, Debug)]
pub struct ErrorStatus {
#[serde(rename = "Ok")]
pub ok: Option<()>,
}
pub async fn wait_for_bundle_confirmation<F, Fut>(
fetch_statuses: F,
bundle_id: String,
interval: Duration,
timeout: Duration,
) -> Result<Vec<String>>
where
F: Fn(String) -> Fut,
Fut: Future<Output = Result<Vec<Value>>>,
{
let progress_bar = new_progress_bar();
let start_time = Instant::now();
loop {
let statuses = fetch_statuses(bundle_id.clone()).await?;
if let Some(status) = statuses.first() {
let bundle_status: BundleStatus =
serde_json::from_value(status.clone()).inspect_err(|err| {
println!(
"Failed to parse JSON when get_bundle_statuses, err: {}",
err,
);
})?;
println!("{:?}", bundle_status);
match bundle_status.confirmation_status.as_str() {
"finalized" | "confirmed" => {
progress_bar.finish_and_clear();
println!(
"Finalized bundle {}: {}",
bundle_id, bundle_status.confirmation_status
);
// print tx
bundle_status
.transactions
.iter()
.for_each(|tx| println!("https://solscan.io/tx/{}", tx));
return Ok(bundle_status.transactions);
}
_ => {
progress_bar.set_message(format!(
"Finalizing bundle {}: {}",
bundle_id, bundle_status.confirmation_status
));
}
}
} else {
progress_bar.set_message(format!("Finalizing bundle {}: {}", bundle_id, "None"));
}
// check loop exceeded 1 minute,
if start_time.elapsed() > timeout {
println!("Loop exceeded {:?}, breaking out.", timeout);
return Err(anyhow!("Bundle status get timeout"));
}
// Wait for a certain duration before retrying
sleep(interval).await;
}
}
pub fn new_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
progress_bar.set_style(
ProgressStyle::default_spinner()
.template("{spinner:.green} {wide_msg}")
.expect("ProgressStyle::template direct input to be correct"),
);
progress_bar.enable_steady_tick(Duration::from_millis(100));
progress_bar
}

View File

@@ -0,0 +1,505 @@
// src/shred_stream.rs
// Module to replace Geyser-based streams with Jito Shred-stream
use crate::apply_sol_flow_rules;
use crate::MIN_SOL_INFLOW_TRIGGER;
use crate::MIN_SOL_OUTFLOW_TRIGGER;
use crate::PENDING_MINTS;
use crate::PUMP_PROGRAM_PUBKEY;
use crate::{flatten_transaction_response, try_send_buy_if_allowed};
use anyhow::Context;
use backoff::future::retry;
use backoff::ExponentialBackoff;
use jito_protos::shredstream::{
shredstream_proxy_client::ShredstreamProxyClient, SubscribeEntriesRequest,
};
use log::{debug, info, warn};
use once_cell::sync::Lazy;
use pump_interface::PumpProgramIx;
use shared_state::CURRENT_SLOT;
use shared_state::{BuyOrder, SellOrder, BOUGHT_TOKENS};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_entry::entry::Entry as SolanaEntry;
use solana_sdk::message::VersionedMessage;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::transaction::VersionedTransaction;
use solana_transaction_status::{
ConfirmedTransactionWithStatusMeta, TransactionStatusMeta, TransactionWithStatusMeta,
VersionedTransactionWithStatusMeta,
};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::mpsc::Sender;
use tokio::sync::Mutex;
use tokio_stream::StreamExt;
const FALLBACK_SELL_AFTER_SLOTS: u64 = 50;
struct MintTracker {
creator: Pubkey,
last_activity: Instant,
sol_inflow: f64,
sol_outflow: f64,
waiting_sw: bool,
silence_until: Instant,
}
static MINT_TRACKERS: Lazy<Mutex<HashMap<String, MintTracker>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
// Silence window duration before we begin listening for the "second wave"
static SILENCE_DURATION: Lazy<Duration> = Lazy::new(|| {
let secs = std::env::var("SW_SILENCE_SECS").unwrap_or_else(|_| "60".into());
Duration::from_secs(secs.parse().unwrap_or(60))
});
// at top of file, make sure you have:
static SECOND_WAVE_MODE: Lazy<bool> = Lazy::new(|| {
std::env::var("SECOND_WAVE_MODE").map(|v| v == "true").unwrap_or(false)
});
/// Begin tracking a newly detected mint, recording its creator
/// and setting up the silence window before second‐wave sniping.
async fn start_tracking_mint(mint: String, creator: Pubkey) {
let now = Instant::now();
let silence = *SILENCE_DURATION; // from your SW_SILENCE_SECS env
let tracker = MintTracker {
creator,
last_activity: now,
sol_inflow: 0.0,
sol_outflow: 0.0,
waiting_sw: false,
silence_until: now + silence,
};
MINT_TRACKERS.lock().await.insert(mint, tracker);
}
/// Subscribe to Jito shredstream and manage new‐mint sniping vs. second‐wave mode
pub async fn shred_subscribe_transactions(
url: String,
sell_sender: Sender<SellOrder>,
mint_sender: Sender<BuyOrder>,
) -> anyhow::Result<()> {
let backoff = ExponentialBackoff::default();
let mut client = retry(backoff, || async {
ShredstreamProxyClient::connect(url.clone())
.await
.map_err(backoff::Error::transient)
})
.await?;
let mut stream = client
.subscribe_entries(SubscribeEntriesRequest::default())
.await?
.into_inner();
while let Some(Ok(batch)) = stream.next().await {
let slot = batch.slot;
CURRENT_SLOT.store(slot, Ordering::Relaxed);
let entries: Vec<SolanaEntry> = bincode::deserialize(&batch.entries)?;
for sol_entry in entries {
for tx in sol_entry.transactions {
if let Some((mint, creator)) = detect_raw_create(&tx) {
info!("New mint {} @ slot {}", mint, slot);
// Always start the silence‐window tracker
let mint_clone = mint.clone();
tokio::spawn(async move {
start_tracking_mint(mint_clone, creator).await;
});
// Only fire the *immediate* new‐mint buy if NOT in second‐wave mode
if !*SECOND_WAVE_MODE {
try_send_buy_if_allowed(
&mint,
creator,
true, // first_buy
true, // urgent=false
&mint_sender,
)
.await;
}
}
// offload your reconcilers (flows, confirms, sells)
let sell_tx = sell_sender.clone();
let mint_tx = mint_sender.clone();
tokio::spawn(async move {
let confirmed = confirm_from_versioned(tx, slot);
reconcile_flows(&confirmed, &sell_tx, &mint_tx).await;
reconcile_buys(&confirmed, slot).await;
reconcile_sells(&confirmed, slot, &sell_tx).await;
});
}
}
}
Ok(())
}
/// Update flows and trigger second-wave sniping, but only after our own buy has confirmed.
async fn reconcile_flows(
confirmed: &ConfirmedTransactionWithStatusMeta,
sell_sender: &Sender<SellOrder>,
mint_sender: &Sender<BuyOrder>,
) {
// only complete transactions
let vtx = match &confirmed.tx_with_meta {
TransactionWithStatusMeta::Complete(v) => v,
_ => return,
};
let now = Instant::now();
for tx_ix in flatten_transaction_response(vtx) {
// skip non-Pump instructions
if tx_ix.instruction.program_id != *PUMP_PROGRAM_PUBKEY {
continue;
}
let mint = tx_ix.instruction.accounts[2].pubkey.to_string();
// don’t start counting flows until our buy has landed
{
let tokens = BOUGHT_TOKENS.lock().await;
if tokens.get(&mint).map(|e| e.buy_slot()).unwrap_or(0) == 0 {
continue;
}
}
if let Some(tracker) = MINT_TRACKERS.lock().await.get_mut(&mint) {
tracker.last_activity = now;
if let Ok(ix) = PumpProgramIx::deserialize(&tx_ix.instruction.data) {
match ix {
PumpProgramIx::Buy(buy_args) => {
let sol_in = buy_args.max_sol_cost as f64 / 1e9;
tracker.sol_inflow += sol_in;
debug!("SOL inflow +{:.6} on {}", sol_in, mint);
if tracker.waiting_sw {
info!("🚀 Second wave hit on {} → sniping", mint);
try_send_buy_if_allowed(
&mint,
tracker.creator,
true,
true,
mint_sender,
).await;
tracker.waiting_sw = false;
}
}
PumpProgramIx::Sell(sell_args) => {
let sol_out = sell_args.min_sol_output as f64 / 1e9;
tracker.sol_outflow += sol_out;
debug!("SOL outflow +{:.6} on {}", sol_out, mint);
}
_ => {}
}
}
if !tracker.waiting_sw && now >= tracker.silence_until {
tracker.waiting_sw = true;
info!("⏱ {} entered second-wave watch mode", mint);
}
}
}
}
/// Confirm our buy, then reset that mint’s tracker so we only count SOL flows from now on.
async fn reconcile_buys(
confirmed: &ConfirmedTransactionWithStatusMeta,
slot: u64,
) {
// pull out on-chain signature (if any)
let sig_opt = match &confirmed.tx_with_meta {
TransactionWithStatusMeta::Complete(v) => v.transaction.signatures.get(0).cloned(),
_ => None,
};
if let Some(sig) = sig_opt {
let sig_str = sig.to_string();
let mut tokens = BOUGHT_TOKENS.lock().await;
// find our minted entry by matching its stored signature
if let Some((mint, entry)) = tokens
.iter_mut()
.find(|(_, e)| e.signature() == Some(&sig_str))
{
// confirm the buy
entry.set_buy_slot(slot);
entry.set_buy_executed_at(Instant::now());
PENDING_MINTS.fetch_sub(1, Ordering::Relaxed);
info!("✅ Confirmed BUY for {} @ slot {}", mint, slot);
// re-arm second-wave tracker
if let Some(tracker) = MINT_TRACKERS.lock().await.get_mut(mint) {
let now = Instant::now();
tracker.last_activity = now;
tracker.sol_inflow = 0.0;
tracker.sol_outflow = 0.0;
tracker.waiting_sw = false;
tracker.silence_until = now + *SILENCE_DURATION;
info!("🔄 Re-armed second-wave tracker for {}", mint);
}
}
}
}
fn detect_raw_create(vtx: &VersionedTransaction) -> Option<(String, Pubkey)> {
let msg = &vtx.message;
let keys = msg.static_account_keys();
debug!(
"🔑 static keys (len={}): {:?}",
keys.len(),
keys.iter().take(4).collect::<Vec<_>>()
);
for instr in msg.instructions() {
let program_key = keys[instr.program_id_index as usize];
if program_key == *PUMP_PROGRAM_PUBKEY {
debug!(
"🧩 Found PumpProgramIx at idx={} data_prefix={:02x?}",
instr.program_id_index,
&instr.data[..4.min(instr.data.len())]
);
match PumpProgramIx::deserialize(&instr.data) {
Ok(PumpProgramIx::Create(args)) => {
let mint_key = keys[instr.accounts[0] as usize];
debug!("✅ Deserialized Create; mint = {}", mint_key);
return Some((mint_key.to_string(), args.creator));
}
Ok(other) => {
debug!("ℹ️ Other PumpProgramIx::{:?}, skipping", other);
}
Err(e) => {
debug!("❌ deserialize error: {}", e);
}
}
}
}
None
}
/// Quickly wrap a VersionedTransaction in a ConfirmedTransactionWithStatusMeta
fn confirm_from_versioned(
tx: solana_sdk::transaction::VersionedTransaction,
slot: u64,
) -> ConfirmedTransactionWithStatusMeta {
// minimal meta with defaults:
let meta = TransactionStatusMeta {
status: Ok(()),
fee: 0,
pre_balances: vec![],
post_balances: vec![],
inner_instructions: None,
log_messages: None,
pre_token_balances: None,
post_token_balances: None,
rewards: None,
loaded_addresses: Default::default(),
return_data: None,
compute_units_consumed: None,
};
let vtx = VersionedTransactionWithStatusMeta {
transaction: tx,
meta,
};
ConfirmedTransactionWithStatusMeta {
slot,
tx_with_meta: TransactionWithStatusMeta::Complete(vtx),
block_time: None,
}
}
/// Decode raw bytes and slot into ConfirmedTransactionWithStatusMeta
pub fn decode_txn_from_bytes(
raw: &[u8],
slot: u64,
) -> anyhow::Result<ConfirmedTransactionWithStatusMeta> {
use bincode;
use solana_sdk::transaction::VersionedTransaction;
use solana_transaction_status::TransactionStatusMeta;
// 1. Deserialize the wire-format VersionedTransaction
let tx: VersionedTransaction = bincode::deserialize(raw)
.context("failed to deserialize VersionedTransaction from shred entry")?;
// 2. Build a minimal TransactionStatusMeta with defaults
let meta = TransactionStatusMeta {
status: Ok(()),
fee: 0,
pre_balances: vec![],
post_balances: vec![],
inner_instructions: None,
log_messages: None,
pre_token_balances: None,
post_token_balances: None,
rewards: None,
loaded_addresses: Default::default(),
return_data: None,
compute_units_consumed: None,
};
// 3. Wrap into VersionedTransactionWithStatusMeta and then into ConfirmedTransactionWithStatusMeta
let vtx = VersionedTransactionWithStatusMeta {
transaction: tx,
meta,
};
Ok(ConfirmedTransactionWithStatusMeta {
slot,
tx_with_meta: TransactionWithStatusMeta::Complete(vtx),
block_time: None,
})
}
const LOW_BUY_FREQ_WINDOW_SLOTS: u64 = 10;
const MIN_BUYS_IN_WINDOW: usize = 6;
const BUY_CONFIRM_TIMEOUT_SECS: u64 = 60;
const SELL_CONFIRM_TIMEOUT_SECS: u64 = 60;
async fn reconcile_sells(
confirmed: &ConfirmedTransactionWithStatusMeta,
slot: u64,
sell_sender: &Sender<SellOrder>,
) {
// --- 0) remove any entries that are already confirmed sells, just in case ---
{
let mut tokens = BOUGHT_TOKENS.lock().await;
let to_remove: Vec<String> = tokens
.iter()
.filter_map(|(mint, entry)| {
if entry.sell_confirmed() {
Some(mint.clone())
} else {
None
}
})
.collect();
for mint in to_remove {
tokens.remove(&mint);
}
}
// 1) On-chain confirmations
if let Some(sig) = match &confirmed.tx_with_meta {
TransactionWithStatusMeta::Complete(v) => {
v.transaction.signatures.get(0).map(|s| s.to_string())
}
_ => None,
} {
// find mint without holding a mutable borrow across removal
let mint_opt = {
let tokens = BOUGHT_TOKENS.lock().await;
tokens
.iter()
.find(|(_, e)| e.sell_signature().as_deref() == Some(&sig))
.map(|(m, _)| m.clone())
};
if let Some(mint) = mint_opt {
// confirm + slot in one lock
{
let mut tokens = BOUGHT_TOKENS.lock().await;
if let Some(entry) = tokens.get_mut(&mint) {
entry.confirm_sell();
entry.set_sell_slot(slot);
}
// guard against underflow
if PENDING_MINTS.load(Relaxed) > 0 {
PENDING_MINTS.fetch_sub(1, Relaxed);
}
}
// remove in a separate lock
{
let mut tokens = BOUGHT_TOKENS.lock().await;
tokens.remove(&mint);
}
log::info!("✅ Confirmed SELL for {} @ slot {}", mint, slot);
return;
}
}
// 2) retry logic
// build stale list under lock
let now = Instant::now();
let timeout = Duration::from_secs(SELL_CONFIRM_TIMEOUT_SECS);
let stale: Vec<String> = {
let tokens = BOUGHT_TOKENS.lock().await;
tokens
.iter()
.filter_map(|(mint, entry)| {
if entry.sell_signature().is_some()
&& !entry.sell_confirmed()
&& entry.retry_count() < 3
&& entry
.sell_executed_at()
.map(|t| now.duration_since(t) > timeout)
.unwrap_or(false)
{
Some(mint.clone())
} else {
None
}
})
.collect()
};
// drop lock, then re-send each stale sell
for mint in stale {
let mut tokens = BOUGHT_TOKENS.lock().await;
if let Some(entry) = tokens.get_mut(&mint) {
entry.increment_retry_count();
log::info!(
"⌛ Sell for {} timed out; retry #{}, re-sending…",
mint,
entry.retry_count()
);
let amount = entry.amount();
let _ = sell_sender
.send(SellOrder {
mint: mint.clone(),
amount,
use_jito: true,
urgent: true,
})
.await;
// record when we issued this retry
entry.set_sell_executed_at(Instant::now());
}
}
}
/// Find mint & creator from Create ix
fn extract_mint_and_creator(
confirmed: &ConfirmedTransactionWithStatusMeta,
) -> Option<(String, Pubkey)> {
if let TransactionWithStatusMeta::Complete(v) = &confirmed.tx_with_meta {
for tx_ix in flatten_transaction_response(v) {
if tx_ix.instruction.program_id == *crate::PUMP_PROGRAM_PUBKEY {
if let Ok(PumpProgramIx::Create(args)) =
PumpProgramIx::deserialize(&tx_ix.instruction.data)
{
// the very first account is the mint
if let Some(meta) = tx_ix.instruction.accounts.get(0) {
// return (mint_pubkey_string, creator_pubkey)
return Some((meta.pubkey.to_string(), args.creator));
}
}
}
}
}
None
}

View File

@@ -0,0 +1,179 @@
// src/solana_helpers.rs
use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `aes_gcm::Aes256Gcm`
use aes_gcm::aead::{Aead, NewAead};
use base64::{encode as b64_encode, decode as b64_decode};
use hex::decode as hex_decode;
use rand::RngCore;
use rand::rngs::OsRng;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::pubkey::Pubkey;
use bip39::{Mnemonic, Language, Seed};
use anyhow::{Result, Context};
use serde_json::Value;
/// Load the AES-256 key from the environment variable `ENCRYPTION_KEY`.
/// The env var should be a hex-encoded 32-byte key (i.e., 64 hex chars).
fn load_encryption_key() -> Result<[u8; 32]> {
let hex = std::env::var("ENCRYPTION_KEY")
.context("ENCRYPTION_KEY env var not set")?;
let bytes = hex_decode(&hex)
.context("Failed to hex-decode ENCRYPTION_KEY")?;
if bytes.len() != 32 {
anyhow::bail!("ENCRYPTION_KEY must be 32 bytes (hex-encoded 64 chars)");
}
let mut key = [0u8; 32];
key.copy_from_slice(&bytes);
Ok(key)
}
/// Encrypt `plaintext` using AES-256-GCM. Returns a base64 string containing nonce||ciphertext.
/// Format: base64( [12-byte nonce] || ciphertext ).
pub fn encrypt_data(plaintext: &str) -> Result<String> {
let key_bytes = load_encryption_key()?;
let key = Key::from_slice(&key_bytes);
let cipher = Aes256Gcm::new(key);
// Generate a random 12-byte nonce
let mut nonce_bytes = [0u8; 12];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext.as_bytes())
.context("Encryption failure")?;
// Prepend nonce
let mut combined = Vec::with_capacity(12 + ciphertext.len());
combined.extend_from_slice(&nonce_bytes);
combined.extend_from_slice(&ciphertext);
Ok(b64_encode(&combined))
}
/// Decrypt a base64 string produced by `encrypt_data`, returning the plaintext.
pub fn decrypt_data(encoded: &str) -> Result<String> {
let combined = b64_decode(encoded).context("Base64 decode failed")?;
if combined.len() < 12 {
anyhow::bail!("Ciphertext too short");
}
let (nonce_bytes, ciphertext) = combined.split_at(12);
let key_bytes = load_encryption_key()?;
let key = Key::from_slice(&key_bytes);
let cipher = Aes256Gcm::new(key);
let nonce = Nonce::from_slice(nonce_bytes);
let plaintext = cipher
.decrypt(nonce, ciphertext)
.context("Decryption failure")?;
let s = String::from_utf8(plaintext).context("Decrypted data not valid UTF-8")?;
Ok(s)
}
/// Derive Solana public address (Pubkey) from a JSON-array keypair string.
///
/// # Arguments
/// - `json`: a JSON string representing an array of 64 or 32 or 64 u8 values, e.g. `[12,34, ...]`.
/// - If length is 64: interpreted as full keypair bytes.
/// - If length is 32: interpreted as secret seed, but Solana Keypair::from_bytes expects 64; you may need to expand.
///
/// Returns the `Pubkey` as a base58 string.
pub fn derive_address_from_json_keypair(json: &str) -> Result<String> {
// Parse JSON array
let v: Value = serde_json::from_str(json).context("Invalid JSON")?;
let arr = v.as_array().context("Expected JSON array")?;
// Convert to Vec<u8>
let bytes: Vec<u8> = arr.iter()
.map(|val| {
val.as_u64()
.and_then(|n| Some(n as u8))
.ok_or_else(|| anyhow::anyhow!("Invalid byte in array"))
})
.collect::<Result<Vec<_>, _>>()?;
// Depending on length:
let kp = if bytes.len() == 64 {
Keypair::from_bytes(&bytes).context("Failed to parse keypair from bytes")?
} else if bytes.len() == 32 {
// interpret as secret seed: expand to keypair via `from_seed`
// Note: solana_sdk::signature::Keypair::from_seed requires `Signer` trait; but no direct from_seed in stable.
// We can use ed25519_dalek to expand, then wrap in Keypair.
// Solana Keypair::from_seed is nightly; instead:
// Use solana_sdk::signature::Keypair::from_seed (requires "ed25519-dalek" feature)
Keypair::from_seed(&bytes).context("Failed to derive keypair from 32-byte seed")?
} else {
anyhow::bail!("Keypair JSON must be 32 or 64 bytes");
};
let pubkey = kp.pubkey();
Ok(pubkey.to_string())
}
/// Derive Solana public address from a base58-encoded secret key (64-byte) string.
/// E.g., if user has a base58 of secret key bytes.
///
/// # Arguments
/// - `b58`: base58 string of 64 bytes.
/// Returns base58 pubkey string.
pub fn derive_address_from_base58_secret(b58: &str) -> Result<String> {
let data = bs58::decode(b58).into_vec().context("Invalid base58")?;
if data.len() == 64 {
let kp = Keypair::from_bytes(&data).context("Failed to parse keypair bytes")?;
Ok(kp.pubkey().to_string())
} else if data.len() == 32 {
let kp = Keypair::from_seed(&data).context("Failed to derive keypair from seed")?;
Ok(kp.pubkey().to_string())
} else {
anyhow::bail!("Base58 secret must be 32 or 64 bytes");
}
}
/// Derive Solana public address from a BIP39 mnemonic phrase.
/// Uses the standard Solana derivation: m/44'/501'/0'/0' by default, but this can be adjusted.
///
/// # Arguments
/// - `mnemonic`: the phrase, e.g. "abandon abandon ...".
/// - `passphrase`: optional passphrase for mnemonic (usually empty string).
/// - `account`: u32, default 0.
/// - `change`: u32, default 0.
///
/// Returns base58 pubkey string.
pub fn derive_address_from_mnemonic(
mnemonic: &str,
passphrase: &str,
account: u32,
change: u32,
) -> Result<String> {
// Parse mnemonic
let mn = Mnemonic::from_phrase(mnemonic, Language::English)
.context("Invalid mnemonic phrase")?;
let seed = Seed::new(&mn, passphrase);
let seed_bytes = seed.as_bytes(); // 64 bytes
// Derivation path for Solana: m/44'/501'/<account>'/<change>'
// We can use `solana_sdk::derivation_path::DerivationPath` if available, or derive via ed25519-dalek + slip10.
// For simplicity, we use `solana_sdk::derivation_path::DerivationPath` if in scope:
// If not, use `ed25519-dalek-bip32` or similar. Here we assume solana-sdk has `derive_keypair_from_seed_and_path`.
// As of solana-sdk v1.14+, there's `keypair_from_seed_and_derivation_path`.
#[cfg(feature = "solana-derive-keypair")]
{
// Pseudo-code; adjust per your solana-sdk version:
/*
use solana_sdk::derivation_path::DerivationPath;
let path = DerivationPath::new_bip44(Some(501), Some(account), Some(change));
let kp = Keypair::from_seed_with_derivation(&seed_bytes, &path)
.context("Failed deriving keypair from mnemonic")?;
Ok(kp.pubkey().to_string())
*/
unimplemented!("Adjust to your solana-sdk version's derivation API");
}
#[cfg(not(feature = "solana-derive-keypair"))]
{
// As fallback: derive the ED25519 key by directly using the seed (not recommended for multiple accounts).
// Here we just derive from seed_bytes[0..32]:
let seed32 = &seed_bytes[0..32];
let kp = Keypair::from_seed(seed32).context("Failed to derive keypair from mnemonic seed")?;
Ok(kp.pubkey().to_string())
}
}

View File

@@ -0,0 +1,44 @@
// src/tginterface.rs
use teloxide::prelude::*;
use std::env;
use dotenv::dotenv;
use crate::db::Database;
use crate::commands::{handle_command, Command};
mod db;
mod models;
mod commands;
#[tokio::main]
async fn main() {
dotenv().ok();
env_logger::init();
let bot_token = env::var("TELEGRAM_BOT_TOKEN").expect("TELEGRAM_BOT_TOKEN not set");
let mongo_uri = env::var("MONGODB_URI").expect("MONGODB_URI not set");
let mongo_db = env::var("MONGODB_DB").unwrap_or_else(|_| "telegram_wallet_bot".to_string());
let admin_ids: Vec<i64> = env::var("ADMIN_IDS")
.unwrap_or_default()
.split(',')
.filter_map(|s| s.trim().parse().ok())
.collect();
let db = Database::new(&mongo_uri, &mongo_db)
.await
.expect("failed to initialize database");
let bot = Bot::new(bot_token).auto_send();
log::info!("Starting bot...");
// Use a cloned Database in handler
teloxide::commands_repl(bot.clone(), move |bot: AutoSend<Bot>, msg: Message, cmd: Command| {
let db = db.clone(); // Database should be Cloneable or use Arc
let admin_ids = admin_ids.clone();
async move {
if let Err(e) = handle_command(bot.clone().into_inner(), msg, cmd, db.clone(), admin_ids.clone()).await {
log::error!("Error handling command: {:?}", e);
}
}
}, Command::ty()).await;
}

View File

@@ -0,0 +1,200 @@
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use solana_program::{program_option::COption, pubkey::Pubkey};
use spl_token::instruction::{AuthorityType, TokenInstruction};
// Helper function to convert COption<Pubkey> to Option<Pubkey>
fn convert_coption<T>(coption: COption<T>) -> Option<T> {
match coption {
COption::Some(value) => Some(value),
COption::None => None,
}
}
fn convert_set_authority(
authority_type: AuthorityType,
new_authority: COption<Pubkey>,
) -> SerializableSetAuthority {
SerializableSetAuthority {
authority_type: match authority_type {
AuthorityType::MintTokens => "MintTokens".to_string(),
AuthorityType::FreezeAccount => "FreezeAccount".to_string(),
AuthorityType::AccountOwner => "AccountOwner".to_string(),
AuthorityType::CloseAccount => "CloseAccount".to_string(),
},
new_authority: new_authority.into(), // Convert COption<Pubkey> to Option<Pubkey>
}
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct SerializableInitializeMint {
decimals: u8,
#[serde_as(as = "DisplayFromStr")]
mint_authority: Pubkey,
#[serde_as(as = "Option<DisplayFromStr>")]
freeze_authority: Option<Pubkey>,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct SerializableInitializeAccount3 {
#[serde_as(as = "DisplayFromStr")]
owner: Pubkey,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct SerializableSetAuthority {
authority_type: String,
#[serde_as(as = "Option<DisplayFromStr>")]
new_authority: Option<Pubkey>,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct SerializableTransfer {
amount: u64,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub enum SerializableTokenInstruction {
InitializeMint(SerializableInitializeMint),
InitializeAccount,
InitializeMultisig {
m: u8,
},
Transfer(SerializableTransfer),
Approve {
amount: u64,
},
Revoke,
SetAuthority(SerializableSetAuthority),
MintTo {
amount: u64,
},
Burn {
amount: u64,
},
CloseAccount,
FreezeAccount,
ThawAccount,
TransferChecked {
amount: u64,
decimals: u8,
},
ApproveChecked {
amount: u64,
decimals: u8,
},
MintToChecked {
amount: u64,
decimals: u8,
},
BurnChecked {
amount: u64,
decimals: u8,
},
InitializeAccount2 {
#[serde_as(as = "DisplayFromStr")]
owner: Pubkey,
},
SyncNative,
InitializeAccount3(SerializableInitializeAccount3),
InitializeMultisig2 {
m: u8,
},
InitializeMint2(SerializableInitializeMint),
GetAccountDataSize,
InitializeImmutableOwner,
AmountToUiAmount {
amount: u64,
},
UiAmountToAmount {
ui_amount: String,
},
}
// Convert TokenInstruction to SerializableTokenInstruction
pub fn convert_to_serializable(ix: TokenInstruction) -> SerializableTokenInstruction {
match ix {
TokenInstruction::InitializeMint {
decimals,
mint_authority,
freeze_authority,
} => {
SerializableTokenInstruction::InitializeMint(SerializableInitializeMint {
decimals,
mint_authority,
freeze_authority: freeze_authority.into(), // Convert COption to Option
})
}
TokenInstruction::InitializeAccount => SerializableTokenInstruction::InitializeAccount,
TokenInstruction::InitializeMultisig { m } => {
SerializableTokenInstruction::InitializeMultisig { m }
}
TokenInstruction::Transfer { amount } => {
SerializableTokenInstruction::Transfer(SerializableTransfer { amount })
}
TokenInstruction::Approve { amount } => SerializableTokenInstruction::Approve { amount },
TokenInstruction::Revoke => SerializableTokenInstruction::Revoke,
TokenInstruction::SetAuthority {
authority_type,
new_authority,
} => SerializableTokenInstruction::SetAuthority(convert_set_authority(
authority_type,
new_authority,
)),
TokenInstruction::MintTo { amount } => SerializableTokenInstruction::MintTo { amount },
TokenInstruction::Burn { amount } => SerializableTokenInstruction::Burn { amount },
TokenInstruction::CloseAccount => SerializableTokenInstruction::CloseAccount,
TokenInstruction::FreezeAccount => SerializableTokenInstruction::FreezeAccount,
TokenInstruction::ThawAccount => SerializableTokenInstruction::ThawAccount,
TokenInstruction::TransferChecked { amount, decimals } => {
SerializableTokenInstruction::TransferChecked { amount, decimals }
}
TokenInstruction::ApproveChecked { amount, decimals } => {
SerializableTokenInstruction::ApproveChecked { amount, decimals }
}
TokenInstruction::MintToChecked { amount, decimals } => {
SerializableTokenInstruction::MintToChecked { amount, decimals }
}
TokenInstruction::BurnChecked { amount, decimals } => {
SerializableTokenInstruction::BurnChecked { amount, decimals }
}
TokenInstruction::InitializeAccount2 { owner } => {
SerializableTokenInstruction::InitializeAccount2 { owner }
}
TokenInstruction::SyncNative => SerializableTokenInstruction::SyncNative,
TokenInstruction::InitializeAccount3 { owner } => {
SerializableTokenInstruction::InitializeAccount3(SerializableInitializeAccount3 {
owner,
})
}
TokenInstruction::InitializeMultisig2 { m } => {
SerializableTokenInstruction::InitializeMultisig2 { m }
}
TokenInstruction::InitializeMint2 {
decimals,
mint_authority,
freeze_authority,
} => SerializableTokenInstruction::InitializeMint2(SerializableInitializeMint {
decimals,
mint_authority,
freeze_authority: freeze_authority.into(),
}),
TokenInstruction::GetAccountDataSize => SerializableTokenInstruction::GetAccountDataSize,
TokenInstruction::InitializeImmutableOwner => {
SerializableTokenInstruction::InitializeImmutableOwner
}
TokenInstruction::AmountToUiAmount { amount } => {
SerializableTokenInstruction::AmountToUiAmount { amount }
}
TokenInstruction::UiAmountToAmount { ui_amount } => {
SerializableTokenInstruction::UiAmountToAmount {
ui_amount: ui_amount.to_string(),
}
}
}
}

View File

@@ -0,0 +1,143 @@
// trade_logger.rs
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
use std::str::FromStr;
use tokio::io::AsyncWriteExt;
use tokio::sync::Mutex;
// Remove std::io::BufReader / std::fs::File imports for async loading:
use std::collections::HashSet;
use std::sync::RwLock;
use tokio::fs::File;
use tokio::io::{AsyncBufReadExt, BufReader};
static SKIP_CREATORS: Lazy<RwLock<HashSet<Pubkey>>> = Lazy::new(|| RwLock::new(HashSet::new()));
/// Full record to serialize:
#[derive(Serialize, Deserialize)]
pub struct TradeRecord {
pub mint: String,
pub creator: String,
pub buy_lamports: u64,
pub tokens_received: u64,
pub buy_slot: u64,
pub sell_lamports: u64,
pub tokens_sold: u64,
pub sell_slot: u64,
pub pnl_lamports: i128,
pub timestamp: i64, // unix secs
}
// At startup, load trade_records.jsonl and populate SKIP_CREATORS.
// Assumes each line is a JSON object matching TradeRecord.
pub async fn load_skip_creators_from_file(path: &str) -> anyhow::Result<()> {
// Use tokio::fs::File so we can await.
let file = match File::open(path).await {
Ok(f) => f,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// No file yet: nothing to skip
return Ok(());
}
Err(e) => return Err(e.into()),
};
let reader = BufReader::new(file);
let mut lines = reader.lines();
// Acquire write lock synchronously (no `.await`).
let mut skip_set = SKIP_CREATORS.write().unwrap();
while let Some(line) = lines.next_line().await? {
let line = line.trim();
if line.is_empty() {
continue;
}
match serde_json::from_str::<TradeRecord>(line) {
Ok(rec) => {
if rec.pnl_lamports < 0 {
if let Ok(pubkey) = Pubkey::from_str(&rec.creator) {
skip_set.insert(pubkey);
}
}
}
Err(_) => {
log::warn!("Invalid trade record line: {}", line);
}
}
}
Ok(())
}
// When a new trade record is written and is negative, also update SKIP_CREATORS in-memory:
pub async fn record_trade_and_maybe_skip(path: &str, rec: &TradeRecord) -> anyhow::Result<()> {
// append to file asynchronously
let mut file = tokio::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)
.await?;
let json = serde_json::to_string(rec)?;
file.write_all(json.as_bytes()).await?;
file.write_all(b"\n").await?;
if rec.pnl_lamports < 0 {
if let Ok(pubkey) = Pubkey::from_str(&rec.creator) {
// Acquire write lock synchronously:
SKIP_CREATORS.write().unwrap().insert(pubkey);
}
}
Ok(())
}
/// In-memory partials keyed by mint:
static PARTIALS: Lazy<Mutex<HashMap<String, PartialTrade>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
struct PartialTrade {
creator: String,
buy_lamports: u64,
tokens_received: u64,
buy_slot: u64,
}
/// Call when a buy is confirmed:
pub async fn record_buy(
mint: &str,
creator: &Pubkey,
buy_lamports: u64,
tokens_received: u64,
buy_slot: u64,
) {
let mut map = PARTIALS.lock().await;
map.insert(
mint.to_string(),
PartialTrade {
creator: creator.to_string(),
buy_lamports,
tokens_received,
buy_slot,
},
);
}
/// Call when a sell is confirmed:
pub async fn record_sell(mint: &str, sell_lamports: u64, tokens_sold: u64, sell_slot: u64) {
let mut map = PARTIALS.lock().await;
if let Some(partial) = map.remove(mint) {
let pnl = sell_lamports as i128 - partial.buy_lamports as i128;
let record = TradeRecord {
mint: mint.to_string(),
creator: partial.creator.clone(),
buy_lamports: partial.buy_lamports,
tokens_received: partial.tokens_received,
buy_slot: partial.buy_slot,
sell_lamports,
tokens_sold,
sell_slot,
pnl_lamports: pnl,
timestamp: chrono::Utc::now().timestamp(),
};
// Append to file and update skip-set if needed
if let Err(e) = record_trade_and_maybe_skip("trade_records.jsonl", &record).await {
log::error!("Failed to append trade record: {:?}", e);
}
}
}

View File

@@ -0,0 +1,120 @@
// trading_loop.rs
use crate::common::utils::{
create_nonblocking_rpc_client, create_rpc_client, import_env_var, import_wallet, AppState,
SwapExecutionMode, SwapInput,
};
use crate::core::tx;
use crate::dex::pump_fun::PUMP_PROGRAM;
use crate::engine::swap::pump_swap;
use dotenv::dotenv;
use shared_state::{BuyOrder, SellOrder};
use solana_sdk::{native_token, pubkey::Pubkey};
use std::{
env,
str::FromStr,
sync::{Arc, OnceLock},
};
use tokio::sync::{mpsc::Receiver, Notify, OnceCell};
static TRIGGER_NOTIFY: OnceCell<Arc<Notify>> = OnceCell::const_new();
/// Exposed so `pub use trading_loop::get_notify_handle;` still works.
pub async fn get_notify_handle() -> Arc<Notify> {
TRIGGER_NOTIFY
.get_or_init(|| async { Arc::new(Notify::new()) })
.await
.clone()
}
// Pre-parse these exactly once at program load:
static PUMP_PROGRAM_KEY: OnceLock<Pubkey> = OnceLock::new();
static LAMPORTS_PER_SOL_U64: OnceLock<u64> = OnceLock::new();
pub async fn start_trading_loop(mut mint_rx: Receiver<BuyOrder>, mut sell_rx: Receiver<SellOrder>) {
// load .env if present
dotenv().ok();
// one-time RPC & wallet setup
let rpc = create_rpc_client().unwrap();
let rpc_nonblocking = create_nonblocking_rpc_client().await.unwrap();
let wallet = import_wallet().unwrap();
let app = AppState {
rpc_client: rpc.clone(),
rpc_nonblocking_client: rpc_nonblocking.clone(),
wallet: wallet.clone(),
};
// ─── SPAWN BLOCKHASH REFRESHER ───
// This will keep a fresh recent‐blockhash in the background so sends never stall
// ─── ONE-TIME TIP‐ENGINE INITIALIZATION ───
// Read your env vars just once
let block_engine = env::var("BLOCK_ENGINE_PROVIDER").unwrap_or_default();
let use_nextblock = block_engine.eq_ignore_ascii_case("nextblock");
let use_priority_tip = env::var("USE_PRIORITY_TIP")
.unwrap_or_default()
.eq_ignore_ascii_case("true");
// This will create/fetch your tip‐accounts and pull the first tip value
tx::init_tip_state(use_nextblock, use_priority_tip)
.await
.expect("failed to init tip engine");
// stash env vars once
let slippage_bps = import_env_var("SLIPPAGE").parse::<u16>().unwrap_or(15);
let buy_amount_sol = import_env_var("BUY_AMOUNT_SOL")
.parse::<f64>()
.unwrap_or(0.01);
// initialize our OnceLocks
let lamports_per_sol = *LAMPORTS_PER_SOL_U64.get_or_init(|| native_token::LAMPORTS_PER_SOL);
let buy_amount_lamports = (buy_amount_sol * lamports_per_sol as f64) as u64;
let pump_program_key =
*PUMP_PROGRAM_KEY.get_or_init(|| Pubkey::from_str(PUMP_PROGRAM).unwrap());
loop {
tokio::select! {
// ───────── BUY ORDERS ─────────
Some(BuyOrder { mint, creator, use_jito, urgent }) = mint_rx.recv() => {
let mint_pk = Pubkey::from_str(&mint).unwrap();
// derive creator vault address
let (creator_vault, _) =
Pubkey::find_program_address(&[b"creator-vault", creator.as_ref()], &pump_program_key);
let input = SwapInput {
input_token_mint: spl_token::native_mint::ID,
output_token_mint: mint_pk,
slippage_bps,
amount: buy_amount_lamports,
mode: SwapExecutionMode::ExactIn,
market: None,
creator_vault: Some(creator_vault),
};
let app_clone = app.clone();
tokio::spawn(async move {
let _ = pump_swap(app_clone, input, "buy", use_jito, urgent).await;
});
}
// ───────── SELL ORDERS ────────
Some(SellOrder { mint, amount, use_jito, urgent }) = sell_rx.recv() => {
let mint_pk = Pubkey::from_str(&mint).unwrap();
let input = SwapInput {
input_token_mint: mint_pk,
output_token_mint: spl_token::native_mint::ID,
slippage_bps,
amount,
mode: SwapExecutionMode::ExactOut,
market: None,
creator_vault: None,
};
let app_clone = app.clone();
tokio::spawn(async move {
let _ = pump_swap(app_clone, input, "sell", use_jito, urgent).await;
});
}
}
}
}

View File

@@ -0,0 +1,390 @@
{"mint":"4DxH2nRs2SA4uqqddkSkpAM3mPLbhTK86q3ZahBypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":153937741,"tokens_received":0,"buy_slot":347311946,"sell_lamports":137943497,"tokens_sold":5000000000000,"sell_slot":347311982,"pnl_lamports":-15994244,"timestamp":1750133620}
{"mint":"F6LCD6eLzLcCivZiGiQddyrvm8xJgAGS31bkTZoLpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174649889,"tokens_received":0,"buy_slot":347316858,"sell_lamports":137297743,"tokens_sold":0,"sell_slot":347316894,"pnl_lamports":-37352146,"timestamp":1750135583}
{"mint":"3P1UMsoUntc5GDJRvnoS38py3K8wNNJyJFAyEo7apump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144623036,"tokens_received":0,"buy_slot":347316984,"sell_lamports":136639943,"tokens_sold":5000000000000,"sell_slot":347317010,"pnl_lamports":-7983093,"timestamp":1750135633}
{"mint":"5AYswZcdHVdu5BCKJPzbNyBCSzaLfTG8bXzcrQZ5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168758193,"tokens_received":0,"buy_slot":347317104,"sell_lamports":159867553,"tokens_sold":0,"sell_slot":347317120,"pnl_lamports":-8890640,"timestamp":1750135678}
{"mint":"6jusvqh1dUm7KUCcK56hxeBQYkggAMKpmSEaaY8Ef3ZC","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":151494208,"tokens_received":0,"buy_slot":347471412,"sell_lamports":136785977,"tokens_sold":0,"sell_slot":347471446,"pnl_lamports":-14708231,"timestamp":1750197584}
{"mint":"DsSFNuRPxGX9AHhuRSTdKVf232x2Dk3TTRZNEkLUpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":145098776,"tokens_received":0,"buy_slot":347477111,"sell_lamports":137559240,"tokens_sold":0,"sell_slot":347477261,"pnl_lamports":-7539536,"timestamp":1750199923}
{"mint":"E9AdXtfabeBWR7UdJZw29DiNWutRfFb9Jro2cfq2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144420990,"tokens_received":0,"buy_slot":347477343,"sell_lamports":136818639,"tokens_sold":0,"sell_slot":347477427,"pnl_lamports":-7602351,"timestamp":1750199993}
{"mint":"EieqGdcJMwRuqVnSbfULwTJzmYhQRq8JT9idQkCzpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":177810409,"tokens_received":0,"buy_slot":347477727,"sell_lamports":138039943,"tokens_sold":0,"sell_slot":347477745,"pnl_lamports":-39770466,"timestamp":1750200119}
{"mint":"9rNGPghfS2THjDaJQQKmiJM3o26ykyatVZn39fL4pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170546532,"tokens_received":0,"buy_slot":347479142,"sell_lamports":158050354,"tokens_sold":0,"sell_slot":347479146,"pnl_lamports":-12496178,"timestamp":1750200681}
{"mint":"9ceRs9ZwsB3mTAwFGdQ5qSGZeQmgLA8WLMniHJEApump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":171638164,"tokens_received":0,"buy_slot":347479151,"sell_lamports":139039943,"tokens_sold":5000000000000,"sell_slot":347479171,"pnl_lamports":-32598221,"timestamp":1750200691}
{"mint":"CDTDTqqJmL1ft8a3gcPuYBvMXEA9zXNWhoFVEDaipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":146002227,"tokens_received":0,"buy_slot":347487368,"sell_lamports":141102292,"tokens_sold":5000000000000,"sell_slot":347487523,"pnl_lamports":-4899935,"timestamp":1750204042}
{"mint":"Dqp6ats2iFAwXHZrBpV2NsXuo5hW41FLLD8NNRdWpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163592683,"tokens_received":0,"buy_slot":347488212,"sell_lamports":136639951,"tokens_sold":0,"sell_slot":347488244,"pnl_lamports":-26952732,"timestamp":1750204329}
{"mint":"8Hc3xZydnjDWnmicCiDn57kbLSJFM45EdxZPhSZipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185930736,"tokens_received":0,"buy_slot":347488323,"sell_lamports":177546077,"tokens_sold":0,"sell_slot":347488380,"pnl_lamports":-8384659,"timestamp":1750204384}
{"mint":"DLWq4iKhQintex7j8kghjrFE4H6SPp95aeBRHW9ppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":148677265,"tokens_received":0,"buy_slot":347488678,"sell_lamports":141744160,"tokens_sold":0,"sell_slot":347488831,"pnl_lamports":-6933105,"timestamp":1750204567}
{"mint":"34wqHmwFKdC671bBmE5weWNeXCWQeHHXBaumgnuBpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347491416,"sell_lamports":0,"tokens_sold":0,"sell_slot":347491421,"pnl_lamports":-5000,"timestamp":1750205606}
{"mint":"5Q4QYSeRtVeqoKKv2i7jABuhiMiYJNSrSzHqKmmrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":148754165,"tokens_received":0,"buy_slot":347491652,"sell_lamports":141819538,"tokens_sold":5000000000000,"sell_slot":347491808,"pnl_lamports":-6934627,"timestamp":1750205760}
{"mint":"CALrEKfSxko4gTgtpxxhjuHJbA35k6MzudADcUteo6xM","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180453395,"tokens_received":0,"buy_slot":347491892,"sell_lamports":157616043,"tokens_sold":0,"sell_slot":347491903,"pnl_lamports":-22837352,"timestamp":1750205797}
{"mint":"GoLqWvc1DqQGDNpbjx5XFXRqdapcdStXR9CUnvAXKRak","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":147230609,"tokens_received":0,"buy_slot":347491991,"sell_lamports":140785927,"tokens_sold":5000000000000,"sell_slot":347492016,"pnl_lamports":-6444682,"timestamp":1750205843}
{"mint":"DnC6p9dHZnFwGtPy8FwkSaifswVCDVTBQS5zKoqcpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347492108,"sell_lamports":0,"tokens_sold":0,"sell_slot":347492127,"pnl_lamports":-5000,"timestamp":1750205887}
{"mint":"ERy1mi86Yj5bvwkj6ZcPTENqKtMaMz9JiQMHLwbEPpDU","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185361594,"tokens_received":0,"buy_slot":347492217,"sell_lamports":138039950,"tokens_sold":5000000000000,"sell_slot":347492349,"pnl_lamports":-47321644,"timestamp":1750205976}
{"mint":"4z8vxGoPS2HMnNz61WpqqCT2Grpb5exVqRDbsZwCpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":156439422,"tokens_received":0,"buy_slot":347493397,"sell_lamports":136039952,"tokens_sold":0,"sell_slot":347493422,"pnl_lamports":-20399470,"timestamp":1750206407}
{"mint":"5d1sEe8zsjkssyQtYau6ALpCgtiAEiptvAMFPdZppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":148842226,"tokens_received":0,"buy_slot":347493500,"sell_lamports":137945459,"tokens_sold":0,"sell_slot":347493653,"pnl_lamports":-10896767,"timestamp":1750206501}
{"mint":"2xQeszcoYrKsMmnobE8hQYR1AD5EDKBfUcXZFUtCpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":153189012,"tokens_received":0,"buy_slot":347496103,"sell_lamports":134460553,"tokens_sold":5000000000000,"sell_slot":347496508,"pnl_lamports":-18728459,"timestamp":1750207644}
{"mint":"AsFHiuqHUU4mLDE6xUii4UBekk7eXacgR15TXienpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":198747842,"tokens_received":0,"buy_slot":347496774,"sell_lamports":181218546,"tokens_sold":0,"sell_slot":347496777,"pnl_lamports":-17529296,"timestamp":1750207752}
{"mint":"BpaSVkodWrV1ZbxRpGDu5E4VMvXiZjpcykmvYf6ypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":157954942,"tokens_received":0,"buy_slot":347496856,"sell_lamports":142917329,"tokens_sold":5000000000000,"sell_slot":347496862,"pnl_lamports":-15037613,"timestamp":1750207788}
{"mint":"Fu6PgDj4RkjhyvhG5zVpjQNHPx3A4bUo52CCB8UndmAk","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":186653950,"tokens_received":0,"buy_slot":347496943,"sell_lamports":171048041,"tokens_sold":5000000000000,"sell_slot":347496944,"pnl_lamports":-15605909,"timestamp":1750207821}
{"mint":"5oUFJFHK84hZfMZqPF2BRfWgEh1PfaToM1PzADmepump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":148978986,"tokens_received":0,"buy_slot":347497258,"sell_lamports":134166999,"tokens_sold":0,"sell_slot":347497662,"pnl_lamports":-14811987,"timestamp":1750208108}
{"mint":"H7jqp3g6uTcLydmWQBCnrRnRpzf1H9CkPu4sVURqpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154580474,"tokens_received":0,"buy_slot":347501008,"sell_lamports":135885940,"tokens_sold":0,"sell_slot":347501020,"pnl_lamports":-18694534,"timestamp":1750209465}
{"mint":"EyNzeVDoFPPXfGhDh9LcCrgt2oPjjh9qGcALGzcopump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347502059,"sell_lamports":0,"tokens_sold":0,"sell_slot":347502062,"pnl_lamports":-5000,"timestamp":1750209879}
{"mint":"BuEKpmaJiXgnjZzH8PckLMzNGkFBkZDv1DekgJofpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165885512,"tokens_received":0,"buy_slot":347502067,"sell_lamports":136684668,"tokens_sold":5000000000000,"sell_slot":347502071,"pnl_lamports":-29200844,"timestamp":1750209883}
{"mint":"AYR3GRRJ5VRwPo591GdmHdFkSyaAfwYHvMZU6cmYpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":161639469,"tokens_received":0,"buy_slot":347502077,"sell_lamports":138479030,"tokens_sold":5000000000000,"sell_slot":347502085,"pnl_lamports":-23160439,"timestamp":1750209888}
{"mint":"DRVqp9uBWfScfjxC2wj3ADbK8K8Ar1WuC6HKA5Aipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":161000817,"tokens_received":0,"buy_slot":347502087,"sell_lamports":134039952,"tokens_sold":0,"sell_slot":347502159,"pnl_lamports":-26960865,"timestamp":1750209919}
{"mint":"H9oC98SM2MnFhHxujKZbayjecf7YM8qiPkd111KJpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":164582588,"tokens_received":0,"buy_slot":347502521,"sell_lamports":136819798,"tokens_sold":0,"sell_slot":347502533,"pnl_lamports":-27762790,"timestamp":1750210066}
{"mint":"5sq6xDoiqnTEXToLpA4ZavJW3UmwuUXeR9GgzeHdpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185047505,"tokens_received":0,"buy_slot":347502738,"sell_lamports":152106430,"tokens_sold":0,"sell_slot":347502784,"pnl_lamports":-32941075,"timestamp":1750210166}
{"mint":"4ZPs2t1TfaXX5QN6Y6KhoKwbs2hZsUFTTYFJWmTQpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":162337130,"tokens_received":0,"buy_slot":347503132,"sell_lamports":152857570,"tokens_sold":0,"sell_slot":347503148,"pnl_lamports":-9479560,"timestamp":1750210310}
{"mint":"aG8kT6wwLZMrrJ55iQ9HanphmrMnstd597QRyCrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":177645442,"tokens_received":0,"buy_slot":347503403,"sell_lamports":162997634,"tokens_sold":5000000000000,"sell_slot":347503404,"pnl_lamports":-14647808,"timestamp":1750210414}
{"mint":"E5rzqJzBvFDx4zq6mTTEyV5ou8YQg92dj1CeTW9ipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154851919,"tokens_received":0,"buy_slot":347503408,"sell_lamports":140151244,"tokens_sold":5000000000000,"sell_slot":347503585,"pnl_lamports":-14700675,"timestamp":1750210487}
{"mint":"8Jbe8VgaLEVpmE8DRYAsi8c8Rm7WxSfaqYRwykt1pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180767390,"tokens_received":0,"buy_slot":347503589,"sell_lamports":241765820,"tokens_sold":5000000000000,"sell_slot":347503597,"pnl_lamports":60998430,"timestamp":1750210492}
{"mint":"3BoomWazAARD3yLrtUfvqmkja23GEonVzs6NoJecpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":164766466,"tokens_received":0,"buy_slot":347504654,"sell_lamports":169873045,"tokens_sold":5000000000000,"sell_slot":347504658,"pnl_lamports":5106579,"timestamp":1750210914}
{"mint":"CHeFZPQZ6X4rL6sTvQZVxk7atywWXgv9uq51Uyivpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":169334341,"tokens_received":0,"buy_slot":347504662,"sell_lamports":162480810,"tokens_sold":0,"sell_slot":347504703,"pnl_lamports":-6853531,"timestamp":1750210932}
{"mint":"HhELATZZia8u2KRRrZYLuQjCweCHGf7LS4pMcNmEpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347504706,"sell_lamports":0,"tokens_sold":0,"sell_slot":347504709,"pnl_lamports":-5000,"timestamp":1750210936}
{"mint":"FvaqGvRv4mCm8yRrLe3mwXceUzcubVsc4qbMWXtTpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":177139433,"tokens_received":0,"buy_slot":347504722,"sell_lamports":301752412,"tokens_sold":5000000000000,"sell_slot":347504728,"pnl_lamports":124612979,"timestamp":1750210943}
{"mint":"GPpZtiJ7LEhTjERnuKLaNC2G5i2hXdBJCsjvGGQ4pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":164021069,"tokens_received":0,"buy_slot":347504737,"sell_lamports":139266269,"tokens_sold":5000000000000,"sell_slot":347504743,"pnl_lamports":-24754800,"timestamp":1750210949}
{"mint":"G8zfcGneV7bX3zfme9AfZeYvYHLe3ayMGgyn8y1Tpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165029947,"tokens_received":0,"buy_slot":347504760,"sell_lamports":162676058,"tokens_sold":5000000000000,"sell_slot":347504776,"pnl_lamports":-2353889,"timestamp":1750210963}
{"mint":"HAVgh5JDKdfW5S6QKUsraLTbr9YduR5YaU8TLCVcpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":179279906,"tokens_received":0,"buy_slot":347504795,"sell_lamports":170518209,"tokens_sold":5000000000000,"sell_slot":347504796,"pnl_lamports":-8761697,"timestamp":1750210970}
{"mint":"AZjq4U89GDYfoD1cSdMccoQE54476KM298M2aPQoyc4H","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347504808,"sell_lamports":0,"tokens_sold":0,"sell_slot":347504814,"pnl_lamports":-5000,"timestamp":1750210978}
{"mint":"9rmEegGhmcipQ91mSdUHLs9JgNHYqhieHXcjW4Gqpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":175512577,"tokens_received":0,"buy_slot":347504817,"sell_lamports":138039951,"tokens_sold":5000000000000,"sell_slot":347504859,"pnl_lamports":-37472626,"timestamp":1750210996}
{"mint":"79WYGn7ANggpawF9QZtFY9XXkfyJwkkZ5jzSNhPMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":169977975,"tokens_received":0,"buy_slot":347504873,"sell_lamports":146010644,"tokens_sold":0,"sell_slot":347504892,"pnl_lamports":-23967331,"timestamp":1750211010}
{"mint":"GiXb7UQjBjS412uWhbPwBi7MrGmgdqxxcidvNW8dpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":175206230,"tokens_received":0,"buy_slot":347504896,"sell_lamports":169400726,"tokens_sold":0,"sell_slot":347504903,"pnl_lamports":-5805504,"timestamp":1750211014}
{"mint":"CVkEbMhDHe6JawXvmx8eanHHYcZkekHtd13QBpQKpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":160712566,"tokens_received":0,"buy_slot":347504908,"sell_lamports":138662517,"tokens_sold":0,"sell_slot":347505164,"pnl_lamports":-22050049,"timestamp":1750211119}
{"mint":"Aq1wu7GJ4yKGe3SSNvQQnSrU2v1aCs3NSpsRHvocpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":162002474,"tokens_received":0,"buy_slot":347505175,"sell_lamports":147392215,"tokens_sold":5000000000000,"sell_slot":347505196,"pnl_lamports":-14610259,"timestamp":1750211132}
{"mint":"92TWc7kEjprezUTZ2VViz6Aw37fRxJsfLhjRDGVZpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170819668,"tokens_received":0,"buy_slot":347505209,"sell_lamports":159948483,"tokens_sold":0,"sell_slot":347505295,"pnl_lamports":-10871185,"timestamp":1750211172}
{"mint":"8jHMSkXEQiSchfURmxRBBuk6bjXhgSCkfk5nTjRjpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163799593,"tokens_received":0,"buy_slot":347505300,"sell_lamports":177434405,"tokens_sold":0,"sell_slot":347505321,"pnl_lamports":13634812,"timestamp":1750211181}
{"mint":"1Tp3k5DRKBdQ3B5RYm7CvKLGwZNSFUVUEFBGiC1YFKc","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":175654766,"tokens_received":0,"buy_slot":347506777,"sell_lamports":177981016,"tokens_sold":5000000000000,"sell_slot":347506789,"pnl_lamports":2326250,"timestamp":1750211770}
{"mint":"CDUcTUQMNiYhX3E9vjLc94uau6XdojLQPkE9sYtwC3v9","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":147277924,"tokens_received":0,"buy_slot":347506795,"sell_lamports":138039952,"tokens_sold":5000000000000,"sell_slot":347506857,"pnl_lamports":-9237972,"timestamp":1750211796}
{"mint":"2QmaWwjheohrLUusSB1Zz1fmMcgwSPrfcdjn6sDspump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":167349441,"tokens_received":0,"buy_slot":347507551,"sell_lamports":157050364,"tokens_sold":5000000000000,"sell_slot":347507573,"pnl_lamports":-10299077,"timestamp":1750212084}
{"mint":"9VpmheJu714qmwbWabkWaB3nDCzSjKsvXNduYsaTpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347507578,"sell_lamports":0,"tokens_sold":0,"sell_slot":347507583,"pnl_lamports":-5000,"timestamp":1750212087}
{"mint":"3xP6Ju9pWJ8sRLSc8PxNR1L328vUjC6zYLEjtCkppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180576243,"tokens_received":0,"buy_slot":347507587,"sell_lamports":142543755,"tokens_sold":5000000000000,"sell_slot":347507591,"pnl_lamports":-38032488,"timestamp":1750212090}
{"mint":"9q5bCnBW6TKNZZeMEW58cLKNVQses6Z7nGMaABCLPXBn","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":178158085,"tokens_received":0,"buy_slot":347507593,"sell_lamports":191063682,"tokens_sold":5000000000000,"sell_slot":347507604,"pnl_lamports":12905597,"timestamp":1750212096}
{"mint":"FWQUcSoU2eGshPGc2hs2MG4sqs2FoT1qXzcoFV2rpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168587994,"tokens_received":0,"buy_slot":347507614,"sell_lamports":183743684,"tokens_sold":0,"sell_slot":347507617,"pnl_lamports":15155690,"timestamp":1750212102}
{"mint":"9ft48QMXug18oo7KghoYcixXFVVVae1Bjhfzgdc8pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":162562531,"tokens_received":0,"buy_slot":347507626,"sell_lamports":149700840,"tokens_sold":5000000000000,"sell_slot":347507641,"pnl_lamports":-12861691,"timestamp":1750212111}
{"mint":"AykmqoiLDHioQxJcgREdThzt6uL2t4kgMjwAhqsvak1N","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347509026,"sell_lamports":0,"tokens_sold":0,"sell_slot":347509036,"pnl_lamports":-5000,"timestamp":1750212674}
{"mint":"FeHNSjVCDiJuDV8ZZdnKTvciz53CWwepUW84XSwjpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":326240564,"tokens_received":0,"buy_slot":347509039,"sell_lamports":453783733,"tokens_sold":10000000000000,"sell_slot":347509043,"pnl_lamports":127543169,"timestamp":1750212676}
{"mint":"9kXVDAQdyiD1shTMgPLXEkQ3fJPVSb2D3ZtnEz4Fsrtz","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":302587330,"tokens_received":0,"buy_slot":347509044,"sell_lamports":278694782,"tokens_sold":10000000000000,"sell_slot":347509061,"pnl_lamports":-23892548,"timestamp":1750212683}
{"mint":"1dpF1Zirs9asqxuXQCFW9Pk7SgbQS1ZkgCdGnWQxFLg","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288086615,"tokens_received":0,"buy_slot":347509081,"sell_lamports":278411315,"tokens_sold":10000000000000,"sell_slot":347509483,"pnl_lamports":-9675300,"timestamp":1750212852}
{"mint":"2fRJApP8puJzynzYihgp2G1CLeHVZrGN4TczDxQ1pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":327159115,"tokens_received":0,"buy_slot":347509487,"sell_lamports":278392945,"tokens_sold":10000000000000,"sell_slot":347509535,"pnl_lamports":-48766170,"timestamp":1750212873}
{"mint":"vkTRLDaFZ8o5NAoE3pD49sCLP8gAjQw6bipp7V9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":358376754,"tokens_received":0,"buy_slot":347509540,"sell_lamports":351989860,"tokens_sold":10000000000000,"sell_slot":347509541,"pnl_lamports":-6386894,"timestamp":1750212876}
{"mint":"9P9zJWnEVzjfHeiLZVKA7sm4i3xDM7V31RGtgkgpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":311611639,"tokens_received":0,"buy_slot":347509544,"sell_lamports":301452110,"tokens_sold":10000000000000,"sell_slot":347509551,"pnl_lamports":-10159529,"timestamp":1750212880}
{"mint":"7x4KXgQ3h9tULjdQe9jz6dWN2tDZ9uuMzHogASe9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":337528122,"tokens_received":0,"buy_slot":347509560,"sell_lamports":278392928,"tokens_sold":0,"sell_slot":347509610,"pnl_lamports":-59135194,"timestamp":1750212905}
{"mint":"CU7px7F94bgQP1M4N2pZ1NTf3Wy39eLGGz1bpDaq1aEg","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":391627824,"tokens_received":0,"buy_slot":347509627,"sell_lamports":278392942,"tokens_sold":0,"sell_slot":347509704,"pnl_lamports":-113234882,"timestamp":1750212942}
{"mint":"AputqVucipkN6SKtKSJH1Gt3gd5Xcr5GYCcxgJEVpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":178512891,"tokens_received":0,"buy_slot":347511703,"sell_lamports":138039943,"tokens_sold":5000000000000,"sell_slot":347511745,"pnl_lamports":-40472948,"timestamp":1750213761}
{"mint":"FuSuvNP31ugJ7a3M6j2dxDA686ANEWdLrNtYgCDMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":191562495,"tokens_received":0,"buy_slot":347511757,"sell_lamports":209372956,"tokens_sold":0,"sell_slot":347511761,"pnl_lamports":17810461,"timestamp":1750213768}
{"mint":"Bx6GthauvET9wPzEX9dhYZFQZREQaZuG2yQNB9jepump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":169450415,"tokens_received":0,"buy_slot":347511780,"sell_lamports":144967615,"tokens_sold":5000000000000,"sell_slot":347511966,"pnl_lamports":-24482800,"timestamp":1750213850}
{"mint":"FYiszCW3gFAG8D1hwU4jKyhdWRwkJaynVnv7KXgVpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168313880,"tokens_received":0,"buy_slot":347512476,"sell_lamports":138039951,"tokens_sold":0,"sell_slot":347512547,"pnl_lamports":-30273929,"timestamp":1750214084}
{"mint":"5Qw1dS2AMFPUcmvgC7xbpaa9LAewHJSNePdSP1Wf2jqJ","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174767935,"tokens_received":0,"buy_slot":347512555,"sell_lamports":160765646,"tokens_sold":5000000000000,"sell_slot":347512584,"pnl_lamports":-14002289,"timestamp":1750214098}
{"mint":"DeW5KqFmH4mBe4nSgpiQm47c139GfJSsUVX4RumApump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":175458276,"tokens_received":0,"buy_slot":347512593,"sell_lamports":138039951,"tokens_sold":5000000000000,"sell_slot":347512657,"pnl_lamports":-37418325,"timestamp":1750214126}
{"mint":"FoFq5ffiipdCoFEejKGkJHDSHKPUPd2N1wrTjFUDoHLt","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185361583,"tokens_received":0,"buy_slot":347512941,"sell_lamports":174028315,"tokens_sold":0,"sell_slot":347512964,"pnl_lamports":-11333268,"timestamp":1750214249}
{"mint":"NNT2Q5cpnKSBbhFSyTyQtZekKyE8sk6bCNfApiipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":166320932,"tokens_received":0,"buy_slot":347512976,"sell_lamports":167804891,"tokens_sold":0,"sell_slot":347512986,"pnl_lamports":1483959,"timestamp":1750214258}
{"mint":"8veaQPsWZ2mDwphJQEs2RbZMh8eUiN7FJGriUkFUpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180360057,"tokens_received":0,"buy_slot":347512998,"sell_lamports":185120939,"tokens_sold":0,"sell_slot":347513010,"pnl_lamports":4760882,"timestamp":1750214267}
{"mint":"GZk7aLb6fwysRZwHFvNRuDzpTbSaazfStEMmarC8pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154829405,"tokens_received":0,"buy_slot":347513857,"sell_lamports":138711380,"tokens_sold":0,"sell_slot":347513894,"pnl_lamports":-16118025,"timestamp":1750214623}
{"mint":"61SBrndyLQweFvYz1RUC6C9rSwLUcdhDrXspQqz5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168080857,"tokens_received":0,"buy_slot":347513906,"sell_lamports":157050364,"tokens_sold":5000000000000,"sell_slot":347513924,"pnl_lamports":-11030493,"timestamp":1750214636}
{"mint":"Fp833QMb3YRWHrDoyGnndSq8GoPjz1ZMQeNgwJQY4ynL","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":196274018,"tokens_received":0,"buy_slot":347513929,"sell_lamports":138080471,"tokens_sold":0,"sell_slot":347514014,"pnl_lamports":-58193547,"timestamp":1750214671}
{"mint":"134tbGFATvhQwDUYfp7Dpju79KxmTCYZq2cpGypWpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":175425825,"tokens_received":0,"buy_slot":347514019,"sell_lamports":138085938,"tokens_sold":5000000000000,"sell_slot":347514332,"pnl_lamports":-37339887,"timestamp":1750214800}
{"mint":"6ygocSg36t7su8b52NZqgwbbLHcVG5vii8saSJJvpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":190116418,"tokens_received":0,"buy_slot":347515425,"sell_lamports":194304301,"tokens_sold":5000000000000,"sell_slot":347515426,"pnl_lamports":4187883,"timestamp":1750215240}
{"mint":"FjTSrGt8N1PTWC8ywWjKjuChFGKtaJ3euuNo6qRxpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":167309527,"tokens_received":0,"buy_slot":347515428,"sell_lamports":138039951,"tokens_sold":0,"sell_slot":347515479,"pnl_lamports":-29269576,"timestamp":1750215260}
{"mint":"EAayig1Cqk1eNH9RYJZAsEfEdAcMb3K4ZiXDLuD6pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174767935,"tokens_received":0,"buy_slot":347515485,"sell_lamports":167318194,"tokens_sold":0,"sell_slot":347515513,"pnl_lamports":-7449741,"timestamp":1750215274}
{"mint":"Gi4HkMfywxGwHVJWCDTJsKb1FcKJKPwEQdjozBknpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347515518,"sell_lamports":0,"tokens_sold":0,"sell_slot":347515532,"pnl_lamports":-5000,"timestamp":1750215281}
{"mint":"GYP4aTcDaVkKm7YeDDLmcA2Qni247trgsWt9RiQ8pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":193516428,"tokens_received":0,"buy_slot":347515544,"sell_lamports":224002490,"tokens_sold":0,"sell_slot":347515550,"pnl_lamports":30486062,"timestamp":1750215288}
{"mint":"3gABeNGS5Fj6FEsNwEZtawXCvrrsYHRfABwJEwHqwbDW","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":159118273,"tokens_received":0,"buy_slot":347515563,"sell_lamports":145974801,"tokens_sold":5000000000000,"sell_slot":347515579,"pnl_lamports":-13143472,"timestamp":1750215299}
{"mint":"DCGuj3YVS3dY9BmxMQBn9ahPVn9pp33wDZp53XHcpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168321400,"tokens_received":0,"buy_slot":347515584,"sell_lamports":138039951,"tokens_sold":5000000000000,"sell_slot":347515618,"pnl_lamports":-30281449,"timestamp":1750215315}
{"mint":"GXUHcwp9HDrwoqDHiBPtQeaEpKuL5TUeBiRYGUp8M4Ve","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154536409,"tokens_received":0,"buy_slot":347515624,"sell_lamports":147487282,"tokens_sold":5000000000000,"sell_slot":347515702,"pnl_lamports":-7049127,"timestamp":1750215349}
{"mint":"H3uqskXom9Yo7A5qBLyK55aLxc3nzgsC2tZ4cXC2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":145838296,"tokens_received":0,"buy_slot":347516120,"sell_lamports":139017358,"tokens_sold":5000000000000,"sell_slot":347516151,"pnl_lamports":-6820938,"timestamp":1750215527}
{"mint":"4z7EMkVbcgwjBjrCmB2W7e1xbdhpTU4EW4M5fwiTf8rn","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347516155,"sell_lamports":0,"tokens_sold":0,"sell_slot":347516166,"pnl_lamports":-5000,"timestamp":1750215533}
{"mint":"43DatYePiw5wAx6S5yXHEwcyqtLKMDhQL9ZpNMmJdiqJ","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":196274018,"tokens_received":0,"buy_slot":347516172,"sell_lamports":138039950,"tokens_sold":0,"sell_slot":347516245,"pnl_lamports":-58234068,"timestamp":1750215566}
{"mint":"2BNFmyfcnSjnYgwiRMMwNvrMEHG4EMFRkPJSP8fwpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":164292683,"tokens_received":0,"buy_slot":347516251,"sell_lamports":157050364,"tokens_sold":0,"sell_slot":347516328,"pnl_lamports":-7242319,"timestamp":1750215599}
{"mint":"Epwg2GGZ4ccNeFCUudqUjtxzzWFcQn3W6vz1pgEiLCAg","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185361583,"tokens_received":0,"buy_slot":347516337,"sell_lamports":138039950,"tokens_sold":0,"sell_slot":347516388,"pnl_lamports":-47321633,"timestamp":1750215623}
{"mint":"4resdanSkUpY7Eru1jXbUDpn9XqsEkJRCoLUANzGpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":190977591,"tokens_received":0,"buy_slot":347516390,"sell_lamports":191111142,"tokens_sold":0,"sell_slot":347516392,"pnl_lamports":133551,"timestamp":1750215625}
{"mint":"Buwa3feVdgVZG1sSYYeY4yZhPtEdw72YLS7WqtrFpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":192382339,"tokens_received":0,"buy_slot":347516404,"sell_lamports":167931074,"tokens_sold":5000000000000,"sell_slot":347516409,"pnl_lamports":-24451265,"timestamp":1750215632}
{"mint":"DCR7ZZoFM5KDWjePbVSGXHEK2X7G9JL5y3EkJqVrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":159901727,"tokens_received":0,"buy_slot":347516437,"sell_lamports":138189808,"tokens_sold":0,"sell_slot":347516440,"pnl_lamports":-21711919,"timestamp":1750215644}
{"mint":"E5Y7atNeq6s4FX2R9Nw2XktMoVPtiFss5HNycQMKpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":160596506,"tokens_received":0,"buy_slot":347516458,"sell_lamports":147392214,"tokens_sold":5000000000000,"sell_slot":347516466,"pnl_lamports":-13204292,"timestamp":1750215655}
{"mint":"CKvzjdVm5jfBymN3LHUZsNQi9fhExaFznwZYTAd9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144992082,"tokens_received":0,"buy_slot":347516767,"sell_lamports":138251038,"tokens_sold":5000000000000,"sell_slot":347516793,"pnl_lamports":-6741044,"timestamp":1750215786}
{"mint":"4uyLURYRqZHSgWz8xesi7u258QsK2QeojU6mwcgppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180560153,"tokens_received":0,"buy_slot":347516795,"sell_lamports":225265367,"tokens_sold":5000000000000,"sell_slot":347516801,"pnl_lamports":44705214,"timestamp":1750215790}
{"mint":"3rpp1AkGnSiL2Sn8LMhtm128V9JnDZNj7ktb3aqupump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165817451,"tokens_received":0,"buy_slot":347516802,"sell_lamports":167423757,"tokens_sold":5000000000000,"sell_slot":347516851,"pnl_lamports":1606306,"timestamp":1750215811}
{"mint":"H1BCkUB7UtjESVcHXDwUAnR4mR3aXp7q4V7JmXAipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":176992067,"tokens_received":0,"buy_slot":347517155,"sell_lamports":176243609,"tokens_sold":0,"sell_slot":347517157,"pnl_lamports":-748458,"timestamp":1750215931}
{"mint":"769QBKCMDFEhyA4kxacJ9VPYiZpVk1y76LGVgN9bpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168128579,"tokens_received":0,"buy_slot":347517173,"sell_lamports":148493513,"tokens_sold":0,"sell_slot":347517178,"pnl_lamports":-19635066,"timestamp":1750215940}
{"mint":"BgBiSYYZ3YC7xzkVaCrx4cWpr2iqrsXFt2N66VYMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154439412,"tokens_received":0,"buy_slot":347517185,"sell_lamports":139228417,"tokens_sold":5000000000000,"sell_slot":347517190,"pnl_lamports":-15210995,"timestamp":1750215944}
{"mint":"24rpR3wxbd6PHNiXVzYk82Z1KVkyybctmRA98DXjpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154829492,"tokens_received":0,"buy_slot":347517203,"sell_lamports":138217201,"tokens_sold":0,"sell_slot":347517210,"pnl_lamports":-16612291,"timestamp":1750215953}
{"mint":"8qJ2z1Hi4maiTHyP9a6dzsTNPwvnJ8pvvoshpUk1Sgny","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174552930,"tokens_received":0,"buy_slot":347517218,"sell_lamports":187522131,"tokens_sold":5000000000000,"sell_slot":347517220,"pnl_lamports":12969201,"timestamp":1750215957}
{"mint":"DcDnAQqjLR8g5sj3tDTFtjxFXzsaYVYyNmfpZueFpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":176861169,"tokens_received":0,"buy_slot":347517222,"sell_lamports":155874037,"tokens_sold":5000000000000,"sell_slot":347517225,"pnl_lamports":-20987132,"timestamp":1750215960}
{"mint":"CN6b4XxEaHCyUb2Ey2gpV1DY7AHzWrUCtuopMZkm3mGK","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174024140,"tokens_received":0,"buy_slot":347517227,"sell_lamports":160251850,"tokens_sold":5000000000000,"sell_slot":347517237,"pnl_lamports":-13772290,"timestamp":1750215965}
{"mint":"DFQfq3YxQAfFg5BUcLKKJ2TnjHMNGuYCdgsYKtC2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":151914813,"tokens_received":0,"buy_slot":347517240,"sell_lamports":142488375,"tokens_sold":0,"sell_slot":347517318,"pnl_lamports":-9426438,"timestamp":1750215997}
{"mint":"GaAAvMGR8TNECm9LN7BhHJGHsMSZE9H2bFR5eeFvpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":166320932,"tokens_received":0,"buy_slot":347517333,"sell_lamports":159232051,"tokens_sold":0,"sell_slot":347517344,"pnl_lamports":-7088881,"timestamp":1750216007}
{"mint":"Dh3uEWobcyERQJZGFdavTdNPxgKjCWrBj91YWUv6d7xM","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170619703,"tokens_received":0,"buy_slot":347517889,"sell_lamports":163252094,"tokens_sold":0,"sell_slot":347517891,"pnl_lamports":-7367609,"timestamp":1750216229}
{"mint":"69uHdZNRzPYFJvRifAhfUqnd7UePPfEhfgqzEbehpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":166320932,"tokens_received":0,"buy_slot":347517893,"sell_lamports":437980310,"tokens_sold":5000000000000,"sell_slot":347517899,"pnl_lamports":271659378,"timestamp":1750216232}
{"mint":"6bFKPWTUD6Xep3M3BWdVxGrRM7kLoe5uSmJgBHK9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":151717366,"tokens_received":0,"buy_slot":347517908,"sell_lamports":142497725,"tokens_sold":0,"sell_slot":347517986,"pnl_lamports":-9219641,"timestamp":1750216267}
{"mint":"8dpmBfMGcsFdNRT3B3k112X8QnpgPGeTjVcEwuF9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":150175534,"tokens_received":0,"buy_slot":347518199,"sell_lamports":141266384,"tokens_sold":5000000000000,"sell_slot":347518257,"pnl_lamports":-8909150,"timestamp":1750216376}
{"mint":"9aDtA41UDhBUnts6V1pGR4uM2zAnewWG4FxuXGPBpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":177686389,"tokens_received":0,"buy_slot":347518267,"sell_lamports":179128076,"tokens_sold":0,"sell_slot":347518277,"pnl_lamports":1441687,"timestamp":1750216385}
{"mint":"DoBuLF4oGf4c7pYYupuXxWTfu8ZhrKexUUPSF1o2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":153471252,"tokens_received":0,"buy_slot":347518284,"sell_lamports":138039943,"tokens_sold":5000000000000,"sell_slot":347518329,"pnl_lamports":-15431309,"timestamp":1750216406}
{"mint":"5vHnVZRetzwVoGq7rkXtgxWpH6YANDeAaaRa7bx4pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":184101706,"tokens_received":0,"buy_slot":347518339,"sell_lamports":269163656,"tokens_sold":5000000000000,"sell_slot":347518344,"pnl_lamports":85061950,"timestamp":1750216412}
{"mint":"85ArwijitJ8pUqpQNiP6zrrWgxUYebyRzVDyWoX5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":189889436,"tokens_received":0,"buy_slot":347518349,"sell_lamports":152248365,"tokens_sold":0,"sell_slot":347518354,"pnl_lamports":-37641071,"timestamp":1750216416}
{"mint":"C1NdJvJmxnunUPUWqPG9AcjRsH68XmkYDPBTfpJjpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170507468,"tokens_received":0,"buy_slot":347518366,"sell_lamports":148344253,"tokens_sold":0,"sell_slot":347518370,"pnl_lamports":-22163215,"timestamp":1750216422}
{"mint":"7RMQre4vQMtQdjWJt5SkRGYHBby4EKxpgEhQrWW3pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":202376021,"tokens_received":0,"buy_slot":347518379,"sell_lamports":193518242,"tokens_sold":5000000000000,"sell_slot":347518384,"pnl_lamports":-8857779,"timestamp":1750216428}
{"mint":"A9Tabone9NJ3sK193DjSJvT7ShGKTxpmXktFW5aYpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":158382562,"tokens_received":0,"buy_slot":347518401,"sell_lamports":147392215,"tokens_sold":0,"sell_slot":347518409,"pnl_lamports":-10990347,"timestamp":1750216438}
{"mint":"5mrnPovjnpypbebb7mvUoVmCEmyzpHG5kufvnaSZpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170609809,"tokens_received":0,"buy_slot":347518442,"sell_lamports":153843546,"tokens_sold":0,"sell_slot":347518452,"pnl_lamports":-16766263,"timestamp":1750216455}
{"mint":"DtrrKkCah7iXtwPUYokDymTCRgyycANwipAFvXEjFpLE","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154536400,"tokens_received":0,"buy_slot":347518932,"sell_lamports":147487282,"tokens_sold":0,"sell_slot":347519010,"pnl_lamports":-7049118,"timestamp":1750216678}
{"mint":"5rjvHdcF2B1ArjmFB9RJiVxP2tCVPN4bSdrZGWrkpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":181826373,"tokens_received":0,"buy_slot":347519017,"sell_lamports":144277064,"tokens_sold":0,"sell_slot":347519022,"pnl_lamports":-37549309,"timestamp":1750216682}
{"mint":"4YobXovMLVeEqVGzs3oPAe3JQYEywKsgG2CQu1XEpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154738234,"tokens_received":0,"buy_slot":347519031,"sell_lamports":138045462,"tokens_sold":5000000000000,"sell_slot":347519040,"pnl_lamports":-16692772,"timestamp":1750216690}
{"mint":"7Q4FJRc8vgAMSJAdgUtA5os4MYEjEBA8mydvTCQbpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347519050,"sell_lamports":0,"tokens_sold":0,"sell_slot":347519063,"pnl_lamports":-5000,"timestamp":1750216699}
{"mint":"7zT8ZNqJtMmSXgAhmCPRXuiTyQEToHYWcicecbiypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":180873334,"tokens_received":0,"buy_slot":347519352,"sell_lamports":175450475,"tokens_sold":0,"sell_slot":347519353,"pnl_lamports":-5422859,"timestamp":1750216817}
{"mint":"6DrHbBvBPQTyLcwwWZFYiHnAbcqfwR7bx9iUK8eXpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174767935,"tokens_received":0,"buy_slot":347519355,"sell_lamports":142699951,"tokens_sold":0,"sell_slot":347519360,"pnl_lamports":-32067984,"timestamp":1750216820}
{"mint":"HGMCJkXHyJDEd1MzJxCf6i7hSr6kKo6YCy1tWVsgpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":169365309,"tokens_received":0,"buy_slot":347519373,"sell_lamports":162022550,"tokens_sold":0,"sell_slot":347519375,"pnl_lamports":-7342759,"timestamp":1750216826}
{"mint":"7CyK5ZK3GAg8zedHGr5wCy5ptpVTshYQsm5Cz4rTpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":154407936,"tokens_received":0,"buy_slot":347519380,"sell_lamports":149430944,"tokens_sold":0,"sell_slot":347519389,"pnl_lamports":-4976992,"timestamp":1750216831}
{"mint":"Gt8XznFv5hN2K6YZyyiwF5d5UJowRKqDdzBvpt9upump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":160154094,"tokens_received":0,"buy_slot":347519392,"sell_lamports":164607744,"tokens_sold":0,"sell_slot":347519393,"pnl_lamports":4453650,"timestamp":1750216832}
{"mint":"3jGDUnFJj2tFUts4t9RLK9bLhrYE4ZZ1rRUEnLECpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":152664321,"tokens_received":0,"buy_slot":347519401,"sell_lamports":142299640,"tokens_sold":0,"sell_slot":347519480,"pnl_lamports":-10364681,"timestamp":1750216867}
{"mint":"8r6VZKDbJjTTBf6mcmfY2qAAWFRm8KsQmSr9gcyzpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185466956,"tokens_received":0,"buy_slot":347519770,"sell_lamports":179967098,"tokens_sold":0,"sell_slot":347519776,"pnl_lamports":-5499858,"timestamp":1750216987}
{"mint":"37KSUe76NCu4pUWUDX6nNpLhmmanx6Rst7MhuivPpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347519792,"sell_lamports":0,"tokens_sold":0,"sell_slot":347519797,"pnl_lamports":-5000,"timestamp":1750216995}
{"mint":"AgpMKK3Q8FBYD4CfCbFZZoUfv9kgwRprATUYcwqepump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168464311,"tokens_received":0,"buy_slot":347519836,"sell_lamports":138039951,"tokens_sold":0,"sell_slot":347519848,"pnl_lamports":-30424360,"timestamp":1750217016}
{"mint":"BHfyTvcM89GvgoqbJ8EVs2WvLtvRtYR6y2fF7Chypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347519856,"sell_lamports":0,"tokens_sold":0,"sell_slot":347519863,"pnl_lamports":-5000,"timestamp":1750217021}
{"mint":"Ds7Mcxg9x8mPAkFXvGyP2LKHZ2LFR3CQdFnRPxR154Jc","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":182110735,"tokens_received":0,"buy_slot":347519876,"sell_lamports":174516121,"tokens_sold":5000000000000,"sell_slot":347519878,"pnl_lamports":-7594614,"timestamp":1750217027}
{"mint":"8YyvwW6eB2gZSNH7pFX1sQ8TthFukDSeYE3JNXJspump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168716762,"tokens_received":0,"buy_slot":347519885,"sell_lamports":161059462,"tokens_sold":0,"sell_slot":347519886,"pnl_lamports":-7657300,"timestamp":1750217031}
{"mint":"7e8EjeHCbSzvPXuxQwsF8fpWrXYZnJDG7mdSD7mRpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":156404624,"tokens_received":0,"buy_slot":347519891,"sell_lamports":147392215,"tokens_sold":0,"sell_slot":347519909,"pnl_lamports":-9012409,"timestamp":1750217040}
{"mint":"GSbFoKiHuWSuNRRMXW6c8ExGN8XpXwzqVujbgHh5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":194272644,"tokens_received":0,"buy_slot":347519917,"sell_lamports":186436673,"tokens_sold":5000000000000,"sell_slot":347519918,"pnl_lamports":-7835971,"timestamp":1750217044}
{"mint":"91HjAWvPBhDZADUySKK4JKaeBVJsoCDChT3QhHncpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":150442710,"tokens_received":0,"buy_slot":347519942,"sell_lamports":143667871,"tokens_sold":5000000000000,"sell_slot":347519943,"pnl_lamports":-6774839,"timestamp":1750217054}
{"mint":"4jsQHqshVcfJ5wy3xBREKy5VbkXK4eCyWPT8zXicpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":149598851,"tokens_received":0,"buy_slot":347519956,"sell_lamports":141744160,"tokens_sold":0,"sell_slot":347519958,"pnl_lamports":-7854691,"timestamp":1750217060}
{"mint":"B2MMargytCabncwmDfz7JzrcGXcUhYnNPx1BWdvcpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165817461,"tokens_received":0,"buy_slot":347519975,"sell_lamports":157050364,"tokens_sold":0,"sell_slot":347519982,"pnl_lamports":-8767097,"timestamp":1750217070}
{"mint":"8VQd57yZoTNESX2MhyAuoK5YYV2Jdfij8QTJW9Vwpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":185466953,"tokens_received":0,"buy_slot":347519987,"sell_lamports":179967095,"tokens_sold":5000000000000,"sell_slot":347519988,"pnl_lamports":-5499858,"timestamp":1750217072}
{"mint":"G9ETocYppB3Ek6ew4Xy32RuhMq63UxtqRyMLsSTmpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":150167351,"tokens_received":0,"buy_slot":347520013,"sell_lamports":143204750,"tokens_sold":0,"sell_slot":347520018,"pnl_lamports":-6962601,"timestamp":1750217084}
{"mint":"4xrTSPjXxYoVE4UU1Bfpds9SvFB2ub9rdxgytmrjpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":157523807,"tokens_received":0,"buy_slot":347520019,"sell_lamports":151468252,"tokens_sold":5000000000000,"sell_slot":347520020,"pnl_lamports":-6055555,"timestamp":1750217085}
{"mint":"ErfaH7fMh1QgDbY3JpqgHt2seygUkATcwDsoX38yr2Mz","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347520036,"sell_lamports":0,"tokens_sold":0,"sell_slot":347520040,"pnl_lamports":-5000,"timestamp":1750217092}
{"mint":"BoA5ib3xAUxyMrdHkZBscY2id4q6P5ifjbW8CsZSpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165416759,"tokens_received":0,"buy_slot":347520057,"sell_lamports":164583479,"tokens_sold":0,"sell_slot":347520067,"pnl_lamports":-833280,"timestamp":1750217103}
{"mint":"F9nXCqW5u1ZH3jS35U9UcuC3HNumEqEWfx7mrdAgpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":173429492,"tokens_received":0,"buy_slot":347520089,"sell_lamports":166006254,"tokens_sold":5000000000000,"sell_slot":347520090,"pnl_lamports":-7423238,"timestamp":1750217112}
{"mint":"D4umoMz2nTrm9L2XqdbpjrJsctaZyYpvaNpQSMvbpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":172910222,"tokens_received":0,"buy_slot":347520104,"sell_lamports":166001070,"tokens_sold":0,"sell_slot":347520110,"pnl_lamports":-6909152,"timestamp":1750217121}
{"mint":"DLDZ88ZFDVm25RuqqbsSCa2NxVde2yCdC4YfmVY5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163410441,"tokens_received":0,"buy_slot":347520130,"sell_lamports":153130600,"tokens_sold":0,"sell_slot":347520153,"pnl_lamports":-10279841,"timestamp":1750217139}
{"mint":"79u2TnaXemucTGGtxrWmte8isX19u6Uebx96tz9npump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163909844,"tokens_received":0,"buy_slot":347520168,"sell_lamports":156675114,"tokens_sold":0,"sell_slot":347520171,"pnl_lamports":-7234730,"timestamp":1750217147}
{"mint":"2zMCWe2Y79hJutHmbYwy47UUPF7LN8cmQ6LQayXPpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":165773028,"tokens_received":0,"buy_slot":347520186,"sell_lamports":158501404,"tokens_sold":0,"sell_slot":347520187,"pnl_lamports":-7271624,"timestamp":1750217153}
{"mint":"EzvRjRBQoLSfiLJXuAfK2Lwj8ve6QdCg7xx2LQdupump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163589942,"tokens_received":0,"buy_slot":347520208,"sell_lamports":158655803,"tokens_sold":5000000000000,"sell_slot":347520209,"pnl_lamports":-4934139,"timestamp":1750217163}
{"mint":"E4PzfTZo5NY4Ug9oDTJJ2YZVT8yP7Y8eZx22moqUpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":158382552,"tokens_received":0,"buy_slot":347520226,"sell_lamports":151257273,"tokens_sold":0,"sell_slot":347520227,"pnl_lamports":-7125279,"timestamp":1750217169}
{"mint":"3EaWwNWkWHJFxGH5c82uxE8aAxzmtAapozi7Ch8Xpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":181872778,"tokens_received":0,"buy_slot":347520229,"sell_lamports":175205215,"tokens_sold":0,"sell_slot":347520232,"pnl_lamports":-6667563,"timestamp":1750217171}
{"mint":"5kxqRb3g6s7DYzpMN72LK8D4rW4pByZPE2z8ZW2DKH7n","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":179139900,"tokens_received":0,"buy_slot":347520241,"sell_lamports":171603585,"tokens_sold":0,"sell_slot":347520242,"pnl_lamports":-7536315,"timestamp":1750217176}
{"mint":"BJ5qNX9rt3cYa71u1w6vsebYhgrJMSA69qCmMFLPpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":160281832,"tokens_received":0,"buy_slot":347520722,"sell_lamports":153118943,"tokens_sold":5000000000000,"sell_slot":347520727,"pnl_lamports":-7162889,"timestamp":1750217368}
{"mint":"EpY1rVa2FPAD1rFQvGcdVysdETBNurFy5tBQRD9ipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":164798544,"tokens_received":0,"buy_slot":347520739,"sell_lamports":147088682,"tokens_sold":0,"sell_slot":347520752,"pnl_lamports":-17709862,"timestamp":1750217378}
{"mint":"66cJUuYv69g6i9PyViWHojX7vPXraFuFcKdVyc94pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170144542,"tokens_received":0,"buy_slot":347520762,"sell_lamports":142764913,"tokens_sold":5000000000000,"sell_slot":347520763,"pnl_lamports":-27379629,"timestamp":1750217382}
{"mint":"DoREj57VkS1jrAyMYCdzY5qDSEk6jhSU5Hxm8nkapump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":168341659,"tokens_received":0,"buy_slot":347520768,"sell_lamports":159018702,"tokens_sold":5000000000000,"sell_slot":347520789,"pnl_lamports":-9322957,"timestamp":1750217392}
{"mint":"2RaQQ7k8tHCV7GApk1ZorLA9KrwBc4EmQArXbkHjDHGM","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":196274029,"tokens_received":0,"buy_slot":347520822,"sell_lamports":138039950,"tokens_sold":5000000000000,"sell_slot":347520898,"pnl_lamports":-58234079,"timestamp":1750217436}
{"mint":"7ZZJbeCPkQm6nZ56DtNLGs1c2GFUjPuxcTZc5vJapump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":147697046,"tokens_received":0,"buy_slot":347520909,"sell_lamports":172967400,"tokens_sold":0,"sell_slot":347520962,"pnl_lamports":25270354,"timestamp":1750217461}
{"mint":"C1NKN93pkT3zTtM7JecwnuYrtVEorUptXzTS4Csapump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":178904976,"tokens_received":0,"buy_slot":347521102,"sell_lamports":173808497,"tokens_sold":5000000000000,"sell_slot":347521104,"pnl_lamports":-5096479,"timestamp":1750217517}
{"mint":"2Sk19LDFmd9bnQyg2KDyk6N7tvNU2cBVfjQ2zNnnpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":182813629,"tokens_received":0,"buy_slot":347521106,"sell_lamports":177549144,"tokens_sold":5000000000000,"sell_slot":347521107,"pnl_lamports":-5264485,"timestamp":1750217518}
{"mint":"9k8BFvvtMzRqtovmodjSbi7wMcnGEDdRCsCi9Wt6pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":176452042,"tokens_received":0,"buy_slot":347521109,"sell_lamports":168968941,"tokens_sold":0,"sell_slot":347521110,"pnl_lamports":-7483101,"timestamp":1750217519}
{"mint":"F85S3X1wEhbrAzPCTagcNUngCVNuY7dFtGuuQs7kpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170144542,"tokens_received":0,"buy_slot":347521131,"sell_lamports":162786354,"tokens_sold":5000000000000,"sell_slot":347521133,"pnl_lamports":-7358188,"timestamp":1750217529}
{"mint":"99dYt5XHu7gJ7Q4KsuJ2xn1EqyodbCzhMBVECMBVMTMK","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347521147,"sell_lamports":0,"tokens_sold":0,"sell_slot":347521153,"pnl_lamports":-5000,"timestamp":1750217537}
{"mint":"HaaHpJJXw3vWorMP5yABnJNBXUDHTQoPmiRg8wr2Jw5C","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":188535477,"tokens_received":0,"buy_slot":347521157,"sell_lamports":180813111,"tokens_sold":0,"sell_slot":347521159,"pnl_lamports":-7722366,"timestamp":1750217539}
{"mint":"2TkEpjRjDT9GmxnyEA6GVuXHEd351XvVtMTuB3Rmpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":153171339,"tokens_received":0,"buy_slot":347521163,"sell_lamports":146142931,"tokens_sold":0,"sell_slot":347521184,"pnl_lamports":-7028408,"timestamp":1750217549}
{"mint":"AppAUHfC2USYs8eoDjbg91AbGdMGbJhtaSRj3UvLpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":163410441,"tokens_received":0,"buy_slot":347521187,"sell_lamports":162039233,"tokens_sold":0,"sell_slot":347521200,"pnl_lamports":-1371208,"timestamp":1750217556}
{"mint":"CRLwGR99o9HJyNbjn34gd5oc7RSMjGUYyZ4pzoXMg99E","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347521249,"sell_lamports":0,"tokens_sold":0,"sell_slot":347521262,"pnl_lamports":-5000,"timestamp":1750217582}
{"mint":"3pCkxnvU9vX5vp2o8LYtpwDYJxaq8sKSDxrCDTqKpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":174612538,"tokens_received":0,"buy_slot":347521269,"sell_lamports":167165865,"tokens_sold":5000000000000,"sell_slot":347521270,"pnl_lamports":-7446673,"timestamp":1750217585}
{"mint":"6tqLq2FK5cXbxDTqHBcqeqvAdrQhczGshNkqqgeTpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":173978664,"tokens_received":0,"buy_slot":347521275,"sell_lamports":161945257,"tokens_sold":5000000000000,"sell_slot":347521281,"pnl_lamports":-12033407,"timestamp":1750217589}
{"mint":"85Xm7XbRaP4nWZwhg3gFzKdJRZHgeC66WsNcHuuapump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144916986,"tokens_received":0,"buy_slot":347521297,"sell_lamports":138131808,"tokens_sold":0,"sell_slot":347521302,"pnl_lamports":-6785178,"timestamp":1750217598}
{"mint":"DuvPcmGHwkXcrF5MxwxatU2AjtY8HhM2sKxPVzmGpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347522916,"sell_lamports":0,"tokens_sold":0,"sell_slot":347522921,"pnl_lamports":-5000,"timestamp":1750218248}
{"mint":"5xpTAv5mEYJKk5KgMPi1xuXwnr8qPAjH4DdQtSBupump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":152418032,"tokens_received":0,"buy_slot":347522992,"sell_lamports":142488375,"tokens_sold":0,"sell_slot":347523070,"pnl_lamports":-9929657,"timestamp":1750218308}
{"mint":"CgofhQEowd5bU5j5vjcKTmTrRLSLPtqzjVNKCJrdpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523088,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523093,"pnl_lamports":-5000,"timestamp":1750218317}
{"mint":"DQvmGbuchtC9PevTzpu4Pt6bYxEkhAoXEL2AogtWpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":145085980,"tokens_received":0,"buy_slot":347523095,"sell_lamports":138251351,"tokens_sold":5000000000000,"sell_slot":347523106,"pnl_lamports":-6834629,"timestamp":1750218322}
{"mint":"72fxuwMXixzJgkpGqA8vW3Dp381QRdPER2tfwuwBpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523118,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523136,"pnl_lamports":-5000,"timestamp":1750218335}
{"mint":"R5daazaATfx5vaA2axFHdvpZJ3uKspSteSaRJRRpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523161,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523167,"pnl_lamports":-5000,"timestamp":1750218347}
{"mint":"FnEbgV2fy9WwcTxN85DUujxHYjzQnRpq8V2Kc4STpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":145838304,"tokens_received":0,"buy_slot":347523181,"sell_lamports":139146182,"tokens_sold":5000000000000,"sell_slot":347523241,"pnl_lamports":-6692122,"timestamp":1750218377}
{"mint":"4ryoNE6jq92dpLrDSAoVUea3NC7fYznKsRit7Rtkpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523268,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523273,"pnl_lamports":-5000,"timestamp":1750218390}
{"mint":"5YNJDStETkwmjq4uhmgQ5Qw3ZCrd3XyDYYQPnCq9pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523281,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523288,"pnl_lamports":-5000,"timestamp":1750218396}
{"mint":"8H1UiWLhPEqXBN4bCyVfu8Dbd1gptYo2ULzb32VSpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523302,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523307,"pnl_lamports":-5000,"timestamp":1750218403}
{"mint":"6jrnjH8MgohX9NdtLMFEpW7ERnZjRn6W6CchrWE2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523310,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523318,"pnl_lamports":-5000,"timestamp":1750218408}
{"mint":"R5jY9aieD42yJr5ebagtF9eUmZSabqWHgF3Pq3ppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523352,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523364,"pnl_lamports":-5000,"timestamp":1750218426}
{"mint":"2JzgWCs4sBCNfhrBLKg2i6TGLJMak2FzhgLfwA7Mpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523367,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523379,"pnl_lamports":-5000,"timestamp":1750218432}
{"mint":"7wyNUAerada8d2biycudofwPTaz3zQmibn7Y4h4NFLV9","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":147532319,"tokens_received":0,"buy_slot":347523401,"sell_lamports":140628344,"tokens_sold":5000000000000,"sell_slot":347523408,"pnl_lamports":-6903975,"timestamp":1750218444}
{"mint":"7UVFcESedP2bqHH3vogpTSMjvbXoc76vPpu2tTqY4Tok","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144898214,"tokens_received":0,"buy_slot":347523412,"sell_lamports":166418629,"tokens_sold":5000000000000,"sell_slot":347523422,"pnl_lamports":21520415,"timestamp":1750218449}
{"mint":"R6Uzwg7ekTd2gUW1MZ8p8r1GNMEhvsDUG4H3aFDpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523428,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523435,"pnl_lamports":-5000,"timestamp":1750218454}
{"mint":"GdEh9vmcGNXzfqBtuuLqtZTDvCN6bw4SgZJQ4CjZekDh","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523451,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523453,"pnl_lamports":-5000,"timestamp":1750218462}
{"mint":"8NDjtwUV9Hcdi36xjn7PggJfxGhoKESJdeY5hti8pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":155218132,"tokens_received":0,"buy_slot":347523472,"sell_lamports":148361381,"tokens_sold":5000000000000,"sell_slot":347523477,"pnl_lamports":-6856751,"timestamp":1750218472}
{"mint":"H4qiPNnbpBn7DrtrMWdTZFDNZ1crakZioE9LQSSJpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523486,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523507,"pnl_lamports":-5000,"timestamp":1750218484}
{"mint":"5WuTwnqNa1GcocTpXmdwbgW5hrMrUt1ANqysr2pMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":149963610,"tokens_received":0,"buy_slot":347523522,"sell_lamports":141390129,"tokens_sold":5000000000000,"sell_slot":347523526,"pnl_lamports":-8573481,"timestamp":1750218492}
{"mint":"R6mbY1YUufeS1cpGEA1zrgXmNaURF2EB269vyFWpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523535,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523546,"pnl_lamports":-5000,"timestamp":1750218500}
{"mint":"ewJSLCqhrcZq3V3EZ8JE1SbPFtEpqLxa4o5jSzfpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523556,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523567,"pnl_lamports":-5000,"timestamp":1750218508}
{"mint":"HTdPEk7uqWJayi4k9Uasy5vK6QiQQA7ZMMmS4sLipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523581,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523588,"pnl_lamports":-5000,"timestamp":1750218517}
{"mint":"v87i1Tmpm9eppeKqW1LJccvyU9tCAHi8Y7px1oEpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523611,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523616,"pnl_lamports":-5000,"timestamp":1750218529}
{"mint":"Da1v3xajSxzAszjWGkAPg9G9WA6rCLkr6UA1Y9p1pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":147318183,"tokens_received":0,"buy_slot":347523627,"sell_lamports":141364787,"tokens_sold":5000000000000,"sell_slot":347523629,"pnl_lamports":-5953396,"timestamp":1750218534}
{"mint":"3jeSGwURoNdTaQFqgXuY2Ab2bjhqiDo1dsFND6cTpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523654,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523678,"pnl_lamports":-5000,"timestamp":1750218554}
{"mint":"5iSjy2hiBXQiQWnerf2j841pvUDNte4oNhhCDnZypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144992082,"tokens_received":0,"buy_slot":347523680,"sell_lamports":138159846,"tokens_sold":5000000000000,"sell_slot":347523683,"pnl_lamports":-6832236,"timestamp":1750218556}
{"mint":"6dnA1DmnGEgBHsN4AhNmhkxzVx2KWxeztGnBefwnpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523686,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523690,"pnl_lamports":-5000,"timestamp":1750218559}
{"mint":"R6nWKdqBatoS6BnzeCBkcoGgwQSfpsXzDTVqrHwpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523696,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523703,"pnl_lamports":-5000,"timestamp":1750218564}
{"mint":"CPxLWQWxojneW2yiu5ypFtdgMZD2638aRmFNSNVqpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523717,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523720,"pnl_lamports":-5000,"timestamp":1750218571}
{"mint":"GMG83dGQapxy8gZjCSZQRRnEnXuQAAvQNfPGWpVwstfZ","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523729,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523775,"pnl_lamports":-5000,"timestamp":1750218592}
{"mint":"9Ybv8kCJnVCDgkiYvTDyRuToMm2xL9DstaoMrq6XNckT","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523779,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523782,"pnl_lamports":-5000,"timestamp":1750218595}
{"mint":"5sAAvfFmp1VPZrkZe4a8JjhAHLgYJMXygweFyyFekXaJ","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523791,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523799,"pnl_lamports":-5000,"timestamp":1750218602}
{"mint":"bnUshhvY3LAthpDgvdNJpTfPRoTkoTxuBqpn1kXHNGk","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347523815,"sell_lamports":0,"tokens_sold":0,"sell_slot":347523823,"pnl_lamports":-5000,"timestamp":1750218611}
{"mint":"GTguetbZkuH5dBHQ2bE7YxZRG8jbWukbcweQKMhtpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":33686594,"tokens_received":0,"buy_slot":347651814,"sell_lamports":22383525,"tokens_sold":0,"sell_slot":347651819,"pnl_lamports":-11303069,"timestamp":1750269868}
{"mint":"DjeTfNx91DzBHXQYJSX1Kmddy9VC2HivFS92QeUDpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":31314865,"tokens_received":0,"buy_slot":347651828,"sell_lamports":26724424,"tokens_sold":0,"sell_slot":347651907,"pnl_lamports":-4590441,"timestamp":1750269904}
{"mint":"Cxr6B2E1KuEDzcs9JaexX1FwFpWu59Cs5H1mijupump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":103977367,"tokens_received":0,"buy_slot":347651912,"sell_lamports":91412904,"tokens_sold":0,"sell_slot":347651925,"pnl_lamports":-12564463,"timestamp":1750269911}
{"mint":"E4fniLy4uk2c6qpWHhcamCAK5TbMbLHcecrbY7MhH5Am","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":41010893,"tokens_received":0,"buy_slot":347651927,"sell_lamports":26871354,"tokens_sold":0,"sell_slot":347651942,"pnl_lamports":-14139539,"timestamp":1750269917}
{"mint":"DTCZd58vQ6UBsQjGCtyZSjwErWCt7oMSvJbS6RQfpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":41394187,"tokens_received":0,"buy_slot":347651954,"sell_lamports":32864512,"tokens_sold":1000000000000,"sell_slot":347651962,"pnl_lamports":-8529675,"timestamp":1750269925}
{"mint":"HVqKyb8qzKjdMT5cm196ZKugJe4QoTJ8qe3yPP9Rpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":35568697,"tokens_received":0,"buy_slot":347651970,"sell_lamports":23875372,"tokens_sold":1000000000000,"sell_slot":347651972,"pnl_lamports":-11693325,"timestamp":1750269929}
{"mint":"CMKt348vhbRbSRoeRnUqGexKMTmQkVLdmjygqs3Vpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":36813604,"tokens_received":0,"buy_slot":347651977,"sell_lamports":25504182,"tokens_sold":0,"sell_slot":347651988,"pnl_lamports":-11309422,"timestamp":1750269936}
{"mint":"8eY3nivHeQpofx4zCnxrLdBZG6M9xGxvV2BK56WKpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":33636182,"tokens_received":0,"buy_slot":347651992,"sell_lamports":19718522,"tokens_sold":0,"sell_slot":347652000,"pnl_lamports":-13917660,"timestamp":1750269941}
{"mint":"CPkAyHABqZYxnj83tuL6AB3C6xkiuSrqDzopaXwppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":31309207,"tokens_received":0,"buy_slot":347652002,"sell_lamports":19705766,"tokens_sold":1000000000000,"sell_slot":347652009,"pnl_lamports":-11603441,"timestamp":1750269944}
{"mint":"EFfQuW5n8PxTCDSQg7znVFr6nWs4M43j9zHyPU1zpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":31309209,"tokens_received":0,"buy_slot":347652032,"sell_lamports":19714901,"tokens_sold":1000000000000,"sell_slot":347652064,"pnl_lamports":-11594308,"timestamp":1750269965}
{"mint":"2ZNttFyToZn4xzGAWPiecgJMCmGw3YiqwbX7LcDkpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144973597,"tokens_received":0,"buy_slot":347657917,"sell_lamports":135113842,"tokens_sold":0,"sell_slot":347657931,"pnl_lamports":-9859755,"timestamp":1750272320}
{"mint":"FTaje9uPJ3goHcSHmx6qGfrYyzfHcJRKtNpYhqktpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":159277773,"tokens_received":0,"buy_slot":347657947,"sell_lamports":144392215,"tokens_sold":0,"sell_slot":347657976,"pnl_lamports":-14885558,"timestamp":1750272339}
{"mint":"7fc1XyeSgX4bqbFprQsj83kVvSjiJsnrnNFkfUoHpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":170315856,"tokens_received":0,"buy_slot":347657995,"sell_lamports":159954265,"tokens_sold":5000000000000,"sell_slot":347658004,"pnl_lamports":-10361591,"timestamp":1750272350}
{"mint":"FCfPzzrX2RcYKBjEGQpXUxrhseLibo1nDzTfKZp3pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":186326929,"tokens_received":0,"buy_slot":347658016,"sell_lamports":222443290,"tokens_sold":0,"sell_slot":347658019,"pnl_lamports":36116361,"timestamp":1750272356}
{"mint":"4PGQ1HQH5LtGD927w4sizLWBkD8GMu8iLx4pHecppump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347658027,"sell_lamports":0,"tokens_sold":0,"sell_slot":347658036,"pnl_lamports":-5000,"timestamp":1750272363}
{"mint":"B6YJ7vKJBsWts9Ecd8mPEf9q5fPvCyEpMyJAdFzFpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":144898232,"tokens_received":0,"buy_slot":347658047,"sell_lamports":135067837,"tokens_sold":5000000000000,"sell_slot":347658049,"pnl_lamports":-9830395,"timestamp":1750272368}
{"mint":"9P8ct4q8ZARFtaA2JRYXS2BEDEXkoQUpWtwbz3Hvpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":156502233,"tokens_received":0,"buy_slot":347658091,"sell_lamports":135175315,"tokens_sold":5000000000000,"sell_slot":347658104,"pnl_lamports":-21326918,"timestamp":1750272389}
{"mint":"7Cj2BiXpNKVneyQcYDo1a2M1tpXyGkcuiwU3wc11n2z5","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":378638037,"tokens_received":0,"buy_slot":347660845,"sell_lamports":366151276,"tokens_sold":10000000000000,"sell_slot":347660847,"pnl_lamports":-12486761,"timestamp":1750273487}
{"mint":"39Dyg4x5Qns6SWVEuVYBnfpMwTbE74osrV9ekuUQpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":291249420,"tokens_received":0,"buy_slot":347660879,"sell_lamports":280493105,"tokens_sold":10000000000000,"sell_slot":347660909,"pnl_lamports":-10756315,"timestamp":1750273512}
{"mint":"AaSFnZ9c1HJb6sUncmoWYyvTu2GGHMcsVKVApAR3pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":307306228,"tokens_received":0,"buy_slot":347660911,"sell_lamports":277392946,"tokens_sold":0,"sell_slot":347660941,"pnl_lamports":-29913282,"timestamp":1750273524}
{"mint":"GjFD4BD2ZXfDj2uRiWKL2bk5Vwjw6FbQKLdRQHiCpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":319624088,"tokens_received":0,"buy_slot":347660945,"sell_lamports":319423640,"tokens_sold":0,"sell_slot":347660958,"pnl_lamports":-200448,"timestamp":1750273531}
{"mint":"29LDXaLQyMfxVMaYFN49Pkq2DN3128W5Xs1cod5gpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":321597674,"tokens_received":0,"buy_slot":347660966,"sell_lamports":310240424,"tokens_sold":0,"sell_slot":347660967,"pnl_lamports":-11357250,"timestamp":1750273534}
{"mint":"9mYZUZ6q2YYSZyb89mnqqktLBpBhNDpaNcBquSaXpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":407502529,"tokens_received":0,"buy_slot":347660969,"sell_lamports":277393061,"tokens_sold":10000000000000,"sell_slot":347660974,"pnl_lamports":-130109468,"timestamp":1750273537}
{"mint":"H7p6mqKyVjYeEQTgAsHRExy3pz3pC6eqKwaJS6xxpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":355242021,"tokens_received":0,"buy_slot":347660993,"sell_lamports":315691735,"tokens_sold":0,"sell_slot":347660997,"pnl_lamports":-39550286,"timestamp":1750273547}
{"mint":"6SP2Ft7RDu1xKtH3fpHsm94Z84SL1NN57BX4Bb4Npump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":351861219,"tokens_received":0,"buy_slot":347664296,"sell_lamports":332772812,"tokens_sold":10000000000000,"sell_slot":347664313,"pnl_lamports":-19088407,"timestamp":1750274867}
{"mint":"DLkQ8GVG7EmSjNhNDygW9sVzrcDwUXdXrabWzpU2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":350764564,"tokens_received":0,"buy_slot":347664324,"sell_lamports":457910398,"tokens_sold":0,"sell_slot":347664327,"pnl_lamports":107145834,"timestamp":1750274873}
{"mint":"ZpoARLgzJmVTjnU1N62Nfiunyd2omcumccXjhbisL4Y","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347664339,"sell_lamports":0,"tokens_sold":0,"sell_slot":347664350,"pnl_lamports":-5000,"timestamp":1750274883}
{"mint":"7D38JZZ6C3azhFggWtxeNnGL8eoyw7tYPnePcBe1pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":351446695,"tokens_received":0,"buy_slot":347664355,"sell_lamports":276614126,"tokens_sold":0,"sell_slot":347664361,"pnl_lamports":-74832569,"timestamp":1750274888}
{"mint":"G4cccQhUouzAWCGpPWmkqXvJUbq8BBkC9HxM6xHspump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":369435393,"tokens_received":0,"buy_slot":347664377,"sell_lamports":354130840,"tokens_sold":10000000000000,"sell_slot":347664378,"pnl_lamports":-15304553,"timestamp":1750274894}
{"mint":"8QpZwfj98wi6v6sWD9c4TRjxTdH8MWCXeauhxSf6PF5w","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":472864273,"tokens_received":0,"buy_slot":347682852,"sell_lamports":506578913,"tokens_sold":0,"sell_slot":347682856,"pnl_lamports":33714640,"timestamp":1750282288}
{"mint":"BRNWNzumrc9x6rZBdwXoTgDgh8s1hehpcRGSMsUrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":332048230,"tokens_received":0,"buy_slot":347682893,"sell_lamports":287515097,"tokens_sold":0,"sell_slot":347682900,"pnl_lamports":-44533133,"timestamp":1750282306}
{"mint":"3WsRWtCvYjf4hkQPesLSmPWU31u36MmF1TfWChUNvYAY","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347683876,"sell_lamports":0,"tokens_sold":0,"sell_slot":347683882,"pnl_lamports":-5000,"timestamp":1750282706}
{"mint":"GnTE6mHszVGDkz9QXcqKQvPNW66W7gv8fEwRbF3zBP6D","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347683903,"sell_lamports":0,"tokens_sold":0,"sell_slot":347683984,"pnl_lamports":-5000,"timestamp":1750282747}
{"mint":"jydouknW2GaN2rXZidBYfV1ozQkwUnEAXNSChctdsnN","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":479870273,"tokens_received":0,"buy_slot":347683996,"sell_lamports":481255002,"tokens_sold":0,"sell_slot":347684002,"pnl_lamports":1384729,"timestamp":1750282756}
{"mint":"H4F2vsKRzHJ8qiUT5Lp38ggE9PgB3dbx4oFMEAAh8W7j","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":5000,"tokens_received":0,"buy_slot":347684120,"sell_lamports":0,"tokens_sold":0,"sell_slot":347684123,"pnl_lamports":-5000,"timestamp":1750282803}
{"mint":"Aqof3jsSFkk19FeRtCR7HzHB9CUSn83sg7TRjCuSS3BM","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":403098867,"tokens_received":0,"buy_slot":347684174,"sell_lamports":388850105,"tokens_sold":0,"sell_slot":347684184,"pnl_lamports":-14248762,"timestamp":1750282828}
{"mint":"JALaa12J3tBoXSTSr3iwQAyugnpAzaLJGpHniZwVpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":295924374,"tokens_received":0,"buy_slot":347684224,"sell_lamports":285373372,"tokens_sold":10000000000000,"sell_slot":347684227,"pnl_lamports":-10551002,"timestamp":1750282846}
{"mint":"4tKgCaKthWAaHZoru2hazfgvtronp8RjKBTBmrK5pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":327811783,"tokens_received":0,"buy_slot":347684231,"sell_lamports":412300142,"tokens_sold":0,"sell_slot":347684236,"pnl_lamports":84488359,"timestamp":1750282849}
{"mint":"Cd64Z7G4mDs79JqMvHGq1AvUWNNqopA1CFkwnHeYpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":313154743,"tokens_received":0,"buy_slot":347684247,"sell_lamports":302994274,"tokens_sold":0,"sell_slot":347684249,"pnl_lamports":-10160469,"timestamp":1750282855}
{"mint":"5kW1YP7emJebpeFkZCJSWkM7DqkmJubecEhfBPKqpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":317753321,"tokens_received":0,"buy_slot":347684276,"sell_lamports":284052007,"tokens_sold":0,"sell_slot":347684285,"pnl_lamports":-33701314,"timestamp":1750282869}
{"mint":"8PiREhyPeRErKPqsnfQkiHHborRJpb8iiWknDxSCpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288464426,"tokens_received":0,"buy_slot":347685760,"sell_lamports":278763277,"tokens_sold":0,"sell_slot":347685838,"pnl_lamports":-9701149,"timestamp":1750283491}
{"mint":"HnjY5HJk9C66vx4j7VEi31yMBraVTg4m5iW2fQGWpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":305700847,"tokens_received":0,"buy_slot":347685924,"sell_lamports":274745221,"tokens_sold":0,"sell_slot":347685937,"pnl_lamports":-30955626,"timestamp":1750283529}
{"mint":"2XQ6wdbry1t6mhNsb6JY8XT4cy4NwYfyPmBmyeKypump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":311001924,"tokens_received":0,"buy_slot":347685951,"sell_lamports":274392928,"tokens_sold":0,"sell_slot":347686011,"pnl_lamports":-36608996,"timestamp":1750283557}
{"mint":"FdJsMFv4P71ty3CrRGEWZFCnDAhY8ELxC65gNDJvpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":344984494,"tokens_received":0,"buy_slot":347686029,"sell_lamports":435820988,"tokens_sold":10000000000000,"sell_slot":347686031,"pnl_lamports":90836494,"timestamp":1750283565}
{"mint":"7KHsbXfhQhQGfbAX5CCpV8La5BvzTyGiLhtVmxeJpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288653993,"tokens_received":0,"buy_slot":347686046,"sell_lamports":278392928,"tokens_sold":0,"sell_slot":347686128,"pnl_lamports":-10261065,"timestamp":1750283604}
{"mint":"CRFwRUrcxwAy4Up4r63NM2MCnT7tyNPVK3yAJj9vpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":305324623,"tokens_received":0,"buy_slot":347686259,"sell_lamports":275320147,"tokens_sold":0,"sell_slot":347686264,"pnl_lamports":-30004476,"timestamp":1750283657}
{"mint":"12y2Q8evxufgCmnBsAXP5L9dwEB3xvNv32DXbhuuuFnS","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":289042032,"tokens_received":0,"buy_slot":347686324,"sell_lamports":278392928,"tokens_sold":10000000000000,"sell_slot":347686405,"pnl_lamports":-10649104,"timestamp":1750283714}
{"mint":"8th48ghUL8tdmNwizWVtgvzhCy9mGEPJPZV21YZVv3ND","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":435128951,"tokens_received":0,"buy_slot":347686412,"sell_lamports":387127712,"tokens_sold":10000000000000,"sell_slot":347686418,"pnl_lamports":-48001239,"timestamp":1750283720}
{"mint":"6yRiSqPnaDPCQSB2SkSXdZELaEvEgYb41xUPkkVZpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":329175211,"tokens_received":0,"buy_slot":347686456,"sell_lamports":312419643,"tokens_sold":0,"sell_slot":347686505,"pnl_lamports":-16755568,"timestamp":1750283756}
{"mint":"4FqiHcXQJCWTE92anDMNq1KyJ5uYjN4dD4tEm1ex6H5Z","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":341956180,"tokens_received":0,"buy_slot":347686531,"sell_lamports":327195789,"tokens_sold":10000000000000,"sell_slot":347686536,"pnl_lamports":-14760391,"timestamp":1750283767}
{"mint":"9qm3muJEU1UUzv2SJKZ9qKorvFtg9jSni3FRVfxrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":311720649,"tokens_received":0,"buy_slot":347686616,"sell_lamports":297364174,"tokens_sold":0,"sell_slot":347686622,"pnl_lamports":-14356475,"timestamp":1750283802}
{"mint":"9XZupxgDtySniAuTSxHNNW25nF3tvWsQJATg37MMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":291310898,"tokens_received":0,"buy_slot":347686655,"sell_lamports":275674460,"tokens_sold":0,"sell_slot":347686657,"pnl_lamports":-15636438,"timestamp":1750283816}
{"mint":"VfnRfXhgaLxxgxpeEonko84ZhqT9cxVvd6PcqvZxWqM","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":435128951,"tokens_received":0,"buy_slot":347686659,"sell_lamports":387127712,"tokens_sold":0,"sell_slot":347686663,"pnl_lamports":-48001239,"timestamp":1750283819}
{"mint":"66xb6Q6AE6dFTsqRZBdXXD2FHcdXRwodWgS9bwk1pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":322282591,"tokens_received":0,"buy_slot":347686701,"sell_lamports":307911758,"tokens_sold":0,"sell_slot":347686703,"pnl_lamports":-14370833,"timestamp":1750283834}
{"mint":"8QAEP3JX1j1a11JCoDLcs2q4prDRvNxPhU4SGeicpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288088506,"tokens_received":0,"buy_slot":347686708,"sell_lamports":278394781,"tokens_sold":10000000000000,"sell_slot":347686789,"pnl_lamports":-9693725,"timestamp":1750283868}
{"mint":"Hn8TxYAYywRSMSKSfFCaSztCQG7za7qTpv4heSarpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":292534049,"tokens_received":0,"buy_slot":347686817,"sell_lamports":282752296,"tokens_sold":10000000000000,"sell_slot":347686898,"pnl_lamports":-9781753,"timestamp":1750283913}
{"mint":"7M12yzD1ASQjmBanzhSuR6DuM3ZCFDBuDFFjAA8vpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":321048816,"tokens_received":0,"buy_slot":347686905,"sell_lamports":276620856,"tokens_sold":0,"sell_slot":347686912,"pnl_lamports":-44427960,"timestamp":1750283919}
{"mint":"8f9wP7iHBA5pyGDaRUu5fbZGu4UCZmJEkZtQmegCoBHz","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":435128951,"tokens_received":0,"buy_slot":347686915,"sell_lamports":323648283,"tokens_sold":10000000000000,"sell_slot":347686920,"pnl_lamports":-111480668,"timestamp":1750283922}
{"mint":"ARqTuFKGb62WmE7vJBg7gbCX5U2yk95hpmrfowmUpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":314140763,"tokens_received":0,"buy_slot":347686922,"sell_lamports":299931173,"tokens_sold":0,"sell_slot":347686923,"pnl_lamports":-14209590,"timestamp":1750283923}
{"mint":"D8ckMoJ4ZqKaS62sPxdWrbN4wYp7snyWRX8nuEzkpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":290169595,"tokens_received":0,"buy_slot":347686955,"sell_lamports":276248915,"tokens_sold":10000000000000,"sell_slot":347686973,"pnl_lamports":-13920680,"timestamp":1750283942}
{"mint":"AbdSJ2xPafdUahWRdGdPuz4bKCvax2DVx1W5s4Q2pump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":392170980,"tokens_received":0,"buy_slot":347687017,"sell_lamports":379647746,"tokens_sold":0,"sell_slot":347687018,"pnl_lamports":-12523234,"timestamp":1750283960}
{"mint":"3VhMRcxzUmWpqgoRv71pnAUWzCoWUWTxcGgbqvXRpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288464615,"tokens_received":0,"buy_slot":347687070,"sell_lamports":274763463,"tokens_sold":10000000000000,"sell_slot":347687072,"pnl_lamports":-13701152,"timestamp":1750283982}
{"mint":"9aY4rmAvtFUAGgZ2iHKz3CuxewgYz2NH4UcoY8Tipump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":306733320,"tokens_received":0,"buy_slot":347687087,"sell_lamports":292476124,"tokens_sold":10000000000000,"sell_slot":347687098,"pnl_lamports":-14257196,"timestamp":1750283991}
{"mint":"Cb3Gpk6nqRzLZm2PvKACin8qgZqUXGaksVnNCqEupump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":291120836,"tokens_received":0,"buy_slot":347687106,"sell_lamports":275488788,"tokens_sold":10000000000000,"sell_slot":347687109,"pnl_lamports":-15632048,"timestamp":1750283997}
{"mint":"3KiYcBcBBnMzyhFgBMVAD3xytRP1tjt6ezWNSaWrpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":313967694,"tokens_received":0,"buy_slot":347687150,"sell_lamports":303369747,"tokens_sold":10000000000000,"sell_slot":347687153,"pnl_lamports":-10597947,"timestamp":1750284013}
{"mint":"8juTw5JwFTQCu48MLGWAqvw99VuxjiXqaHLJVoVepump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288407738,"tokens_received":0,"buy_slot":347687176,"sell_lamports":274522535,"tokens_sold":0,"sell_slot":347687191,"pnl_lamports":-13885203,"timestamp":1750284028}
{"mint":"9QDvRhURSoHeXiRBRegc9viGPsrUA5YbUGwBHCMXpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":329122976,"tokens_received":0,"buy_slot":347687204,"sell_lamports":284051989,"tokens_sold":10000000000000,"sell_slot":347687212,"pnl_lamports":-45070987,"timestamp":1750284036}
{"mint":"A2ehpGjMkeCryNN6ygV459bB7DyDgUuv9tGxn8UGpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":365496699,"tokens_received":0,"buy_slot":347687223,"sell_lamports":321844504,"tokens_sold":10000000000000,"sell_slot":347687227,"pnl_lamports":-43652195,"timestamp":1750284042}
{"mint":"HhsynLsfhq322wmRJ7jhEdKK8CLB8g3fEuTaWWT2yR7k","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":435128951,"tokens_received":0,"buy_slot":347687270,"sell_lamports":387127710,"tokens_sold":10000000000000,"sell_slot":347687272,"pnl_lamports":-48001241,"timestamp":1750284061}
{"mint":"ENx4DytUYunhxVDXMoQazMDzuHviKLNpQNjY7pMJpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":305324640,"tokens_received":0,"buy_slot":348530992,"sell_lamports":275320162,"tokens_sold":0,"sell_slot":348530999,"pnl_lamports":-30004478,"timestamp":1750623238}
{"mint":"3ZvCMKFVmunNzMt71M9PfrPjsbLjfTBB8ZH2mRvUpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288086634,"tokens_received":0,"buy_slot":348535865,"sell_lamports":274392966,"tokens_sold":10000000000000,"sell_slot":348535871,"pnl_lamports":-13693668,"timestamp":1750625194}
{"mint":"pVJz467uLWsEjpM8S7dhMCLZztwpFWBZtnAgo88eAVW","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":294237211,"tokens_received":0,"buy_slot":348538810,"sell_lamports":283649685,"tokens_sold":0,"sell_slot":348538819,"pnl_lamports":-10587526,"timestamp":1750626384}
{"mint":"7RV1MNLsPNnS2pw9Lxz1dJ8FtSndQtJmHAFDPxBspump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":312446438,"tokens_received":0,"buy_slot":348539436,"sell_lamports":275320162,"tokens_sold":0,"sell_slot":348539448,"pnl_lamports":-37126276,"timestamp":1750626637}
{"mint":"3o7Hty7d2Wj99FkXYYpYFjJURj9x1sRzgMg4bUNMpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":288266294,"tokens_received":0,"buy_slot":348539473,"sell_lamports":276116188,"tokens_sold":0,"sell_slot":348539545,"pnl_lamports":-12150106,"timestamp":1750626677}
{"mint":"EqDG46R2eRrhu53ywcE3St5qRFxnUPoE8UTY4xrFpump","creator":"6s86v9Scfo6FwhgMJ6gAQtecKYKi4u5y5tHnbSVqGYBM","buy_lamports":317753341,"tokens_received":0,"buy_slot":348740038,"sell_lamports":283734905,"tokens_sold":0,"sell_slot":348740047,"pnl_lamports":-34018436,"timestamp":1750707421}
{"mint":"88qNZrT2W13ytxjnCayT7j1yrM5wo8h4QQjZxC2Dpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":34822021,"tokens_received":0,"buy_slot":348749679,"sell_lamports":24711721,"tokens_sold":1000000000000,"sell_slot":348749684,"pnl_lamports":-10110300,"timestamp":1750711298}
{"mint":"B3t3RnV8Q2XGuT5vKMPso6AZgEaxu91bVAatkyxipump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33499429,"tokens_received":0,"buy_slot":348749733,"sell_lamports":24847074,"tokens_sold":1000000000000,"sell_slot":348749740,"pnl_lamports":-8652355,"timestamp":1750711320}
{"mint":"EtqDQssJeHtzHRRPuKBkDrhJyLTWknXzdTLQMNXDpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":32630843,"tokens_received":0,"buy_slot":348749764,"sell_lamports":23436892,"tokens_sold":1000000000000,"sell_slot":348749771,"pnl_lamports":-9193951,"timestamp":1750711331}
{"mint":"9bAF3JQJayKw1H4km9ZMmACpr61KapdoJYsTLTQeivqy","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":36366188,"tokens_received":0,"buy_slot":348755001,"sell_lamports":29260596,"tokens_sold":0,"sell_slot":348755004,"pnl_lamports":-7105592,"timestamp":1750713435}
{"mint":"FSbSa9Uypd2rvquFCLAo9fpYTKQurAXiPP9VF9b7pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33474832,"tokens_received":0,"buy_slot":348755009,"sell_lamports":26883483,"tokens_sold":0,"sell_slot":348755018,"pnl_lamports":-6591349,"timestamp":1750713440}
{"mint":"88rPZtRTfXtq7J1DkT697zzQg2pda3wYV1cK8aKwpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33204059,"tokens_received":0,"buy_slot":348812776,"sell_lamports":28557558,"tokens_sold":0,"sell_slot":348812779,"pnl_lamports":-4646501,"timestamp":1750736664}
{"mint":"AkCPPeaTa4CXzMDuSPWyZq13oEdUaSWWnN6nLRswpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":56033618,"tokens_received":0,"buy_slot":348813757,"sell_lamports":50817696,"tokens_sold":0,"sell_slot":348813765,"pnl_lamports":-5215922,"timestamp":1750737058}
{"mint":"7qxFW16XW1UWWY1wyGvFpPHAyE8FGk1o1TRJ2jzXpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":32250132,"tokens_received":0,"buy_slot":348814248,"sell_lamports":27078277,"tokens_sold":1000000000000,"sell_slot":348814326,"pnl_lamports":-5171855,"timestamp":1750737285}
{"mint":"i4ksN5rHgktSehPjkhbCcRCthGzbRFBKVbsX6Wxpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31361989,"tokens_received":0,"buy_slot":348815496,"sell_lamports":26751960,"tokens_sold":1000000000000,"sell_slot":348815503,"pnl_lamports":-4610029,"timestamp":1750737756}
{"mint":"9AeRMCq3K6qkZRARQM9WxVbdamEXSgtdDaKgi16Apump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":34346346,"tokens_received":0,"buy_slot":348815629,"sell_lamports":26718522,"tokens_sold":1000000000000,"sell_slot":348815638,"pnl_lamports":-7627824,"timestamp":1750737810}
{"mint":"GHXRJqNM2cyUQc8VrvLi1Ztz6prwQeGiVYfqGMDxpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33337197,"tokens_received":0,"buy_slot":348815680,"sell_lamports":27996655,"tokens_sold":0,"sell_slot":348815686,"pnl_lamports":-5340542,"timestamp":1750737830}
{"mint":"AuGFGJwd296eMAQYuQfKQq65wWPVCBY4eeTBPQGrpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":35303726,"tokens_received":0,"buy_slot":348815713,"sell_lamports":29991635,"tokens_sold":0,"sell_slot":348815720,"pnl_lamports":-5312091,"timestamp":1750737843}
{"mint":"3J1xNU6kSPqZ1ZMs78RYNtpbGmyenfMmvRyj6339pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33011009,"tokens_received":0,"buy_slot":348816097,"sell_lamports":26851735,"tokens_sold":0,"sell_slot":348816500,"pnl_lamports":-6159274,"timestamp":1750738155}
{"mint":"ESEF31DFwc4ZWABSDB6g3KbssZduRYe6rTgbJvGrV6oA","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31309207,"tokens_received":0,"buy_slot":348816781,"sell_lamports":26719259,"tokens_sold":1000000000000,"sell_slot":348817184,"pnl_lamports":-4589948,"timestamp":1750738431}
{"mint":"B7GTauiL3TovWhkGJ3VESZEBzdAKJdau1X8Qm5yzpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33300595,"tokens_received":0,"buy_slot":348818123,"sell_lamports":26709449,"tokens_sold":1000000000000,"sell_slot":348818213,"pnl_lamports":-6591146,"timestamp":1750738842}
{"mint":"4giCz8P46ADLqmxTyivtGnCguYqGH4EBd2CbFLGHpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":35150107,"tokens_received":0,"buy_slot":348819072,"sell_lamports":30950293,"tokens_sold":1000000000000,"sell_slot":348819077,"pnl_lamports":-4199814,"timestamp":1750739190}
{"mint":"CoMMuhRaH9iLZxHC9E6RRd3yyE7dx2yniGyixrKMXh1P","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31309207,"tokens_received":0,"buy_slot":348831244,"sell_lamports":26818359,"tokens_sold":1000000000000,"sell_slot":348832247,"pnl_lamports":-4490848,"timestamp":1750744486}
{"mint":"64MQsQ8HWxLJkrDEHESCv12jnDyjygnvPsJ93RUgCWdq","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31309207,"tokens_received":0,"buy_slot":348832312,"sell_lamports":26793653,"tokens_sold":1000000000000,"sell_slot":348833362,"pnl_lamports":-4515554,"timestamp":1750744938}
{"mint":"5kMazLt27ieg9STMtYJ34f1fabxNmuEYcJhrXrVh26M7","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31309207,"tokens_received":0,"buy_slot":348833394,"sell_lamports":26700224,"tokens_sold":1000000000000,"sell_slot":348834397,"pnl_lamports":-4608983,"timestamp":1750745353}
{"mint":"DjMhGUMwo4MPbzw2DGuVvymPTKKfgTCeR6yhSfRypump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33397128,"tokens_received":0,"buy_slot":348834398,"sell_lamports":27067350,"tokens_sold":1000000000000,"sell_slot":348834408,"pnl_lamports":-6329778,"timestamp":1750745358}
{"mint":"7ssjU8pYAr2cEqwvkagzRzM2CH28RiNb5MEq78QZHP5i","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":59626914,"tokens_received":0,"buy_slot":348835300,"sell_lamports":55182365,"tokens_sold":0,"sell_slot":348835322,"pnl_lamports":-4444549,"timestamp":1750745726}
{"mint":"BYU4vT8qWQbb2wWDemv1S33Kb3Mg8oPDvtYfasuupump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":65716073,"tokens_received":0,"buy_slot":348835329,"sell_lamports":57647290,"tokens_sold":0,"sell_slot":348835336,"pnl_lamports":-8068783,"timestamp":1750745732}
{"mint":"95GgR39yenDuxWJAJmyAnSzamW7bfVxNDkMuVEdBQDc5","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":59626914,"tokens_received":0,"buy_slot":348835382,"sell_lamports":54546531,"tokens_sold":0,"sell_slot":348835435,"pnl_lamports":-5080383,"timestamp":1750745772}
{"mint":"38UdutHoZXsrt8CgYFGte2MKjWGfwnNqkfJUMt5RnYfG","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":59627292,"tokens_received":0,"buy_slot":348840114,"sell_lamports":50457557,"tokens_sold":0,"sell_slot":348840128,"pnl_lamports":-9169735,"timestamp":1750747663}
{"mint":"2s7PnQyG2bLjt68jncLx6oZMyxDqb3z2NVx1hygmpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63930981,"tokens_received":0,"buy_slot":348840171,"sell_lamports":50824219,"tokens_sold":0,"sell_slot":348840180,"pnl_lamports":-13106762,"timestamp":1750747685}
{"mint":"3EM51q1mX12WLFpF96YcZkpRMutDsTysCg8Av3Vfpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63643571,"tokens_received":0,"buy_slot":348840696,"sell_lamports":50551763,"tokens_sold":2000000000000,"sell_slot":348840703,"pnl_lamports":-13091808,"timestamp":1750747894}
{"mint":"2wnUBZeKbAP1FgkDKbg38zvJaJi1vfADH95Nhtyypump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":61892059,"tokens_received":0,"buy_slot":348840710,"sell_lamports":52677486,"tokens_sold":0,"sell_slot":348840711,"pnl_lamports":-9214573,"timestamp":1750747897}
{"mint":"XnvGxuhRJ5qKykyCsSCLdQ8mNtCQfDpXPgGr7VEpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":70515426,"tokens_received":0,"buy_slot":348840723,"sell_lamports":61207149,"tokens_sold":0,"sell_slot":348840724,"pnl_lamports":-9308277,"timestamp":1750747903}
{"mint":"8tx9X4zhqLtpT8qLfxY65kCfQgJLYfNGUdUFZuGjpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":62293709,"tokens_received":0,"buy_slot":348840733,"sell_lamports":53071178,"tokens_sold":2000000000000,"sell_slot":348840734,"pnl_lamports":-9222531,"timestamp":1750747907}
{"mint":"4tXD7WHgn2ccxmkbCZ3s81CKKXGEDMiEp6qTvvqXpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":154426662,"tokens_received":0,"buy_slot":348841422,"sell_lamports":139611451,"tokens_sold":5000000000000,"sell_slot":348841431,"pnl_lamports":-14815211,"timestamp":1750748189}
{"mint":"Hv66WMPFagcSbW5kNRQ37Vvudf4uTjAv7oVZQKtZpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":144899153,"tokens_received":0,"buy_slot":348841457,"sell_lamports":138040862,"tokens_sold":5000000000000,"sell_slot":348841759,"pnl_lamports":-6858291,"timestamp":1750748324}
{"mint":"DDiUg3cmaEAt2uZTAnw4hciyrC5re5ZPuofwADA5pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":156083841,"tokens_received":0,"buy_slot":348841764,"sell_lamports":139250703,"tokens_sold":5000000000000,"sell_slot":348841773,"pnl_lamports":-16833138,"timestamp":1750748329}
{"mint":"EtHhzzKgeqMJVEqj8kcMctEy9v7hMeD6VMNPGWo9pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":144992007,"tokens_received":0,"buy_slot":348841778,"sell_lamports":139088741,"tokens_sold":5000000000000,"sell_slot":348842081,"pnl_lamports":-5903266,"timestamp":1750748453}
{"mint":"5KPEoMgmhQjs5LxAN2aAt5bubQQcYoHkJrPKQPBnpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":154426682,"tokens_received":0,"buy_slot":348843341,"sell_lamports":146879709,"tokens_sold":0,"sell_slot":348843349,"pnl_lamports":-7546973,"timestamp":1750748959}
{"mint":"4JE5RU41HiWy6A1C4zPW1hCnCdANhsSgWJZGvAbsdDMS","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":144898214,"tokens_received":0,"buy_slot":348843440,"sell_lamports":138039952,"tokens_sold":0,"sell_slot":348843643,"pnl_lamports":-6858262,"timestamp":1750749078}
{"mint":"Dx6JEfVaqey9ZAAAnvrKv42AWVCWpwiJt3NJYydypump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":163277721,"tokens_received":0,"buy_slot":348843644,"sell_lamports":158473874,"tokens_sold":0,"sell_slot":348843652,"pnl_lamports":-4803847,"timestamp":1750749082}
{"mint":"ByvKVN91Jcw3rpeM8bSGX7aVG8ALkCFeqCoKZ8S7pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":5000,"tokens_received":0,"buy_slot":348843670,"sell_lamports":0,"tokens_sold":0,"sell_slot":348843677,"pnl_lamports":-5000,"timestamp":1750749091}
{"mint":"4wW6FvqqhCwpp54xXPyQnqwnCeHLYGUygfpjDCz2pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":144992082,"tokens_received":0,"buy_slot":348843696,"sell_lamports":139705382,"tokens_sold":0,"sell_slot":348843899,"pnl_lamports":-5286700,"timestamp":1750749182}
{"mint":"BywScsj9XgcxUCmizhD2ZsCTWnvm2AYPf3rLh7ggpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":313193009,"tokens_received":0,"buy_slot":348845062,"sell_lamports":296730083,"tokens_sold":0,"sell_slot":348845068,"pnl_lamports":-16462926,"timestamp":1750749649}
{"mint":"FWYeJNcKckKD5gEkofe81BqQsxXyzGqf4o86ACG3pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":313195045,"tokens_received":0,"buy_slot":348845137,"sell_lamports":296731976,"tokens_sold":10000000000000,"sell_slot":348845143,"pnl_lamports":-16463069,"timestamp":1750749680}
{"mint":"Ghf16Uo4S6FmVzqUWejiT23ydWxiK3pQoPfPKvNLcFZN","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":288086634,"tokens_received":0,"buy_slot":348845505,"sell_lamports":278392947,"tokens_sold":10000000000000,"sell_slot":348845658,"pnl_lamports":-9693687,"timestamp":1750749887}
{"mint":"CEiBAsaG1jSYkvYtv65vrgxqLVyPgbtAodxNCRYspump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":314419813,"tokens_received":0,"buy_slot":348845670,"sell_lamports":277892928,"tokens_sold":10000000000000,"sell_slot":348845677,"pnl_lamports":-36526885,"timestamp":1750749895}
{"mint":"2SdcFK2nXiVJWTyG1qxUsQuPZA4BmUex77mxXGRnpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":337630594,"tokens_received":0,"buy_slot":348845684,"sell_lamports":298424223,"tokens_sold":10000000000000,"sell_slot":348845693,"pnl_lamports":-39206371,"timestamp":1750749901}
{"mint":"Hb9a7eSFQdauPaC9uT9SmtTugRGVAutLFGwbJF9upump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":158164387,"tokens_received":0,"buy_slot":348846820,"sell_lamports":147665459,"tokens_sold":0,"sell_slot":348846824,"pnl_lamports":-10498928,"timestamp":1750750359}
{"mint":"G6wD1MiBDwhChjTwNMxQFke71CYK3wSG8ouRMUwZp5SV","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":144898214,"tokens_received":0,"buy_slot":348847162,"sell_lamports":138039952,"tokens_sold":5000000000000,"sell_slot":348847248,"pnl_lamports":-6858262,"timestamp":1750750531}
{"mint":"5PAAjsJta7j5fobWM9YMBs6w1quq1faYxLpRmbKZpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":163277710,"tokens_received":0,"buy_slot":348847272,"sell_lamports":145386402,"tokens_sold":5000000000000,"sell_slot":348847310,"pnl_lamports":-17891308,"timestamp":1750750556}
{"mint":"3U2MfYZxdFRQT9nGb8ZfMbiTp115ddH3FNKbHqbspump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":240557728,"tokens_received":0,"buy_slot":348848043,"sell_lamports":225797371,"tokens_sold":8000000000000,"sell_slot":348848049,"pnl_lamports":-14760357,"timestamp":1750750854}
{"mint":"B87cFXzCEFG8xko4y1vTroFfd2Bx3z6w9o5fzgXvpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":262531414,"tokens_received":0,"buy_slot":348848071,"sell_lamports":243690170,"tokens_sold":8000000000000,"sell_slot":348848073,"pnl_lamports":-18841244,"timestamp":1750750864}
{"mint":"59M9CEptdEKVRHiJhifJEppJAP1tHK7cigQJ1Rp2pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":230649918,"tokens_received":0,"buy_slot":348848113,"sell_lamports":222093654,"tokens_sold":0,"sell_slot":348848165,"pnl_lamports":-8556264,"timestamp":1750750903}
{"mint":"W1m7WqD8gaMU24fPamTCa21vMuVyq1RkxYAi48qpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":32893681,"tokens_received":0,"buy_slot":348850445,"sell_lamports":29311907,"tokens_sold":1000000000000,"sell_slot":348850455,"pnl_lamports":-3581774,"timestamp":1750751827}
{"mint":"7q1NLLamka5W2HMiYuY9WiZEX53gYtcGxDUA6eutpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":35298671,"tokens_received":0,"buy_slot":348850469,"sell_lamports":29310040,"tokens_sold":0,"sell_slot":348850475,"pnl_lamports":-5988631,"timestamp":1750751835}
{"mint":"7afH9uykpURnQ4iZwzvG56UVe12U3xaqgA59WKA9pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":38113536,"tokens_received":0,"buy_slot":348850489,"sell_lamports":37218322,"tokens_sold":0,"sell_slot":348850491,"pnl_lamports":-895214,"timestamp":1750751841}
{"mint":"DYvBqTkRBb4a1Rq6qQynGHvJx8mUXJ57HbnqZPFGpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":32887781,"tokens_received":0,"buy_slot":348850517,"sell_lamports":29677597,"tokens_sold":0,"sell_slot":348850524,"pnl_lamports":-3210184,"timestamp":1750751854}
{"mint":"2SuZKRgnv79boZfAmWaWGNB1eh1u4TeCS6t2A8aypump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":30336949,"tokens_received":0,"buy_slot":348851141,"sell_lamports":27707612,"tokens_sold":0,"sell_slot":348851173,"pnl_lamports":-2629337,"timestamp":1750752115}
{"mint":"6SSroMAYNH2ALPXDjcUabeLhCJ9Pf3GYBBtzSJeDpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":31077283,"tokens_received":0,"buy_slot":348851184,"sell_lamports":27766686,"tokens_sold":0,"sell_slot":348851189,"pnl_lamports":-3310597,"timestamp":1750752123}
{"mint":"G3wAS3VSCbup1WFgZFRqDBdXJhPJgLeEDwLpg2k3pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":36911512,"tokens_received":0,"buy_slot":348851741,"sell_lamports":38879167,"tokens_sold":0,"sell_slot":348851744,"pnl_lamports":1967655,"timestamp":1750752347}
{"mint":"8E1Nga4Ks6vaaZseotTh3gsJKaTGChqXihRLy9M5pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":32998500,"tokens_received":0,"buy_slot":348851790,"sell_lamports":29510227,"tokens_sold":1000000000000,"sell_slot":348851793,"pnl_lamports":-3488273,"timestamp":1750752368}
{"mint":"9hTNAimEz3D9Xv2gwqGEanhE3QzriKVt6NHqhFaEpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":35090224,"tokens_received":0,"buy_slot":348851816,"sell_lamports":31536764,"tokens_sold":1000000000000,"sell_slot":348851818,"pnl_lamports":-3553460,"timestamp":1750752378}
{"mint":"CMRaF9TmsJvk6aRX95vzgZFQtoyRi6EvyXMKmjw9pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":34544665,"tokens_received":0,"buy_slot":348851831,"sell_lamports":30413481,"tokens_sold":0,"sell_slot":348851832,"pnl_lamports":-4131184,"timestamp":1750752383}
{"mint":"nX8Det3HEpa1m2VAXrkeF75CFzkG18vvuEvdPtMpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":33083505,"tokens_received":0,"buy_slot":348851842,"sell_lamports":31023264,"tokens_sold":0,"sell_slot":348851848,"pnl_lamports":-2060241,"timestamp":1750752389}
{"mint":"CGfSih9Ls2f6ngnxMHcPeVerLxfyQDE4F8iowupBpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58672448,"tokens_received":0,"buy_slot":348852512,"sell_lamports":55844128,"tokens_sold":2000000000000,"sell_slot":348852595,"pnl_lamports":-2828320,"timestamp":1750752687}
{"mint":"EBDgRzqq26677T7xbA4jhyDcRHmzboEg1Y6FYZtzpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58674304,"tokens_received":0,"buy_slot":348852605,"sell_lamports":56542102,"tokens_sold":2000000000000,"sell_slot":348852688,"pnl_lamports":-2132202,"timestamp":1750752724}
{"mint":"C37mTvcaiL3jgVHDE86KrG1mNBZ8rABQBnrAaTn3pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":334214149,"tokens_received":0,"buy_slot":348852699,"sell_lamports":327276460,"tokens_sold":2000000000000,"sell_slot":348852714,"pnl_lamports":-6937689,"timestamp":1750752734}
{"mint":"56PoMj2gagSkA3dXzomZAH23X5GrkGnexThgzBWWpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":59707519,"tokens_received":0,"buy_slot":348853382,"sell_lamports":56496588,"tokens_sold":0,"sell_slot":348853465,"pnl_lamports":-3210931,"timestamp":1750753038}
{"mint":"3fxc1hJ7JQ4LQjoS1NrCHtMViRe4eU7MhyJh16sZpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":68240263,"tokens_received":0,"buy_slot":348853486,"sell_lamports":64895234,"tokens_sold":0,"sell_slot":348853489,"pnl_lamports":-3345029,"timestamp":1750753047}
{"mint":"55w2WX4VVC6bPYUspCs6ZCDBW2YZ6dTCAfXTQvRsvuPB","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58640691,"tokens_received":0,"buy_slot":348853530,"sell_lamports":55537060,"tokens_sold":2000000000000,"sell_slot":348853613,"pnl_lamports":-3103631,"timestamp":1750753096}
{"mint":"GytSoBW3hPr1uEYobsyYmdJLowHDnEjgsVfqAGZ8pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":83452730,"tokens_received":0,"buy_slot":348853650,"sell_lamports":79257200,"tokens_sold":2000000000000,"sell_slot":348853655,"pnl_lamports":-4195530,"timestamp":1750753113}
{"mint":"7qwYv16DNYCD5yHuvQtU5ZxbY9vodBfBrf73r8Kepump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63401503,"tokens_received":0,"buy_slot":348853683,"sell_lamports":62121470,"tokens_sold":2000000000000,"sell_slot":348853687,"pnl_lamports":-1280033,"timestamp":1750753126}
{"mint":"7BRQYQosZBLGzPeNCqRVMbMovEPyyVgv4P8SNpvHpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":60653849,"tokens_received":0,"buy_slot":348853694,"sell_lamports":57138474,"tokens_sold":2000000000000,"sell_slot":348853776,"pnl_lamports":-3515375,"timestamp":1750753164}
{"mint":"Go6g4aZXZ3RsgXFRNANvYp1pPztyLrS2rDTk4LaHpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":64411884,"tokens_received":0,"buy_slot":348853820,"sell_lamports":59904272,"tokens_sold":2000000000000,"sell_slot":348853825,"pnl_lamports":-4507612,"timestamp":1750753184}
{"mint":"EJZJ22gQqf9XgtjZ9N31pJCRtakjyd4YCmcdmFwLpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":87052436,"tokens_received":0,"buy_slot":348855812,"sell_lamports":83361598,"tokens_sold":0,"sell_slot":348855865,"pnl_lamports":-3690838,"timestamp":1750754013}
{"mint":"GBBrsJEjVomNxGYt2Dkrhfi4wJbzwUsP8RTyW9Rzpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":90031336,"tokens_received":0,"buy_slot":348859474,"sell_lamports":84832357,"tokens_sold":3000000000000,"sell_slot":348859556,"pnl_lamports":-5198979,"timestamp":1750755507}
{"mint":"3hmxuDesxQD7ZrVXn28QnhWwF6C91fbPvxBudCZipump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":94947622,"tokens_received":0,"buy_slot":348859569,"sell_lamports":95767332,"tokens_sold":0,"sell_slot":348859604,"pnl_lamports":819710,"timestamp":1750755526}
{"mint":"FaGKu4x2gx7xMLnZSd8M6Aq6eNZKRNcdBUgUrtuLpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":87007606,"tokens_received":0,"buy_slot":348859744,"sell_lamports":83317598,"tokens_sold":0,"sell_slot":348859789,"pnl_lamports":-3690008,"timestamp":1750755600}
{"mint":"HqAhSCmcCVLtGdXoPeomAmQs3Jebk94TgeybRGSUpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":98105407,"tokens_received":0,"buy_slot":348859840,"sell_lamports":88903282,"tokens_sold":0,"sell_slot":348859849,"pnl_lamports":-9202125,"timestamp":1750755625}
{"mint":"WFdd1g4EY54QKixKLgnpU7WhP1Re7PE3soka8EKpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":5000,"tokens_received":0,"buy_slot":348859860,"sell_lamports":0,"tokens_sold":0,"sell_slot":348859868,"pnl_lamports":-5000,"timestamp":1750755632}
{"mint":"BzbwMagMLkU5y7SaywZneiUuznwcwgEmFLGWDRGr78Lv","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":79093510,"tokens_received":0,"buy_slot":348921450,"sell_lamports":79088938,"tokens_sold":2000000000000,"sell_slot":348921453,"pnl_lamports":-4572,"timestamp":1750780447}
{"mint":"25y7YcNghk9TMHYynYZwKYgBUK9x4tuhhaJbnHJ2pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63601160,"tokens_received":0,"buy_slot":348921487,"sell_lamports":59132111,"tokens_sold":0,"sell_slot":348921494,"pnl_lamports":-4469049,"timestamp":1750780463}
{"mint":"5n4iU2VP4k4rNaqPsWxgVcHz28a8j1CMGnHWbWqLpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":65198490,"tokens_received":0,"buy_slot":348921528,"sell_lamports":59132115,"tokens_sold":2000000000000,"sell_slot":348921535,"pnl_lamports":-6066375,"timestamp":1750780479}
{"mint":"YVrBU2ZxMSi8gdMDTRxcofKnSYmWs9Vm9sbnvHWpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":77811086,"tokens_received":0,"buy_slot":348921549,"sell_lamports":75745702,"tokens_sold":0,"sell_slot":348921552,"pnl_lamports":-2065384,"timestamp":1750780486}
{"mint":"FBDFQdN1ztSVy4thoPibtECeZg3kZ35Nw3FdMCNEe19e","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":65566214,"tokens_received":0,"buy_slot":348921597,"sell_lamports":56409159,"tokens_sold":0,"sell_slot":348921601,"pnl_lamports":-9157055,"timestamp":1750780506}
{"mint":"7KGg12TnBD497VvTUyBEx9WndmTrtokWSTe33FGtpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":66800206,"tokens_received":0,"buy_slot":348921609,"sell_lamports":60570130,"tokens_sold":0,"sell_slot":348921615,"pnl_lamports":-6230076,"timestamp":1750780511}
{"mint":"7to5A8SaV8jM4L86CV2FRd7X7mcoF3fmk2ZmigbQPsZa","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":85237997,"tokens_received":0,"buy_slot":348921634,"sell_lamports":75458701,"tokens_sold":0,"sell_slot":348921639,"pnl_lamports":-9779296,"timestamp":1750780521}
{"mint":"4BrkzdZaNzs4rN5VoeqQjN9496c1U7sKPgQmzizTpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":69490872,"tokens_received":0,"buy_slot":348921648,"sell_lamports":83127494,"tokens_sold":2000000000000,"sell_slot":348921649,"pnl_lamports":13636622,"timestamp":1750780525}
{"mint":"HBFKY3oeXsTBj2hzXZgPiQze2Q8PgtKzEesfzPFhAe7E","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58674678,"tokens_received":0,"buy_slot":348921685,"sell_lamports":55444203,"tokens_sold":2000000000000,"sell_slot":348921705,"pnl_lamports":-3230475,"timestamp":1750780549}
{"mint":"GzRmd7nD8dbggpCxFiMwEmE4fwJE5KwgzXa98mXLpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63647684,"tokens_received":0,"buy_slot":348921772,"sell_lamports":55833614,"tokens_sold":2000000000000,"sell_slot":348921780,"pnl_lamports":-7814070,"timestamp":1750780579}
{"mint":"81enfW3nvwMuUZPP3fB3m7X4FrTB3jF3bXSMKnEKpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":61397067,"tokens_received":0,"buy_slot":348921810,"sell_lamports":61590721,"tokens_sold":2000000000000,"sell_slot":348921812,"pnl_lamports":193654,"timestamp":1750780592}
{"mint":"YWHvhAWSMddbnWMtZ1yV1nhDNVAtA4SPJwJnc7Cpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":76060939,"tokens_received":0,"buy_slot":348921836,"sell_lamports":84497393,"tokens_sold":2000000000000,"sell_slot":348921839,"pnl_lamports":8436454,"timestamp":1750780603}
{"mint":"7YcGpgdcGe12mPgQSGqvbBUjTBQrnQ7ncZwhJD6spump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":65789598,"tokens_received":0,"buy_slot":348921884,"sell_lamports":78228983,"tokens_sold":2000000000000,"sell_slot":348921886,"pnl_lamports":12439385,"timestamp":1750780621}
{"mint":"F9p4NK7kQyik7PTyESZP7Qq9LB2Mi7e4YSvvApAqpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":62940981,"tokens_received":0,"buy_slot":348921923,"sell_lamports":55774219,"tokens_sold":2000000000000,"sell_slot":348921930,"pnl_lamports":-7166762,"timestamp":1750780638}
{"mint":"H9YKYRF3QSaN6gLiSHXLgN9jkLL8YNy3N6LfiQ7Zpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":66451413,"tokens_received":0,"buy_slot":348921948,"sell_lamports":58970843,"tokens_sold":2000000000000,"sell_slot":348921956,"pnl_lamports":-7480570,"timestamp":1750780649}
{"mint":"DHAkZieMnbcNBBoRaPriKRhyJL96r4SSnazbaQJpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":62835541,"tokens_received":0,"buy_slot":348921988,"sell_lamports":58377427,"tokens_sold":0,"sell_slot":348921996,"pnl_lamports":-4458114,"timestamp":1750780666}
{"mint":"2iMHfPLU1mVKQ1LA1wxX141ppXRebbFJaPRiq4QGpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":77534598,"tokens_received":0,"buy_slot":348922008,"sell_lamports":74746215,"tokens_sold":0,"sell_slot":348922061,"pnl_lamports":-2788383,"timestamp":1750780690}
{"mint":"5sKaSuEjnKCk78dhCnMHrBw8hibkuvAu4wXBvr18pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63612975,"tokens_received":0,"buy_slot":348922082,"sell_lamports":59132115,"tokens_sold":2000000000000,"sell_slot":348922094,"pnl_lamports":-4480860,"timestamp":1750780704}
{"mint":"GqBGxcrFURRRzmSDVBAWbvdCaAr7ZiS46u9xF5BTpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58640654,"tokens_received":0,"buy_slot":348922095,"sell_lamports":55446996,"tokens_sold":0,"sell_slot":348922131,"pnl_lamports":-3193658,"timestamp":1750780719}
{"mint":"Gz9fgqH1zdjrQcGSAKM3p1LhbjwSYZYFbUEgaxdkpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":64099711,"tokens_received":0,"buy_slot":348922139,"sell_lamports":60112225,"tokens_sold":0,"sell_slot":348922141,"pnl_lamports":-3987486,"timestamp":1750780723}
{"mint":"JAULmkXKAYPb7LtzLwKRvv9RuhKfa2BHqLRnFzCUpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":62354825,"tokens_received":0,"buy_slot":348922192,"sell_lamports":57422024,"tokens_sold":2000000000000,"sell_slot":348922201,"pnl_lamports":-4932801,"timestamp":1750780747}
{"mint":"5qWm348XTs6n5fygzumMHmjr8K7CggSgPQfMo3Sepump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":66035518,"tokens_received":0,"buy_slot":348922228,"sell_lamports":68444613,"tokens_sold":2000000000000,"sell_slot":348922230,"pnl_lamports":2409095,"timestamp":1750780759}
{"mint":"9yzz3jASKveAsFy28eCE67DMTtFukMBsqY23RAYzpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":58636918,"tokens_received":0,"buy_slot":348922259,"sell_lamports":57536067,"tokens_sold":2000000000000,"sell_slot":348922260,"pnl_lamports":-1100851,"timestamp":1750780771}
{"mint":"DC8hVnwsNKAdFDfDrhULLTHQewyBYxd7DhQvw3LfkMSx","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":66559643,"tokens_received":0,"buy_slot":348922283,"sell_lamports":58968010,"tokens_sold":0,"sell_slot":348922288,"pnl_lamports":-7591633,"timestamp":1750780782}
{"mint":"HWAy2jF9YxwkkYCakuB4oXUMwiAWoHWNz7L9CKWzpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":69276780,"tokens_received":0,"buy_slot":348922298,"sell_lamports":61562354,"tokens_sold":2000000000000,"sell_slot":348922308,"pnl_lamports":-7714426,"timestamp":1750780791}
{"mint":"5gSAhjhryMaLurtSPTmmXcq83J2A7xkc9EqYCWT2pump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":5000,"tokens_received":0,"buy_slot":348924397,"sell_lamports":0,"tokens_sold":0,"sell_slot":348924482,"pnl_lamports":-5000,"timestamp":1750781674}
{"mint":"Dzc9wxBKcGjaeKJQQnyqnxfYDXa5AZdqSK4wVAaUpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":63641160,"tokens_received":0,"buy_slot":348924497,"sell_lamports":58932115,"tokens_sold":2000000000000,"sell_slot":348924504,"pnl_lamports":-4709045,"timestamp":1750781682}
{"mint":"3hdnm9VmE8XeaFmLJj9LwfTjWXzbgC1QS13t7BFHpump","creator":"ETiN5Tdx2zGfJGuB5XN29sGybPk7i1rZrYzLeSY1BviH","buy_lamports":62820272,"tokens_received":0,"buy_slot":348924517,"sell_lamports":56834800,"tokens_sold":2000000000000,"sell_slot":348924525,"pnl_lamports":-5985472,"timestamp":1750781691}