Created
December 3, 2025 22:33
-
-
Save mgild/4165e655bc460f0e37c50e68f4062342 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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