Skip to content

Instantly share code, notes, and snippets.

@mgild
Created December 3, 2025 22:33
Show Gist options
  • Select an option

  • Save mgild/4165e655bc460f0e37c50e68f4062342 to your computer and use it in GitHub Desktop.

Select an option

Save mgild/4165e655bc460f0e37c50e68f4062342 to your computer and use it in GitHub Desktop.
use std::str::FromStr;
use anyhow_ext::{anyhow, Result};
use fusionamm_client::FusionPool;
use fusionamm_core::sqrt_price_to_price;
use rust_decimal::Decimal;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
/// DefiTuna program ID: tuna4uSQZncNeeiAMKbstuxA9CUkHH6HmC64wgmnogD
pub const DEFITUNA_PROGRAM_ID: &str = "tuna4uSQZncNeeiAMKbstuxA9CUkHH6HmC64wgmnogD";
/// Calculate swap price for DefiTuna FusionAMM pool
///
/// This function fetches the pool account data and calculates the current
/// exchange rate (price) for swapping token A to token B.
///
/// # Arguments
/// * `client` - RPC client for fetching on-chain data
/// * `pool` - Pool address as a string
///
/// # Returns
/// * `Result<Decimal>` - The current swap price (token B per 1 token A)
pub async fn calculate_swap_price(client: &RpcClient, pool: String) -> Result<Decimal> {
// Parse pool address
let pool_address = Pubkey::from_str(&pool)
.map_err(|e| anyhow!("DefiTuna: Invalid pool address: {}", e))?;
// Fetch pool account data
let account_data = client
.get_account_data(&pool_address)
.await
.map_err(|e| anyhow!("DefiTuna: Failed to fetch pool account: {}", e))?;
// Verify minimum account size - FusionPool has LEN = 423 bytes
if account_data.len() < FusionPool::LEN {
return Err(anyhow!(
"DefiTuna: Invalid account data size: {} bytes (expected at least {})",
account_data.len(),
FusionPool::LEN
));
}
// Deserialize the FusionPool account
let fusion_pool = FusionPool::from_bytes(&account_data)
.map_err(|e| anyhow!("DefiTuna: Failed to deserialize pool state: {}", e))?;
// Fetch token mint decimals
let mint_a_data = client
.get_account_data(&fusion_pool.token_mint_a)
.await
.map_err(|e| anyhow!("DefiTuna: Failed to fetch token A mint: {}", e))?;
let mint_b_data = client
.get_account_data(&fusion_pool.token_mint_b)
.await
.map_err(|e| anyhow!("DefiTuna: Failed to fetch token B mint: {}", e))?;
// Parse mint decimals (decimals is at offset 44 in SPL Token mint account)
let decimals_a = get_mint_decimals(&mint_a_data)?;
let decimals_b = get_mint_decimals(&mint_b_data)?;
// Convert sqrt_price to actual price using fusionamm-core function
let price = sqrt_price_to_price(fusion_pool.sqrt_price, decimals_a, decimals_b);
// Convert to Decimal
Decimal::try_from(price)
.map_err(|_| anyhow!("DefiTuna: Failed to convert price to Decimal"))
}
/// Extract decimals from SPL Token mint account data
/// Supports both SPL Token and Token-2022 mints
fn get_mint_decimals(mint_data: &[u8]) -> Result<u8> {
use solana_sdk::program_pack::Pack;
use spl_token::state::Mint;
use spl_token_2022::{extension::StateWithExtensions, state::Mint as Mint2022};
// Try SPL Token first
if let Ok(mint) = Mint::unpack_unchecked(mint_data) {
return Ok(mint.decimals);
}
// Try Token-2022
if let Ok(mint) = StateWithExtensions::<Mint2022>::unpack(mint_data) {
return Ok(mint.base.decimals);
}
Err(anyhow!("DefiTuna: Failed to parse mint account"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils;
// Example DefiTuna FusionAMM pool from docs: 7VuKeevbvbQQcxz6N4SNLmuq6PYy4AcGQRDssoqo4t65
#[tokio::test]
async fn test_calculate_swap_price() {
let ctx = test_utils::get_test_task_runner_context(true);
let result = calculate_swap_price(
&ctx.mainnet_rpc(),
"7VuKeevbvbQQcxz6N4SNLmuq6PYy4AcGQRDssoqo4t65".to_string(),
)
.await;
println!("DefiTuna pool price result: {:?}", result);
assert!(result.is_ok());
let price = result.unwrap();
println!("DefiTuna FusionAMM pool price: {:?}", price);
// Price should be a positive non-zero value
assert!(price > Decimal::ZERO);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment