use ostium_rust_sdk::abi::{OSTIUM_TRADING_ABI, TRADING_STORAGE_ABI, USDC_ABI};
use ostium_rust_sdk::contracts::fetcher::ContractFetcher;
use ostium_rust_sdk::error::{OstiumError, Result};
use reqwest::Client;
use serde_json::Value;
use std::collections::HashMap;
const OSTIUM_PYTHON_SDK_ABI_URL: &str = "https://raw.githubusercontent.com/0xOstium/ostium-python-sdk/main/ostium_python_sdk/abi/abi.py";
#[tokio::main]
async fn main() -> Result<()> {
println!("🔄 Fetching latest ABIs from Ostium Python SDK...");
println!("✅ Built-in ABIs available:");
println!(" 📄 USDC ABI: {} characters", USDC_ABI.len());
println!(
" 📄 Ostium Trading ABI: {} characters",
OSTIUM_TRADING_ABI.len()
);
println!(
" 📄 Trading Storage ABI: {} characters",
TRADING_STORAGE_ABI.len()
);
match fetch_latest_abis_from_python_sdk().await {
Ok(abis) => {
println!(
"✅ Successfully fetched {} ABIs from Python SDK:",
abis.len()
);
for (name, abi_json) in abis.iter() {
println!(" 📄 {}: {} characters", name, abi_json.len());
match serde_json::from_str::<Value>(abi_json) {
Ok(_) => println!(" ✅ Valid JSON ABI"),
Err(e) => println!(" ❌ Invalid JSON: {}", e),
}
}
}
Err(e) => {
println!("❌ Failed to fetch ABIs from Python SDK: {}", e);
}
}
println!("\n🔍 Comparing built-in vs fetched ABIs...");
if let Ok(fetched_abis) = fetch_latest_abis_from_python_sdk().await {
for (name, fetched_abi) in fetched_abis.iter() {
let built_in_abi = match name.as_str() {
"usdc" => Some(USDC_ABI),
"ostium_trading" => Some(OSTIUM_TRADING_ABI),
"trading_storage" => Some(TRADING_STORAGE_ABI),
_ => None,
};
if let Some(built_in) = built_in_abi {
if fetched_abi.trim() == built_in.trim() {
println!(" ✅ {} ABI matches", name);
} else {
println!(" ⚠️ {} ABI differs from built-in version", name);
println!(
" Built-in: {} chars, Fetched: {} chars",
built_in.len(),
fetched_abi.len()
);
}
} else {
println!(" 🆕 {} ABI is new (not in built-in)", name);
}
}
}
println!("\n🔄 Testing contract fetcher functionality...");
if let Err(e) = check_for_updates_example().await {
println!("❌ Contract fetcher test failed: {}", e);
}
Ok(())
}
async fn fetch_latest_abis_from_python_sdk() -> Result<HashMap<String, String>> {
let client = Client::new();
let response = client
.get(OSTIUM_PYTHON_SDK_ABI_URL)
.header("User-Agent", "ostium-rust-sdk")
.send()
.await
.map_err(|e| OstiumError::network(format!("Failed to fetch Python SDK: {}", e)))?;
let python_abi_content = response
.text()
.await
.map_err(|e| OstiumError::network(format!("Failed to read response: {}", e)))?;
parse_python_abis(&python_abi_content)
}
fn parse_python_abis(python_content: &str) -> Result<HashMap<String, String>> {
let mut abis = HashMap::new();
let lines: Vec<&str> = python_content.lines().collect();
let mut current_abi_name = String::new();
let mut current_abi_content = String::new();
let mut in_abi = false;
let mut bracket_count = 0;
for line in lines {
let trimmed = line.trim();
if trimmed.ends_with("_abi = [") {
if let Some(name_part) = trimmed.strip_suffix("_abi = [") {
current_abi_name = name_part.trim().to_string();
current_abi_content = String::from("[\n");
in_abi = true;
bracket_count = 1;
continue;
}
}
if in_abi {
for ch in trimmed.chars() {
match ch {
'[' | '{' => bracket_count += 1,
']' | '}' => bracket_count -= 1,
_ => {}
}
}
current_abi_content.push_str(line);
current_abi_content.push('\n');
if bracket_count == 0 {
in_abi = false;
let json_abi = convert_python_to_json(¤t_abi_content)?;
let contract_name = if current_abi_name.ends_with("_abi") {
current_abi_name.trim_end_matches("_abi").to_string()
} else {
current_abi_name.clone()
};
abis.insert(contract_name, json_abi);
current_abi_name.clear();
current_abi_content.clear();
}
}
}
Ok(abis)
}
fn convert_python_to_json(python_abi: &str) -> Result<String> {
let mut json_content = python_abi
.replace("True", "true")
.replace("False", "false")
.replace("None", "null");
json_content = json_content
.lines()
.map(|line| {
let line_without_comment = if let Some(comment_pos) = line.find('#') {
let before_comment = &line[..comment_pos];
let quote_count = before_comment.matches('"').count();
if quote_count % 2 == 0 {
before_comment.trim_end().to_string()
} else {
line.to_string()
}
} else {
line.to_string()
};
let trimmed_no_comment = line_without_comment.trim();
if trimmed_no_comment.ends_with(",}") {
line_without_comment.replace(",}", "}")
} else if trimmed_no_comment.ends_with(",]") {
line_without_comment.replace(",]", "]")
} else {
line_without_comment
}
})
.filter(|line| {
let trimmed = line.trim();
!trimmed.is_empty() && !trimmed.starts_with('#')
})
.collect::<Vec<String>>()
.join("\n");
json_content = json_content.trim().to_string();
if let Some(last_bracket_pos) = json_content.rfind(']') {
let after_bracket = &json_content[last_bracket_pos + 1..];
if after_bracket.trim().is_empty() || after_bracket.trim().starts_with('#') {
json_content = json_content[..=last_bracket_pos].to_string();
}
}
let _: serde_json::Value = serde_json::from_str(&json_content)
.map_err(|e| OstiumError::parsing(format!("Failed to parse ABI as JSON: {}", e)))?;
Ok(json_content)
}
pub async fn check_for_updates_example() -> Result<()> {
let mut fetcher = ContractFetcher::new();
let known_contracts = vec![
];
match fetcher.check_for_updates(&known_contracts).await {
Ok(updates) => {
if updates.is_empty() {
println!("✅ All contracts are up to date");
} else {
println!("🔄 Found {} contract updates:", updates.len());
for contract in updates {
println!(" 📄 {} has been updated", contract.name);
}
}
}
Err(e) => {
println!("❌ Failed to check for updates: {}", e);
}
}
Ok(())
}