Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Implement Dynamic Fee Tiers in an AMM

A step-by-step developer guide for implementing dynamic, market-responsive fee structures in automated market makers using hooks and on-chain data.
Chainscore © 2026
introduction
AMM DESIGN

Introduction to Dynamic Fee Tiers

A guide to implementing variable transaction fees that adapt to market conditions, improving capital efficiency and LP returns.

Dynamic fee tiers are a mechanism in Automated Market Makers (AMMs) that allow the protocol to adjust the trading fee percentage based on real-time market volatility and pool activity. Unlike static fee models, where a pool charges a fixed rate (e.g., 0.3% for Uniswap V2), dynamic tiers create a more responsive and capital-efficient system. This design is central to next-generation AMMs like Uniswap V4, which introduces hooks—smart contracts that can execute custom logic at key points in a pool's lifecycle. By linking fee adjustments to on-chain data, protocols can better compensate liquidity providers (LPs) for risk during high-volatility periods and attract more volume during calm markets.

Implementing dynamic fees requires a hook contract that can read and react to market state. The core logic typically involves monitoring an oracle for price volatility, tracking trading volume, or measuring the pool's imbalance. For example, a simple implementation could use a moving average of price changes from a Chainlink oracle. When the observed volatility exceeds a predefined threshold, the hook can propose increasing the fee tier from a base rate of 0.05% to 0.30% for a set period. This fee adjustment is applied to the pool's swap function, directly impacting the amount of tokens deducted from a trader and credited to the LP fee accumulator.

Here is a simplified Solidity snippet demonstrating a hook's beforeSwap function that modifies fees based on a mock volatility check:

solidity
function beforeSwap(address, bool, int256, bytes calldata) external override returns (bytes4) {
    uint256 currentVolatility = _checkVolatility();
    
    if (currentVolatility > VOLATILITY_THRESHOLD) {
        // Dynamically set a higher fee for the upcoming swap
        poolManager.setFee(poolKey, HIGH_FEE_TIER);
    } else {
        poolManager.setFee(poolKey, BASE_FEE_TIER);
    }
    return this.beforeSwap.selector;
}

The poolKey identifies the specific pool, and setFee is a call to the AMM's core manager contract. The hook must have the appropriate permissions, often granted during pool initialization.

Key considerations for a production-grade system include fee transition smoothness, governance, and economic security. Abrupt, large fee changes can lead to arbitrage inefficiencies and user frustration. A robust design might implement a time-weighted schedule or a cap on fee changes per block. Furthermore, control over the volatility parameters and thresholds should be managed via a timelock-controlled governance mechanism to prevent malicious hook updates. It's also critical to audit the hook's gas consumption, as expensive operations in the swap pathway can render the pool economically nonviable due to high transaction costs.

The primary use case is optimizing LP returns relative to risk, known as risk-adjusted returns. During a market crash, high volatility increases impermanent loss risk for LPs; dynamic fees provide additional revenue to offset this. Conversely, low fees in stable markets can attract more volume from competing pools. This flexibility allows a single pool to serve multiple market regimes effectively. Projects like Trader Joe's Liquidity Book employ a similar concept with discrete bins and variable fees, demonstrating the model's viability. When designing your system, backtest fee logic against historical price data to calibrate thresholds that maximize total fees earned for LPs without disproportionately reducing trading volume.

To deploy, you must integrate with an AMM framework that supports hooks, such as Uniswap V4. The development workflow involves writing and auditing the hook contract, defining the pool's key and fee parameters, and initializing the pool with the hook attached via the poolManager. Post-deployment, monitor key metrics like fee accrual rate, volume share, and LP capital flow to iteratively refine your volatility model. Dynamic fee tiers represent a significant evolution in AMM design, moving from passive infrastructure to active, intelligent market-making.

prerequisites
DYNAMIC FEE AMM

Prerequisites and Setup

