Algebra Integral
HomepageSocialsIntegrate
  • Overview
    • What is Algebra?
    • Who Are These Docs For
    • Why Concentrated Liquidity & Modularity Matter
    • Partners & Ecosystem
    • Audits & Security
    • Social Media & Communities
  • Introducing Algebra Integral to Founders & Business Teams
    • Overview of Algebra Integral
      • How It Works: Core + Plugins
      • V3 vs. V4: Key Differences
      • Integral vs. Uniswap V4: Key Differences
    • Benefits of Modular Architecture
      • Perks for DEXes
      • Perks for Builders
      • Perks for Users
  • Modularity: Use Cases
  • Plugin Marketplace
  • Algebra Partner Support
  • User Guide Template For DEXes
    • Concentrated Liquidity & Modular Architecture Basics
      • Glossary
      • How Concentrated Liquidity & Modular Architecture Work
      • Benefits of Modular Concentrated Liquidity AMM for Users
        • Perks for Liquidity Providers
        • Perks for Projects
        • Perks for Traders
      • Fee Mechanics
        • Static Fee
        • Dynamic Fee
        • Sliding Fee
        • Dynamic Fee Based on Trading Volume
        • Managed Swap Fee
        • Whitelist Fee Discount
      • Farming
      • Farming FAQ
  • Price Ranges and Liquidity Strategies
    • What Are Price Ranges
    • Basic Price Range Presets
    • Advanced Range Presets
    • How Price Moves Affect Liquidity
    • Impermanent Loss: Concepts & Mitigation
    • Matching Your Liquidity Strategy to Market Moves
    • Swap & LP Strategies with Price Ranges
    • Liquidity Scenarios & Risk Profiles
  • Liquidity Provisioning: Tutorials & FAQs
    • Adding Liquidity
      • Manual Mode
      • Automated Mode
    • Managing & Adjusting Positions
    • How APR is Calculated
    • FAQ for LPs
  • Algebra Integral / Technical Reference
    • Intro
    • Audits
    • Integration Process
      • Specification and API of contracts
        • Algebra Pool
        • Algebra Factory
        • Swap Router
        • Nonfungible Position Manager
        • Quoter
        • QuoterV2
        • TickLens
      • Interaction with pools
        • Getting data from pools
      • Subgraphs and analytics
        • Examples of queries
      • Technical Guides
        • Intro
        • Swaps
          • Single swaps
          • Multihop swaps
        • Providing liquidity
          • Setting up your contract
          • Mint a new position
          • Collect fees
          • Decrease liquidity
          • Increase liquidity
          • Final Contract
        • Flashloans
          • Setting up your contract
          • Calling flash
          • Flash callback
          • Final contract
      • Migration from UniswapV3
      • FAQ
    • Core Logic
      • Pool overview
      • Swap calculation
      • Liquidity and positions
      • Ticks
        • Ticks search tree
      • Reserves
      • Flash
      • Plugins
      • AlgebraFactory and roles
    • Plugins
      • Overview
      • Farming
      • Adaptive Fee
      • Sliding Fee
      • Whitelist Discount Fee
      • Safety Switch
      • Position Limit Orders
      • Managed Swap Fee
      • FAQ
    • Guides
      • Plugin Development
      • Plugin Testing
      • Plugin Deployment
    • Changes V1
    • Changes V1.1
    • Changes v1.2
  • Changes v1.2.1
  • Other
    • Archived Documentation
Powered by GitBook
On this page
  • Ticks
  • Determination of fee increment within the range of ticks
  1. Algebra Integral / Technical Reference
  2. Core Logic

Ticks

PreviousLiquidity and positionsNextTicks search tree

Relevant and important files:

  • base/TickStructure.sol

Ticks

The entire price space is divided into sections using special cut-offs called ticks.

The ticks are distributed logarithmically: the tick with index i corresponds to the price:

P(ti)=1.0001iP(t_i) = 1.0001^iP(ti​)=1.0001i

