As in CenturionDEX v2, swaps in CenturionDEX v3 are governed locally by a constant-product rule. The essential difference is that, in CenturionDEX v3, the active liquidity is not constant over the entire price axis: it may change whenever the current price reaches an initialized tick.
Consequently, a swap must be executed interval by interval, where each interval is bounded by two consecutive initialized ticks. Within each such interval, the liquidity parameter is constant, so the trade is computed exactly as a constant-product step. If the requested trade would move the price beyond the current interval, only the maximal feasible part of the trade is executed there; then the boundary tick is crossed, the tick-dependent state variables are updated, and the remaining part of the trade continues in the next interval.
Let ϕ∈[0,1) denote the trading-fee rate, and let p be the current price of token X in units of token Y. Let ic be the current tick index, so that
ic=⌊log1.0001(p)⌋.
Let I⊂Z be the set of initialized tick indexes. Define
iℓ=max{i∈I∣i≤ic},iu=min{i∈I∣i>ic}.
The corresponding boundary prices are
pℓ=1.0001iℓ,pu=1.0001iu.
By construction, the current price lies in the active interval
p∈[pℓ,pu),
and there is no initialized tick strictly between iℓ and iu. Hence, the active liquidity is constant throughout this interval. Denote that local liquidity parameter by L.
Within the interval [pℓ,pu), the virtual reserves are
xv=pL,yv=Lp.
The corresponding real balances available inside the current interval are
xr=L(p1−pu1),yr=L(p−pℓ).
The maximal real balances attainable inside the same interval are
xmax=L(pℓ1−pu1),ymax=L(pu−pℓ).
The quantities xr and yr represent, respectively, the currently available in-range balances of tokens X and Y. The differences
xmax−xr=L(pℓ1−p1),ymax−yr=L(pu−p)
measure how much additional effective input of tokens X and Y, respectively, can still be absorbed before the price reaches one of the interval boundaries.
We now analyze the four canonical swap modes.
Case 1: exact output in token X
Suppose the trader wants to receive an amount Δxout>0 of token X. This trade moves the price upward.
If
Δxout≤xr,
then the entire trade can be executed inside the current interval. The new virtual balance of token X is
xv′=xv−Δxout,
and therefore the updated price is
p′=(xv−ΔxoutL)2.
The corresponding effective input in token Y is
Δyeff=yv′−yv=Lp′−Lp,
so the gross input paid by the trader is
Δyin=1−ϕΔyeff,
and the fee collected in token Y is
ϕΔyin.
If instead
Δxout>xr,
then the current interval does not contain enough token X to satisfy the full request. In that case, the maximal feasible first step uses exactly the amount xr, so the price moves all the way to the upper boundary pu. Indeed,
xv−xr=pL−L(p1−pu1)=puL.
Hence, after this partial execution, the updated price is precisely pu. The effective amount of token Y absorbed in this first step is
L(pu−p),
so the corresponding gross input is
1−ϕL(pu−p).
The remaining unsatisfied output request is
Δxout−xr.
At this point, the protocol crosses the upper initialized tick iu, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iu, and then continues the trade in the next interval to the right.
Case 2: exact input in token X
Suppose now that the trader sends a gross amount Δxin>0 of token X and wants to receive token Y. This trade moves the price downward.
Since the fee is charged on the input token, the effective amount that participates in the swap is
Δxeff=(1−ϕ)Δxin.
If
xr+Δxeff≤xmax,
equivalently,
Δxeff≤xmax−xr,
then the trade is completed within the current interval. The new virtual balance of token X is
xv′=xv+Δxeff,
and the updated price becomes
p′=(xv+ΔxeffL)2.
The output amount of token Y is then
Δyout=yv−yv′=Lp−Lp′=L(p−p′).
If instead
xr+Δxeff>xmax,
then the current interval can absorb only the effective amount
δxeff=xmax−xr=L(pℓ1−p1).
The corresponding gross input spent in this first step is
δxin=1−ϕδxeff,
and the fee collected is
ϕδxin=1−ϕϕδxeff.
After this partial execution, the virtual balance of token X becomes
xv+δxeff=pL+L(pℓ1−p1)=pℓL,
so the price reaches the lower boundary pℓ, as expected.
The remaining gross input that must still be processed is
Δxin−δxin=Δxin−1−ϕxmax−xr.
The protocol then crosses the lower initialized tick iℓ, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iℓ, and continues the remaining part of the trade in the next interval to the left.
Case 3: exact output in token Y
Suppose that the trader wants to receive an amount Δyout>0 of token Y. This trade moves the price downward.
If
Δyout≤yr,
then the entire trade can be executed inside the current interval. The new virtual balance of token Y is
yv′=yv−Δyout,
and therefore the updated price is
p′=(Lyv−Δyout)2.
The corresponding effective input in token X is
Δxeff=xv′−xv=p′L−pL,
so the gross input paid by the trader is
Δxin=1−ϕΔxeff,
and the fee collected in token X is
ϕΔxin.
If instead
Δyout>yr,
then the current interval does not contain enough token Y to satisfy the full request. The maximal feasible first step consumes exactly the amount yr, so the price moves to the lower boundary pℓ. Indeed,
yv−yr=Lp−L(p−pℓ)=Lpℓ.
Thus the updated price is precisely pℓ. The effective amount of token X required in this first step is
L(pℓ1−p1),
and the corresponding gross input is
1−ϕL(pℓ1−p1).
The remaining unsatisfied output request is
Δyout−yr.
The protocol then crosses the lower initialized tick iℓ, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iℓ, and continues the trade in the next interval to the left.
Case 4: exact input in token Y
Finally, suppose that the trader sends a gross amount Δyin>0 of token Y and wants to receive token X. This trade moves the price upward.
The effective amount that participates in the swap is
Δyeff=(1−ϕ)Δyin.
If
yr+Δyeff≤ymax,
equivalently,
Δyeff≤ymax−yr,
then the trade is completed within the current interval. The new virtual balance of token Y is
yv′=yv+Δyeff,
so the updated price becomes
p′=(Lyv+Δyeff)2.
The output amount of token X is
Δxout=xv−xv′=pL−p′L.
If instead
yr+Δyeff>ymax,
then the current interval can absorb only the effective amount
δyeff=ymax−yr=L(pu−p).
The corresponding gross input spent in this first step is
δyin=1−ϕδyeff,
and the fee collected is
ϕδyin=1−ϕϕδyeff.
After this partial execution, the virtual balance of token Y becomes
yv+δyeff=Lp+L(pu−p)=Lpu,
so the price reaches the upper boundary pu.
The remaining gross input that must still be processed is
Δyin−δyin=Δyin−1−ϕymax−yr.
The protocol then crosses the upper initialized tick iu, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iu, and continues the remaining part of the trade in the next interval to the right.
Interval-by-interval execution
The previous four cases describe one local step of the CenturionDEX v3 swap algorithm. In each case, one of two things happens:
the trade is completed inside the current interval [pℓ,pu); or
the trade consumes all liquidity available in the relevant direction inside that interval, reaches one of its boundaries, crosses the corresponding initialized tick, updates the tick-dependent state, and then continues in the adjacent interval.
Thus, a full trade is generally a concatenation of such local constant-product steps across consecutive initialized-tick intervals.
A final remark is important. In the discussion above, whenever the current interval is exhausted, we implicitly assume that there exists a next initialized tick in the direction of the trade. This is usually the relevant regime, but it need not always hold. If no further initialized tick exists in the required direction, then no liquidity is available beyond the boundary just reached. In that situation, the swap algorithm stops, and the order is only partially filled.
Worked Example
We now present a fully explicit example illustrating how the CenturionDEX v3 protocol operates at the level of ticks, active liquidity, fee growth, and position accounting.
Consider a CenturionDEX v3 pool with tokens
X=CTN,Y=USDC,
trading fee
ϕ=0.003,
and tick spacing equal to 1. Suppose the current price is
p0=2500USDC/CTN.
Assume that exactly two liquidity providers have opened positions.
The first provider chooses a price interval whose lower and upper tick indexes are
Since the current price p0=2500 lies inside the interval [t1,t3], this provider must deposit both tokens. Suppose that they choose to deposit 4 CTN. Then, from the in-range formula for the real balance of token X,
Since the current price p0=2500 satisfies p0<t2, this second position is opened below its active interval. Therefore, the provider needs to deposit only token X. Suppose they deposit 6 CTN. Then, from the below-range formula,
x=L(pa1−pb1),
we obtain
6=L2(t21−t41),
and thus
L2=2704.0471691−3600.00521216≈2340.142070.
Hence, the second provider opens the position
P2=[t2,t4],L2≈2340.142070,
with initial deposit
6 CTNand0 USDC.
At the initial price p0, we have
t1<p0<t2,
so the current active interval is [t1,t2], and only the first position is active there. Therefore, the active liquidity at the initial state is
Ltot=L1.
Trade 1
Suppose now that a trader wants to buy0.8 CTN from the pool. Since the trader is receiving token X, this is the exact-output-in-X case.
At the initial price p0=2500, the virtual reserves of the active interval [t1,t2] are
xv=p0L1=502199.638149≈43.992763,
and
yv=L1p0=2199.638149⋅50≈109981.907438.
The requested output amount is
Δxout(1)=0.8.
Since the input fee is charged on token Y, the required gross USDC input is given by the local exact-output formula
At this stage, all token-X fee-growth variables remain zero.
Trade 2
Suppose now that another trader deposits
4000 USDC
into the pool in order to buy CTN. This is the exact-input-in-Y case.
At price p1, the real balance of token Y available inside the current interval [t1,t2] is
yr=L1(p1−t1)≈13035.513079.
The maximum real balance of token Y that can be reached inside the same interval is
ymax=L1(t2−t1)≈15398.743781.
Since the fee is charged on the input token, the effective amount of USDC that would participate in the swap if the trade were executed entirely in the current interval is
Hence, even after including fees, the second provider remains below the hold benchmark by approximately
16448.380622−16445.136435=3.244187 USDC.
Final interpretation
This worked example illustrates four distinct mechanisms of the CenturionDEX v3 protocol.
First, liquidity is organized interval by interval. Initially, only the first position is active because the current price lies in [t1,t2). After Trade 2 crosses tick t2, the second position becomes active and the local liquidity jumps from L1 to L1+L2.
Second, fees are accumulated globally per unit liquidity and are then decomposed relative to initialized ticks through the outside-fee variables. In this example, because both trades were CTN-buying trades paid in USDC, all fees were collected in token Y, and tick i2 played the key role in separating the fee share of the second provider from the fee share accrued before their range became active.
Third, the two providers experience very different economic outcomes. The first provider, whose position was active from the beginning, captured the majority of the fees, but also experienced a materially larger impermanent loss. The second provider earned fewer fees, yet their impermanent loss remained small because the final price stayed close to the lower boundary of their active range.
Finally, the example shows that fee income and impermanent loss are distinct phenomena. The fees generated by trading may partially offset the mark-to-market divergence between the LP position and the passive-hold benchmark, but they do not automatically eliminate it.