TONCO Docs
Документация и Гайды
Документация и Гайды
  • TONCO
    • Что такое TONCO?
    • Концентрированная ликвидность впервые на TON
    • Команда
    • Социальные сети
    • Аудиты
    • Roadmap
  • Что такое концентрированная ликвидность?
    • Глоссарий
    • Fees
    • NFT-токены поставщиков ликвидности
    • Фарминг (Farming)
    • Farming FAQ
  • Ценовые диапазоны
    • Значение ценовых диапазонов
    • Предустановленные диапазоны
    • Продвинутые пресеты диапазонов
    • Движение цены в пределах диапазонов
    • Непостоянные потери (Impermanent Loss)
    • Выбор стратегии
    • Сценарии при управлении ликвидностью
  • Преимущества концентрированной ликвидности
    • Преимущества для поставщиков ликвидности
    • Преимущества для трейдеров
    • Преимущества для проектов
    • Токены ликвидного стейкинга (LST)
  • Стратегии Концентрированной Ликвидности
    • Базовые стратегии
    • Хэджирование с EVAA (lending)
    • Хэджирование с Tradoor (perps)
    • Хэджирование со Storm Trade (perps)
  • Руководство по предоставлению ликвидности
    • Добавление ликвидности
    • Управление позицией
    • Руководство по миграции ликвидности
    • Как рассчитывается APR
    • LPs FAQ
  • TONCO Points Program
    • Introduction
    • How to Earn Points
  • Ambassador Program
    • Introduction
    • Content Creators
    • Community Moderators
    • International Ambassadors
  • Technical Reference
    • 📇Indexer
    • GraphQL Schema
    • Integration FAQ
    • Core Logic
      • 🧺Pool overview
      • Swap calculation
      • 💰Liquidity and positions
      • 📏Ticks
      • 🏦Reserves
    • Contracts
      • 📜Scenarios
      • Pool
      • Router
      • Position NFT
      • Account
Powered by GitBook
On this page
  • Ticks
  • Determination of fee increment within the range of ticks
  1. Technical Reference
  2. Core Logic

Ticks

PreviousLiquidity and positionsNextReserves

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 neighboring 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).

Storing ticks in TON Contract

To simplify navigation through ticks during swaps and for other purposes, active ticks are organized in a dict() with 24-bit signed ints used as keys.

Because dict() is internally stored as a tree pool always has quick access to the 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.

Determination of fee increment within the range of ticks

totalFeeGrowthJetton0=∑Famountx/LttotalFeeGrowthJetton0 = \sum F^x_{amount} / L_ttotalFeeGrowthJetton0=∑Famountx​/Lt​

totalFeeGrowthJetton1=∑Famounty/LttotalFeeGrowthJetton1 = \sum F^y_{amount} / L_ttotalFeeGrowthJetton1=∑Famounty​/Lt​

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

These values have a relative character and are set at the moment of tick initialization 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 initialized as follows:

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

outerFeeGrowth0Jetton=totalFeeGrowthJetton0outerFeeGrowth0Jetton = totalFeeGrowthJetton0outerFeeGrowth0Jetton=totalFeeGrowthJetton0

outerFeeGrowth1Jetton=totalFeeGrowthJetton1outerFeeGrowth1Jetton = totalFeeGrowthJetton1outerFeeGrowth1Jetton=totalFeeGrowthJetton1

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

outerFeeGrowth0Jetton=0outerFeeGrowth0Jetton = 0outerFeeGrowth0Jetton=0

outerFeeGrowth1Jetton=0outerFeeGrowth1Jetton = 0outerFeeGrowth1Jetton=0

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

outerFeeGrowth0Jettonnew=totalFeeGrowthJetton0−outerFeeGrowth0JettonoldouterFeeGrowth0Jetton_{new} = totalFeeGrowthJetton0 - outerFeeGrowth0Jetton_{old}outerFeeGrowth0Jettonnew​=totalFeeGrowthJetton0−outerFeeGrowth0Jettonold​

outerFeeGrowth1Jettonnew=totalFeeGrowthJetton1−outerFeeGrowth1JettonoldouterFeeGrowth1Jetton_{new} = totalFeeGrowthJetton1 - outerFeeGrowth1Jetton_{old}outerFeeGrowth1Jettonnew​=totalFeeGrowthJetton1−outerFeeGrowth1Jettonold​

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

At the same time, the pool possesses the accumulator values totalFeeGrowthJetton0 and totalFeeGrowthJetton1, which contains 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 initialization.

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​

The corresponding bit at the root is 1 if the corresponding second-level word has at least one active bit.

The maximum allowed tick is 887272

The minimum allowed tick is -887272.

Thus, there can be (887272 + 887272 + 1).

.

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

📏
the article on liquidity and positions
outerFeeGrowth - commission accumulator increment "on the other side" from tick N
totalFeeGrowth - total fee / L growth for the entire pool lifetime
innerFeeGrowth - increment of accumulator fee / L from the moment of ticks initialisation