Inside the segment defined by two neighbouring ticks (often this segment is also called a tick), the AMM behaves like a regular CPF-AMM (like UniV2). When the price crosses a tick, the value of active liquidity may change (if the boundary of someone's position is crossed).

For this reason, when making swaps, you should be able to find the next active tick (a tick that is the boundary of a position).

Doubly linked tick list

To simplify navigation through ticks during swaps and for other purposes, Algebra Integral organises ticks as a doubly linked list: each tick stores pointers (indices) of the next and previous active ticks.

The list always contains the minimum and maximum possible ticks (as boundary values). Therefore, the list is never empty.

The pool always stores information about which ticks are currently the next and previous active ticks, so when swapping, it is easy to get information about which tick a particular iteration of the swap is up to. In each tick, the indices of the previous and next active ticks are stored in the same storage slot along with the liquidity delta, which makes it cheaper to navigate through the ticks when swapping.

However, when adding liquidity, it is necessary to have access to an arbitrary section of the doubly linked list (to insert a new tick into the list). For this purpose, Algebra Integral implements a ticks search tree.

Determination of fee increment within the range of ticks

Ticks play an important role in allocating the commission between liquidity positions. The accumulator values described in the article on liquidity and positions are used for this purpose:

totalFeeGrowthToken0=∑Famountx/LttotalFeeGrowthToken0 = \sum F^x_{amount} / L_ttotalFeeGrowthToken0=∑Famountx​/Lt​

totalFeeGrowthToken1=∑Famounty/LttotalFeeGrowthToken1 = \sum F^y_{amount} / L_ttotalFeeGrowthToken1=∑Famounty​/Lt​

Two additional values are stored in each tick that correspond to the accumulator increments "outside" the ticks: outerFeeGrowth0Token, outerFeeGrowth1Token.

These values have a relative character and are set at the moment of tick initialisation according to the following rule: it is presumed that the entire "increment" of the commission accumulator occurred below this tick. For this reason, the values are initialised as follows:

If the tick to be initialised is less than or equal to the global tick at the moment(tick≤currentTick)(tick \le currentTick)(tick≤currentTick):

outerFeeGrowth0Token=totalFeeGrowthToken0outerFeeGrowth0Token = totalFeeGrowthToken0outerFeeGrowth0Token=totalFeeGrowthToken0

outerFeeGrowth1Token=totalFeeGrowthToken1outerFeeGrowth1Token = totalFeeGrowthToken1outerFeeGrowth1Token=totalFeeGrowthToken1

On the other hand, if the tick to be initialised is above the current global tick (tick>currentTick)(tick \gt currentTick)(tick>currentTick), then the corresponding values are initialised to zero:

outerFeeGrowth0Token=0outerFeeGrowth0Token = 0outerFeeGrowth0Token=0

outerFeeGrowth1Token=0outerFeeGrowth1Token = 0outerFeeGrowth1Token=0

Later on, at each tick crossing these values are updated according to the following rule:

outerFeeGrowth0Tokennew=totalFeeGrowthToken0−outerFeeGrowth0TokenoldouterFeeGrowth0Token_{new} = totalFeeGrowthToken0 - outerFeeGrowth0Token_{old}outerFeeGrowth0Tokennew​=totalFeeGrowthToken0−outerFeeGrowth0Tokenold​

outerFeeGrowth1Tokennew=totalFeeGrowthToken1−outerFeeGrowth1TokenoldouterFeeGrowth1Token_{new} = totalFeeGrowthToken1 - outerFeeGrowth1Token_{old}outerFeeGrowth1Tokennew​=totalFeeGrowthToken1−outerFeeGrowth1Tokenold​

This ensures that, knowing the current global tick, it is possible at any time to determine what token increment has occurred "on the other side" since the tick was initialised:

At the same time, the pool possesses the accumulator values totalFeeGrowthToken0 and totalFeeGrowthToken1, which contain the total commission increment for the entire time of the pool's existence.

Due to these values, it is easy to calculate the value of the commission increment that occurred within a given range of ticks after their initialisation.

If tickK≤currentTick<tickNtick_K \le currentTick \lt tick_NtickK​≤currentTick<tickN​:

If tickK<tickN≤currentTicktick_K \lt tick_N \le currentTicktickK​<tickN​≤currentTick:

innerFeeGrowthK,N=outerFeeGrowthN−outerFeeGrowthKinnerFeeGrowth_{K, N} = outerFeeGrowth_N - outerFeeGrowth_KinnerFeeGrowthK,N​=outerFeeGrowthN​−outerFeeGrowthK​

If currentTick<tickK<tickNcurrentTick \lt tick_K \lt tick_NcurrentTick<tickK​<tickN​:

innerFeeGrowthK,N=outerFeeGrowthK−outerFeeGrowthNinnerFeeGrowth_{K, N} = outerFeeGrowth_K - outerFeeGrowth_NinnerFeeGrowthK,N​=outerFeeGrowthK​−outerFeeGrowthN​

Thus, using the above formulas for innerFeeGrowth it is possible to know the accumulator increment ∑Famountx,y/Lt\sum F^{x, y}_{amount} / L_t∑Famountx,y​/Lt​ within the range specified by any two active ticks at any time. Distribution of commission among liquidity positions is performed by tracking the change of innerFeeGrowth for a liquidity position:

Δfeeposition=ΔLposition∗ΔinnerFeeFrowthposition\Delta fee_{position} = \Delta L_{position} * \Delta innerFeeFrowth _{position}Δfeeposition​=ΔLposition​∗ΔinnerFeeFrowthposition​

Note: a similar mechanism can be used in plugins to implement time tracking within a range of ticks, distribute additional rewards, etc.

outerFeeGrowth - commission accumulator increment "on the other side" from tick N
Relevant and important files:
innerFeeGrowth - increment of accumulator fee / L from the moment of ticks initialisation