Before implementing dynamic fee tiers, you need a foundational AMM smart contract and a clear strategy for fee logic. This guide outlines the essential components and initial setup.

To build dynamic fee tiers, start with a functional Automated Market Maker (AMM) core. You can fork an existing codebase like Uniswap V3, which has a mature architecture for concentrated liquidity and fee accounting, or build upon a simpler constant product (x*y=k) model. Ensure your base contract has a well-defined swap function and a mechanism to track and distribute protocol fees. You will need a development environment with Hardhat or Foundry, Node.js, and a testnet RPC URL (e.g., from Alchemy or Infura) for deployment and testing.

The core of a dynamic fee system is an oracle or logic module that determines the appropriate fee tier based on market conditions. Common inputs include - pool utilization (the ratio of reserves to trading volume), - volatility measured by recent price swings, - or external data like gas prices. You must decide if fee updates are permissionless, governed by a DAO, or triggered by automated keepers. For on-chain logic, consider using a time-weighted average price (TWAP) oracle from your own pool or an external provider like Chainlink.

Your contract will need new state variables and functions. Key additions are a feeTier mapping (e.g., tierId to basis points), a getCurrentFee() view function that runs your pricing logic, and a mechanism to update the active tier. Store fee changes in an event for off-chain indexing. It's critical that fee updates have a cooldown period or smoothing function to prevent fee oscillation and front-running. For testing, write scripts that simulate high and low volatility periods to verify fee adjustments behave as intended.

Security is paramount when modifying core AMM economics. Use established libraries like OpenZeppelin for access control (e.g., Ownable for initial admin, timelocks for governance). All state changes, especially to the fee logic parameters, should be behind a multi-sig or time-delayed governance process. Thoroughly audit the interaction between the fee update logic and the swap function to prevent reentrancy or precision errors in fee calculations. Consider formal verification tools for critical paths.

Finally, plan for integration and user experience. Frontends need to query the current fee tier dynamically. Implement a subgraph or a simple view function that returns the fee for a given pool. Document the fee logic clearly for liquidity providers, as dynamic fees impact their expected returns. Start by deploying your modified contract on a testnet like Sepolia or Arbitrum Goerli, using a faucet for ETH, and perform end-to-end swaps with different fee tiers before mainnet deployment.

key-concepts
AMM DESIGN

Core Concepts for Dynamic Fees

Dynamic fee tiers allow AMMs to optimize revenue and user experience by adjusting swap costs based on real-time market conditions like volatility and liquidity depth.

03

Liquidity Concentration Impact

Fees can also be adjusted based on active liquidity depth at the current price tick. Sparse liquidity may warrant higher fees to incentivize LPs.

  • Concept: Measure the capital efficiency of the pool by comparing liquidity in the active tick range to total deposited liquidity.
  • Implementation: In concentrated liquidity AMMs (Uniswap V3/V4), hooks can read liquidity and tick state variables to assess concentration.
  • Goal: Dynamically reward LPs who provide liquidity where it's most needed.
05

Backtesting & Parameter Tuning

Before deployment, fee parameters must be tested against historical market data to ensure they improve LP returns without deterring traders.

  • Process: Simulate the dynamic fee model on past price data for the asset pair.
  • Metrics: Compare total fee revenue and LP impermanent loss against static fee models.
  • Tools: Use historical blockchain data from providers like The Graph or Dune Analytics to model scenarios.
06

Security Considerations & Audits

Dynamic fee hooks add complexity and attack surface. The fee calculation must be resistant to manipulation.

  • Oracle Manipulation: An attacker could potentially inflate volatility readings to trigger high fees for their own profit.
  • Mitigation: Use time-weighted data (TWAPs), multiple oracle sources, and circuit breakers.
  • Essential Step: Any production hook must undergo rigorous smart contract audits by specialized firms before mainnet deployment.
designing-fee-model
AMM ARCHITECTURE

Designing the Fee Adjustment Model

Dynamic fee tiers allow an AMM to adapt to market volatility and liquidity depth, optimizing returns for LPs and costs for traders. This guide explains the core mechanisms and provides a practical implementation blueprint.

A static fee model is a significant limitation for modern Automated Market Makers (AMMs). Dynamic fee tiers introduce a mechanism where the swap fee percentage adjusts automatically based on predefined market conditions, primarily volatility and liquidity concentration. This creates a more efficient market: during periods of high volatility, fees increase to compensate LPs for greater impermanent loss risk, while in calm, deep pools, lower fees can attract more trading volume. Protocols like Uniswap V4 with its "hooks" and Trader Joe's Liquidity Book have pioneered this approach, moving beyond the one-size-fits-all 0.3%, 0.05%, or 0.01% static fees.

The core of the model is a fee adjustment function. This is a smart contract logic that takes one or more on-chain inputs (oracles) and outputs a new fee. A common input is a volatility oracle, which measures price movement over a recent time window (e.g., 1-hour TWAP deviation). Another is liquidity depth, measured by the pool's total value locked (TVL) relative to its trading pair's market cap or the concentration of liquidity around the current price tick. The function maps these inputs to a fee range, for example, between 0.01% and 1.00%.

Implementing this requires a hook or a modified pool manager contract. Below is a simplified Solidity snippet for a volatility-based adjustment hook. It uses a TWAP oracle to check the price movement over the last hour and adjusts the fee accordingly.

solidity
// Pseudocode for a dynamic fee hook
contract VolatilityFeeHook {
    IUniswapV4PoolManager public poolManager;
    IOracle public twapOracle;
    uint256 public baseFee = 500; // 0.05%
    uint256 public maxFee = 1000; // 1.00%
    uint256 public volatilityThreshold = 500; // 0.5% price move

    function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params)
        external
        returns (bytes4)
    {
        uint256 volatility = twapOracle.getVolatility(key.currency0, key.currency1, 1 hours);
        uint256 dynamicFee = volatility > volatilityThreshold ? maxFee : baseFee;
        // Logic to apply `dynamicFee` to the ongoing swap
        poolManager.setFee(key, dynamicFee);
        return this.beforeSwap.selector;
    }
}

