diff --git a/src/account.rs b/src/account.rs index 829f916..6210ecc 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,7 +1,6 @@ use std::{collections::HashMap, path::PathBuf}; -use crate::executor::pump_fun::PumpDevAccount; -use anyhow::{Context, anyhow}; +use anyhow::Context; use serde::{Deserialize, Serialize}; pub fn get_accounts_path() -> anyhow::Result { @@ -13,47 +12,21 @@ pub fn get_accounts_path() -> anyhow::Result { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum Account { - PumpDev(PumpDevAccount), +pub struct Account { + #[serde(rename = "publicKey")] + pub public_key: String, + #[serde(rename = "privateKey")] + pub private_key: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AccountManager { + #[serde(rename = "apiKey")] + pub api_key: String, pub active: String, pub accounts: HashMap, } -impl Account { - pub async fn new() -> anyhow::Result { - let response = reqwest::Client::new() - .post("https://pumpdev.io/api/wallet/create") - .json(&serde_json::json!({})) - .send() - .await - .context("Failed to create PumpDev wallet")?; - - if !response.status().is_success() { - let status = response.status(); - let body: serde_json::Value = response.json().await.unwrap_or_default(); - let message = body - .get("error") - .and_then(serde_json::Value::as_str) - .unwrap_or("Unknown error"); - return Err(anyhow!( - "Failed to create PumpDev wallet ({status}): {message}" - )); - } - - let account: PumpDevAccount = response - .json() - .await - .context("Failed to parse PumpDev wallet")?; - - Ok(Self::PumpDev(account)) - } -} - impl AccountManager { pub async fn get() -> anyhow::Result { let path = get_accounts_path()?; @@ -74,10 +47,17 @@ impl AccountManager { pub async fn new() -> anyhow::Result { let mut accounts = HashMap::new(); - accounts.insert("default".to_string(), Account::new().await?); + accounts.insert( + "default".to_string(), + Account { + public_key: String::new(), + private_key: String::new(), + }, + ); Ok(Self { active: "default".to_string(), + api_key: String::new(), accounts, }) } diff --git a/src/bot.rs b/src/bot.rs index 13ad9da..d75126a 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -11,7 +11,7 @@ use tokio_tungstenite::{MaybeTlsStream, WebSocketStream, connect_async}; use crate::{ account::AccountManager, - executor::ExecutorWrapper, + executor::{ExecutorWrapper, pump_fun::PumpDev}, strategy::{Strategy, veloc::MomentumVelocityStrategy}, tradelog::TradeLog, }; @@ -73,7 +73,7 @@ impl Bot { Ok(Arc::new(Self { ws: Mutex::new(connect_async("wss://pumpdev.io/ws").await?.0), executor: Mutex::new(ExecutorWrapper { - executor: Box::new(account.executor()), + executor: Box::new(PumpDev { account }), positions: HashMap::new(), }), accounts: Mutex::new(accounts), @@ -97,7 +97,7 @@ impl Bot { .context("Failed to get account")? .clone(); - self.executor.lock().await.executor = Box::new(account.executor()); + self.executor.lock().await.executor = Box::new(PumpDev { account }); Ok(()) } diff --git a/src/executor/mod.rs b/src/executor/mod.rs index 851ae1d..1a25ed0 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -4,8 +4,6 @@ use std::collections::HashMap; use rust_decimal::Decimal; -use crate::account::Account; - pub struct ExecutorWrapper { pub executor: Box, pub positions: HashMap, @@ -45,14 +43,6 @@ pub trait Executor: Send + Sync { } } -impl Account { - pub fn executor(self) -> impl Executor { - match self { - Self::PumpDev(account) => pump_fun::PumpDev::new(account), - } - } -} - impl ExecutorWrapper { pub async fn buy( &mut self, diff --git a/src/executor/pump_fun.rs b/src/executor/pump_fun.rs index 186e0c0..d29c7eb 100644 --- a/src/executor/pump_fun.rs +++ b/src/executor/pump_fun.rs @@ -1,87 +1,9 @@ -use anyhow::{Context, anyhow}; -use reqwest::Client; use rust_decimal::Decimal; -use serde::{Deserialize, Serialize}; -use tokio::sync::Mutex; -use crate::executor::Executor; - -const TRADE_LIGHTNING_URL: &str = "https://pumpdev.io/api/trade-lightning"; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PumpDevAccount { - #[serde(rename = "apiKey")] - pub api_key: String, - #[serde(rename = "publicKey")] - pub public_key: String, - #[serde(rename = "privateKey")] - pub private_key: String, -} - -#[derive(Debug, Deserialize)] -struct TradeResponse { - signature: String, -} +use crate::{account::Account, executor::Executor}; pub struct PumpDev { - pub account: Mutex, - pub client: Client, -} - -impl PumpDev { - pub fn new(account: PumpDevAccount) -> Self { - Self { - account: Mutex::new(account), - client: Client::new(), - } - } - - async fn trade( - &self, - action: &str, - mint: &str, - amount: String, - priority: Decimal, - slippage: u16, - denominated_in_sol: bool, - ) -> anyhow::Result<()> { - let account = self.account.lock().await; - - let response = self - .client - .post(format!("{TRADE_LIGHTNING_URL}?api-key={}", account.api_key)) - .json(&serde_json::json!({ - "action": action, - "mint": mint, - "amount": amount, - "denominatedInSol": if denominated_in_sol { "true" } else { "false" }, - "slippage": slippage, - "priorityFee": priority, - })) - .send() - .await - .context("Failed to send trade request")?; - - let status = response.status(); - let body = response.text().await?; - - if !status.is_success() { - let error = serde_json::from_str::(&body) - .ok() - .and_then(|value| value.get("error").cloned()) - .and_then(|value| value.as_str().map(str::to_string)) - .unwrap_or_else(|| body.trim().to_string()); - - return Err(anyhow!("Trade failed ({status}): {error}")); - } - - let data: TradeResponse = - serde_json::from_str(&body).context("Failed to parse trade response")?; - - log::info!("{action} executed: {}", data.signature); - - Ok(()) - } + pub account: Account, } #[async_trait::async_trait]