precolator-program 1.0.0

Core Rust library for the Precolator perpetual futures trading protocol on Solana — oracle management, position handling, risk engine, and liquidation system.
Documentation
// Utility functions and helpers

use crate::errors::{ProgramError, Result};
use solana_program::pubkey::Pubkey;

/// Calculate percentage with basis points
pub fn calculate_bps_amount(amount: u64, bps: u16) -> Result<u64> {
    let result = (amount as u128 * bps as u128) / 10_000;
    Ok(result as u64)
}

/// Check if account is valid
pub fn validate_account(account: &Pubkey) -> Result<()> {
    if account == &Pubkey::default() {
        return Err(ProgramError::Unauthorized);
    }
    Ok(())
}

/// Safe multiplication
pub fn safe_mul(a: u64, b: u64) -> Result<u64> {
    a.checked_mul(b).ok_or(ProgramError::Overflow)
}

/// Safe division
pub fn safe_div(a: u64, b: u64) -> Result<u64> {
    if b == 0 {
        return Err(ProgramError::InvalidAmount);
    }
    Ok(a / b)
}

/// Safe subtraction
pub fn safe_sub(a: u64, b: u64) -> Result<u64> {
    a.checked_sub(b).ok_or(ProgramError::Overflow)
}

/// Safe addition
pub fn safe_add(a: u64, b: u64) -> Result<u64> {
    a.checked_add(b).ok_or(ProgramError::Overflow)
}

/// Format lamports to SOL string
pub fn format_lamports_to_sol(lamports: u64) -> String {
    let sol = lamports as f64 / 1_000_000_000.0;
    format!("{:.9}", sol)
}

/// Parse SOL string to lamports
pub fn parse_sol_to_lamports(sol_str: &str) -> Result<u64> {
    let sol: f64 = sol_str.parse().map_err(|_| ProgramError::InvalidAmount)?;
    Ok((sol * 1_000_000_000.0) as u64)
}

/// Calculate percentage change
pub fn calculate_percentage_change(from: u64, to: u64) -> Result<i32> {
    if from == 0 {
        return Ok(0);
    }

    if to >= from {
        let change = ((to - from) as i128 * 10_000) / from as i128;
        Ok(change as i32)
    } else {
        let change = -((from - to) as i128 * 10_000) as i32 / from as i32;
        Ok(change)
    }
}

/// Round up to next power of 2
pub fn next_power_of_2(mut n: u64) -> u64 {
    if n == 0 {
        return 1;
    }
    n -= 1;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    n |= n >> 32;
    n + 1
}

/// Clamp value between min and max
pub fn clamp<T: PartialOrd>(value: T, min: T, max: T) -> T {
    if value < min {
        min
    } else if value > max {
        max
    } else {
        value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_calculate_bps_amount() {
        let result = calculate_bps_amount(1_000_000, 500);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 50_000);
    }

    #[test]
    fn test_safe_mul() {
        let result = safe_mul(1000, 2000);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 2_000_000);
    }

    #[test]
    fn test_percentage_change() {
        let result = calculate_percentage_change(100, 110);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 1000); // 10%
    }
}