Key design considerations include oracle security (using reputable decentralized oracles like Chainlink or robust TWAPs), update frequency (fees shouldn't change too rapidly to avoid gaming), and gas efficiency. The hook must be permissionless and non-custodial. It's also crucial to implement fee caps to prevent the model from suggesting economically irrational fees (e.g., 10%) that would drive away all volume. The parameters (base fee, max fee, threshold) should be governable, often by a DAO, to allow the protocol to adapt its strategy.

For liquidity providers, dynamic fees can lead to more consistent annual percentage yield (APY) by earning higher rewards during risky periods. For traders, it means paying fairer prices: low fees in competitive, liquid markets and higher fees for executing large, volatility-inducing swaps. This aligns incentives more closely than static models. When deploying, extensive backtesting against historical market data is essential to calibrate the adjustment function and avoid unintended consequences like fee spirals or liquidity flight during critical moments.

IMPLEMENTATION STRATEGIES

Dynamic Fee Model Comparison

Comparison of common dynamic fee tier models for AMMs, evaluating their mechanisms, complexity, and suitability.

Model / FeatureVolume-Based TiersVolatility-Adjusted FeesConcentrated Liquidity Tiers

Core Mechanism

Fee % decreases as 24h pool volume increases

Fee % adjusts based on asset price volatility (e.g., TWAP)

Different fee tiers (e.g., 0.05%, 0.3%, 1%) for separate liquidity bands

Implementation Complexity

Medium

High

High

Oracle Dependency

No

Yes (Price Feed)

No

Gas Cost Impact

Low (<5% increase)

High (10-20% increase)

Medium (5-10% increase)

Example Fee Range

0.3% - 0.05%

0.1% - 2.0%

Static: 0.05%, 0.3%, 1%

Best For

High-volume, stable pairs (e.g., ETH/USDC)

Exotic or volatile pairs (e.g., memecoins)

Capital efficiency & LP strategy choice

Adopted By

Uniswap V2 (static), some forks

Uniswap V4 hooks concept

Uniswap V3, PancakeSwap V3

implementing-hook-contract
HOOK CONTRACT TUTORIAL

Implementing Dynamic Fee Tiers in an AMM

This guide explains how to build a Uniswap v4 Hook to create a liquidity pool with multiple, on-chain adjustable fee tiers, moving beyond static fee models.

A dynamic fee tier Hook allows a pool's swap fee to be updated after deployment based on predefined logic, such as time, volume, or governance votes. Unlike traditional AMMs with a fixed fee parameter, this Hook uses the beforeSwap and afterSwap callbacks to apply and potentially modify fees per transaction. The core contract must implement the IPoolManager interface and the BaseHook abstract contract, managing state for the current fee tier and the logic for its adjustment. This enables sophisticated strategies like time-based fee discounts or volume-triggered fee increases.

The key to implementation is the getFee function within your Hook. This function is called by the PoolManager during the swap lifecycle. It must return the fee for the swap, which can be calculated dynamically. For example, a simple two-tier system could offer a 0.05% fee for swaps under 1,000 USDC and 0.30% for larger swaps. The fee state is typically stored in the Hook's storage using a struct, and access is controlled via modifiers to ensure only authorized addresses (e.g., a timelock or DAO) can update the tier parameters.

Here is a minimal skeleton for the dynamic fee logic in a Hook contract:

solidity
contract DynamicFeeHook is BaseHook {
    struct FeeTier {
        uint24 feeBps; // Fee in basis points (e.g., 5 for 0.05%)
        uint256 threshold; // Volume or time threshold
    }
    
    FeeTier public currentTier;
    address public feeAdmin;

    function getFee(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) 
        external view override returns (uint24) {
        // Logic to determine and return the applicable fee
        return currentTier.feeBps;
    }
    
    function setFeeTier(FeeTier calldata newTier) external onlyFeeAdmin {
        currentTier = newTier;
    }
}

The beforeSwap callback can be used to capture pre-swap state for calculations, while afterSwap can update cumulative volume counters that might influence the next fee tier.

When deploying, you must link this Hook to a specific pool via the PoolKey during pool initialization in Uniswap v4. The PoolManager will delegate fee calculations to your Hook's getFee for all swaps in that pool. It is critical to thoroughly test fee update logic and access controls to prevent manipulation. Consider edge cases like flash loans artificially inflating volume; implementing a time-weighted average volume or a cooldown period between tier changes can mitigate this. Always audit the interaction between the Hook's state and the PoolManager's lock mechanism.

Dynamic fee Hooks unlock new AMM design patterns. Use cases include: - Graduated Fees: Lower fees for loyal users or large trades. - Epoch-based Scheduling: Automatically rotating fee tiers weekly to incentivize liquidity. - Governance-controlled Fees: Allowing a DAO to vote on fee changes without migrating liquidity. This moves fee management from a static deployment parameter to an active, programmable component of your pool's economics, aligning incentives more closely with protocol goals and market conditions.

PRACTICAL GUIDES

Code Implementation Examples

Implementing a Dynamic Fee Pool Contract

Below is a simplified Solidity example extending a Uniswap V3-style pool with a basic dynamic fee mechanism. The fee is updated based on time-weighted average volume.

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@uniswap/v3-core/contracts/UniswapV3Pool.sol";

contract DynamicFeePool is UniswapV3Pool {
    uint256 public lastUpdateTime;
    uint256 public rollingVolume;
    uint256 public constant UPDATE_INTERVAL = 1 hours;
    
    // Example fee tiers: 5 bps for low vol, 30 bps for medium, 100 bps for high
    uint24 public constant TIER_LOW = 500;
    uint24 public constant TIER_MED = 3000;
    uint24 public constant TIER_HIGH = 10000;

    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external override returns (int256, int256) {
        // 1. Track volume for the interval
        _updateVolume(uint256(amountSpecified > 0 ? amountSpecified : -amountSpecified));
        
        // 2. Determine current dynamic fee
        uint24 currentFee = _calculateDynamicFee();
        
        // 3. Execute swap with the calculated fee (simplified - actual V3 integrates fee differently)
        // feeGrowthGlobal is updated based on `currentFee`
        
        return super.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data);
    }

    function _updateVolume(uint256 swapAmount) internal {
        if (block.timestamp >= lastUpdateTime + UPDATE_INTERVAL) {
            rollingVolume = swapAmount;
            lastUpdateTime = block.timestamp;
        } else {
            rollingVolume += swapAmount;
        }
    }

    function _calculateDynamicFee() internal view returns (uint24) {
        uint256 avgHourlyVolume = rollingVolume / UPDATE_INTERVAL;
        
        if (avgHourlyVolume < 100 ether) return TIER_LOW;      // Low volume, low fee
        if (avgHourlyVolume < 1000 ether) return TIER_MED;     // Medium volume, standard fee
        return TIER_HIGH;                                      // High volume, high fee
    }
}

