use crate::{primitives::JUP_API, SolanaAgentKit};
use anyhow::Result;
use base64::{engine::general_purpose, Engine as _};
use serde::{Deserialize, Serialize};
use solana_sdk::{
commitment_config::CommitmentConfig, program_pack::Pack, pubkey::Pubkey, transaction::VersionedTransaction,
};
use spl_token::state::Mint;
use std::str::FromStr;
#[derive(Serialize)]
struct SwapRequest {
#[serde(rename = "quoteResponse")]
quote_response: QuoteResponse,
#[serde(rename = "userPublicKey")]
user_public_key: String,
#[serde(rename = "wrapAndUnwrapSol")]
wrap_and_unwrap_sol: bool,
#[serde(rename = "dynamicComputeUnitLimit")]
dynamic_compute_unit_limit: bool,
#[serde(rename = "prioritizationFeeLamports")]
prioritization_fee_lamports: String,
#[serde(rename = "feeAccount")]
fee_account: Option<String>,
}
#[derive(Deserialize)]
struct SwapResponse {
#[serde(rename = "swapTransaction")]
swap_transaction: String,
}
#[derive(Deserialize, Serialize)]
struct QuoteResponse {
#[serde(flatten)]
extra: serde_json::Value,
}
pub async fn trade(
agent: &SolanaAgentKit,
output_mint: &str,
input_amount: f64,
input_mint: Option<String>,
slippage_bps: Option<u32>,
) -> Result<String, Box<dyn std::error::Error>> {
let output_mint = Pubkey::from_str(output_mint)?;
let input_mint = input_mint.as_deref().map(Pubkey::from_str).transpose()?.unwrap_or(spl_token::native_mint::id());
let slippage_bps = slippage_bps.unwrap_or(300);
let is_native_sol = input_mint == spl_token::native_mint::id();
let input_decimals = if is_native_sol {
9
} else {
let account = agent.connection.get_account(&input_mint)?;
let mint = Mint::unpack(&account.data)?;
mint.decimals
};
let scaled_amount = (input_amount * 10f64.powf(input_decimals as f64)) as u64;
let quote_url = format!(
"{}/quote?inputMint={}&outputMint={}&amount={}&slippageBps={}&onlyDirectRoutes=true&maxAccounts=20",
JUP_API, input_mint, output_mint, scaled_amount, slippage_bps
);
let client = reqwest::Client::new();
let quote_response: QuoteResponse = client.get("e_url).send().await?.json().await?;
let swap_request = SwapRequest {
quote_response,
user_public_key: agent.wallet.address.to_string(),
wrap_and_unwrap_sol: true,
dynamic_compute_unit_limit: true,
prioritization_fee_lamports: "auto".to_string(),
fee_account: None,
};
let swap_response: SwapResponse =
client.post(format!("{}/swap", JUP_API)).json(&swap_request).send().await?.json().await?;
let swap_transaction =
general_purpose::STANDARD.decode(&swap_response.swap_transaction).expect("decode swap_transaction");
let versioned_transaction: VersionedTransaction = bincode::deserialize(&swap_transaction)?;
let signed_transaction = VersionedTransaction::try_new(versioned_transaction.message, &[&agent.wallet.wallet])?;
let signature = agent.connection.send_transaction(&signed_transaction)?;
let latest_blockhash = agent.connection.get_latest_blockhash()?;
agent.connection.confirm_transaction_with_spinner(&signature, &latest_blockhash, CommitmentConfig::confirmed())?;
Ok(signature.to_string())
}