pub mod transaction;
use isahc::AsyncReadResponseExt;
use serde::{Deserialize, Serialize};
use std::{fs::File, io::Read};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenMetadata {
pub name: String,
pub symbol: String,
pub description: String,
pub image: String,
pub show_name: bool,
pub created_on: String,
pub twitter: Option<String>,
pub telegram: Option<String>,
pub website: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenMetadataResponse {
pub metadata: TokenMetadata,
pub metadata_uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateTokenMetadata {
pub name: String,
pub symbol: String,
pub description: String,
pub file: String,
pub twitter: Option<String>,
pub telegram: Option<String>,
pub website: Option<String>,
}
pub async fn create_token_metadata(
metadata: CreateTokenMetadata,
) -> Result<TokenMetadataResponse, Box<dyn std::error::Error>> {
let boundary = "------------------------f4d9c2e8b7a5310f";
let mut body = Vec::new();
fn append_text_field(body: &mut Vec<u8>, boundary: &str, name: &str, value: &str) {
body.extend_from_slice(b"--");
body.extend_from_slice(boundary.as_bytes());
body.extend_from_slice(b"\r\n");
body.extend_from_slice(
format!("Content-Disposition: form-data; name=\"{}\"\r\n\r\n", name).as_bytes(),
);
body.extend_from_slice(value.as_bytes());
body.extend_from_slice(b"\r\n");
}
append_text_field(&mut body, boundary, "name", &metadata.name);
append_text_field(&mut body, boundary, "symbol", &metadata.symbol);
append_text_field(&mut body, boundary, "description", &metadata.description);
if let Some(twitter) = metadata.twitter {
append_text_field(&mut body, boundary, "twitter", &twitter);
}
if let Some(telegram) = metadata.telegram {
append_text_field(&mut body, boundary, "telegram", &telegram);
}
if let Some(website) = metadata.website {
append_text_field(&mut body, boundary, "website", &website);
}
append_text_field(&mut body, boundary, "showName", "true");
body.extend_from_slice(b"--");
body.extend_from_slice(boundary.as_bytes());
body.extend_from_slice(b"\r\n");
body.extend_from_slice(b"Content-Disposition: form-data; name=\"file\"; filename=\"file\"\r\n");
body.extend_from_slice(b"Content-Type: application/octet-stream\r\n\r\n");
let mut file = File::open(&metadata.file)?;
let mut file_contents = Vec::new();
file.read_to_end(&mut file_contents)?;
body.extend_from_slice(&file_contents);
body.extend_from_slice(b"\r\n--");
body.extend_from_slice(boundary.as_bytes());
body.extend_from_slice(b"--\r\n");
let client = isahc::HttpClient::new()?;
let request = isahc::Request::builder()
.method("POST")
.uri("https://pump.fun/api/ipfs")
.header(
"Content-Type",
format!("multipart/form-data; boundary={}", boundary),
)
.header("Content-Length", body.len() as u64)
.body(isahc::AsyncBody::from(body))?;
let mut response = client.send_async(request).await?;
let text = response.text().await?;
let json: TokenMetadataResponse = serde_json::from_str(&text)?;
Ok(json)
}
pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 {
amount + (amount * basis_points) / 10000
}
pub fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 {
amount - (amount * basis_points) / 10000
}