Note: This is a conceptual overlay. Integrating with Uniswap V3's architecture requires modifying the FeeGrowthGlobal state and the swap function's fee accounting directly.

backtesting-strategy
DYNAMIC FEE TIERS

Backtesting Your Fee Model

A practical guide to implementing and testing dynamic fee tiers in an Automated Market Maker (AMM) to optimize protocol revenue and user experience.

Dynamic fee tiers allow an AMM to adjust swap fees based on real-time market conditions, such as volatility or liquidity depth. Unlike static fees, a dynamic model can increase fees during high volatility to compensate LPs for impermanent loss risk and lower them in calm markets to attract volume. Implementing this requires a fee calculation function that ingests on-chain data like price oracles, trading volume, or implied volatility from options markets. The core challenge is designing a model that is both responsive to market shifts and resistant to manipulation.

To backtest a fee model, you need historical data. For Ethereum mainnet, you can query The Graph for past swap events, pool reserves, and price data from a specific pair (e.g., WETH/USDC). A simple Python script using web3.py and pandas can simulate applying your proposed fee function to this historical data. Calculate the hypothetical cumulative fees collected and compare them against a baseline static fee (e.g., 0.3%). Key metrics to track include total revenue, volume retention (estimating if higher fees would have driven users away), and the Sharpe ratio of fee income to assess risk-adjusted returns for LPs.

Here is a conceptual code snippet for a volatility-based fee function. It calculates a rolling standard deviation of returns from an oracle price feed and maps it to a fee tier.

python
def calculate_dynamic_fee(price_history, window=24):
    returns = price_history.pct_change()
    volatility = returns.rolling(window=window).std().iloc[-1]
    # Map volatility to a fee between 0.05% and 1%
    base_fee = 0.0005  # 0.05%
    max_fee = 0.01     # 1%
    fee = min(base_fee + (volatility * 10), max_fee)
    return fee

This function would be called by the pool's smart contract, which must securely fetch the volatility metric, often via a trusted oracle like Chainlink.

When deploying a dynamic fee contract, security and gas efficiency are paramount. The fee logic should be executed in a view function first, with the result applied to subsequent swaps. Use OpenZeppelin's AccessControl to restrict fee parameter updates to a governance module. To prevent flash loan manipulation, avoid basing fees on metrics that can be skewed within a single block; instead, use time-weighted averages (TWAPs). Thorough testing on a fork of mainnet using Foundry or Hardhat is essential to simulate real user behavior and edge cases before launch.

