dloom-flow 1.0.0

A Solana flow payment program created with Anchor
Documentation
// FILE: programs/dloom_flow/src/instructions/update_amm_fees.rs

use crate::{
    constants::BASIS_POINT_MAX,
    errors::DloomError,
    events::AmmFeesUpdated,
    state::{ProtocolConfig},
    amm::{state::{AmmPool}}
};
use anchor_lang::prelude::*;

pub fn handle_update_amm_fees(
    ctx: Context<UpdateAmmFees>,
    new_fee_rate: Option<u16>,
) -> Result<()> {
    let amm_pool = &mut ctx.accounts.amm_pool;
    let now = Clock::get()?.unix_timestamp;

    if let Some(manual_fee_rate) = new_fee_rate {
        // MANUAL UPDATE: The authority provides a specific new fee rate.
        require!(
            manual_fee_rate as u128 <= BASIS_POINT_MAX,
            DloomError::InvalidFeeRates
        );
        amm_pool.fee_rate = manual_fee_rate;
    } else {
        // AUTOMATED UPDATE: Based on price volatility.
        let time_elapsed = now
            .checked_sub(amm_pool.last_fee_update_timestamp)
            .ok_or(DloomError::MathOverflow)?;

        // Require at least an hour to pass before another auto-update.
        require!(time_elapsed > 3600, DloomError::UpdateNotNeeded);

        // Update the oracle to get the latest cumulative price.
        crate::amm::instructions::swap::update_oracle(amm_pool)?;

        // Simple metric for volatility: change in cumulative price over time.
        // This now uses the snapshot taken at the last fee update.
        let price_change = if amm_pool.price_a_cumulative_last_fee_update > 0 {
            (amm_pool.price_a_cumulative as i128)
                .checked_sub(amm_pool.price_a_cumulative_last_fee_update as i128)
                .ok_or(DloomError::MathOverflow)?
                .abs() as u128
        } else {
            0
        };

        let volatility = price_change.checked_div(time_elapsed as u128).unwrap_or(0);

        // Example Formula: Base fee of 0.25% + volatility component
        let base_fee = 25; // 0.25% in basis points
        let dynamic_fee = (volatility / 1_000_000) as u16; // Cap dynamic part at 0.75%
        let new_dynamic_rate = (base_fee + dynamic_fee).min(100); // Cap total fee at 1%

        amm_pool.fee_rate = new_dynamic_rate;
    }

    // Reset the timestamp and snapshot after any update.
    amm_pool.last_fee_update_timestamp = now;
    amm_pool.price_a_cumulative_last_fee_update = amm_pool.price_a_cumulative;

    emit!(AmmFeesUpdated {
        pool_address: amm_pool.key(),
        new_fee_rate: amm_pool.fee_rate,
    });

    Ok(())
}

#[derive(Accounts)]
pub struct UpdateAmmFees<'info> {
    pub authority: Signer<'info>,
    #[account(seeds = [b"protocol_config"], bump, has_one = authority)]
    pub protocol_config: Account<'info, ProtocolConfig>,
    #[account(mut, constraint = amm_pool.authority == authority.key() @ DloomError::Unauthorized)]
    pub amm_pool: Box<Account<'info, AmmPool>>,
}