Successful implementations, like Uniswap V4's hook architecture, demonstrate the industry trend toward customizable fee logic. Your backtesting should ultimately answer: does the dynamic model generate more protocol revenue and provide a better LP experience than a static alternative, without materially reducing swap volume? Iterate on your model parameters based on the backtest results, and consider implementing a gradual rollout or fee cap to monitor real-world effects before full deployment.

DYNAMIC FEE TIERS

Frequently Asked Questions

Common questions and technical challenges developers face when implementing dynamic fee tiers in Automated Market Makers (AMMs).

A dynamic fee tier adjusts the swap fee percentage based on real-time market conditions, typically using an on-chain oracle for a target metric like volatility or volume. The core mechanism involves a smart contract function that calculates the optimal fee, often using a formula like fee = baseFee + (volatilityMultiplier * oracleVolatility). This function is called during every swap to determine the fee before executing the trade. Protocols like Uniswap V4 use hooks to implement this logic, allowing the fee to be updated per-pool based on custom logic. The key is ensuring the oracle data is secure, the calculation is gas-efficient, and the fee changes are bounded to prevent manipulation or extreme values that could deter liquidity.

DYNAMIC FEE AMMS

Common Pitfalls and Security Considerations

Implementing dynamic fee tiers in an Automated Market Maker (AMM) introduces complexity around state management, fee calculations, and security. This guide addresses frequent developer questions and critical considerations.

Dynamic fee logic that updates state before performing external calls is a common attack vector. For example, if your swap function calculates a fee based on pool utilization, updates a storage variable, and then transfers tokens to the user via a callback, an attacker can re-enter the function. The second execution will use the already-updated fee state, potentially allowing them to pay lower fees or manipulate the calculation.

Mitigation: Adhere to the Checks-Effects-Interactions pattern. Perform all state changes after external interactions. For dynamic fees, you can:

  • Calculate the fee using only the pre-swap pool state and msg.sender data.
  • Store the fee amount in a local memory variable.
  • Perform the token transfer/callback.
  • Then update any global fee tier or accumulator state variables using the local variable.
solidity
// UNSAFE: State update before external call
feeTier = calculateNewTier();
_transferTokens(to, amount); // Vulnerable to reentrancy

// SAFE: Effects after interactions
uint256 calculatedFee = calculateFee(poolStateBefore);
_transferTokens(to, amount);
accumulatedFees += calculatedFee; // State update after
conclusion-next-steps
IMPLEMENTATION GUIDE

Conclusion and Next Steps

You have now implemented the core logic for dynamic fee tiers in an AMM. This guide covered the essential smart contract architecture and economic parameters.

The primary benefit of a dynamic fee system is its ability to optimize for protocol revenue and liquidity provider (LP) yield based on real-time market conditions. Unlike static fee pools, your implementation can now automatically adjust fees in response to metrics like pool utilization, volatility, or arbitrage opportunities. This creates a more efficient market and can attract sophisticated LPs seeking higher risk-adjusted returns. The key is calibrating your FeeController logic to avoid excessive fee changes that could deter traders.

For production deployment, rigorous testing is non-negotiable. Beyond standard unit tests, you must simulate mainnet-like conditions. Use a forked mainnet environment (e.g., with Foundry's forge or Hardhat) to test fee adjustments under historical high-volatility events. Implement fuzz testing for your mathematical functions to ensure no overflow/underflow edge cases. Finally, consider a timelock or governance-controlled activation for the FeeController to allow for a community veto period before live parameter updates.

Looking ahead, you can extend this foundation. Consider integrating on-chain oracles like Chainlink for volatility data to inform fee decisions, moving beyond simple pool metrics. Explore tiered LP rewards where LPs in higher-fee (and higher-risk) tiers earn a proportionally greater share of the fees. The code structure also allows for EIP-7579 compatibility for modular fee hooks. For further reading, review the Uniswap V4 documentation on its hook architecture and research on dynamic fee papers from teams like Ambient Finance.