CenturionDEX

Trades

Last modified:

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)\phi \in [0,1) denote the trading-fee rate, and let pp be the current price of token XX in units of token YY. Let ici_c be the current tick index, so that

ic=log1.0001(p).i_c=\left\lfloor \log_{1.0001}(p)\right\rfloor.

Let IZ\mathcal I \subset \mathbb Z be the set of initialized tick indexes. Define

i=max{iIiic},iu=min{iIi>ic}.i_\ell=\max\{i\in\mathcal I \mid i\le i_c\}, \qquad i_u=\min\{i\in\mathcal I \mid i> i_c\}.

The corresponding boundary prices are

p=1.0001i,pu=1.0001iu.p_\ell = 1.0001^{i_\ell}, \qquad p_u = 1.0001^{i_u}.

By construction, the current price lies in the active interval

p[p,pu),p \in [p_\ell,p_u),

and there is no initialized tick strictly between ii_\ell and iui_u. Hence, the active liquidity is constant throughout this interval. Denote that local liquidity parameter by LL.

Within the interval [p,pu)[p_\ell,p_u), the virtual reserves are

xv=Lp,yv=Lp.x_v=\frac{L}{\sqrt{p}}, \qquad y_v=L\sqrt{p}.

The corresponding real balances available inside the current interval are

xr=L(1p1pu),yr=L(pp).x_r=L\left(\frac{1}{\sqrt{p}}-\frac{1}{\sqrt{p_u}}\right), \qquad y_r=L\left(\sqrt{p}-\sqrt{p_\ell}\right).

The maximal real balances attainable inside the same interval are

xmax=L(1p1pu),ymax=L(pup).x_{\max}=L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p_u}}\right), \qquad y_{\max}=L\left(\sqrt{p_u}-\sqrt{p_\ell}\right).

The quantities xrx_r and yry_r represent, respectively, the currently available in-range balances of tokens XX and YY. The differences

xmaxxr=L(1p1p),ymaxyr=L(pup)x_{\max}-x_r=L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p}}\right), \qquad y_{\max}-y_r=L\left(\sqrt{p_u}-\sqrt{p}\right)

measure how much additional effective input of tokens XX and YY, 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 XX

Suppose the trader wants to receive an amount Δxout>0\Delta x_{\mathrm{out}}>0 of token XX. This trade moves the price upward.

If

Δxoutxr,\Delta x_{\mathrm{out}} \le x_r,

then the entire trade can be executed inside the current interval. The new virtual balance of token XX is

xv=xvΔxout,x_v' = x_v - \Delta x_{\mathrm{out}},

and therefore the updated price is

p=(LxvΔxout)2.p'=\left(\frac{L}{x_v-\Delta x_{\mathrm{out}}}\right)^2.

The corresponding effective input in token YY is

Δyeff=yvyv=LpLp,\Delta y_{\mathrm{eff}} = y_v'-y_v = L\sqrt{p'}-L\sqrt{p},

so the gross input paid by the trader is

Δyin=Δyeff1ϕ,\Delta y_{\mathrm{in}}=\frac{\Delta y_{\mathrm{eff}}}{1-\phi},

and the fee collected in token YY is

ϕΔyin.\phi\,\Delta y_{\mathrm{in}}.

If instead

Δxout>xr,\Delta x_{\mathrm{out}} > x_r,

then the current interval does not contain enough token XX to satisfy the full request. In that case, the maximal feasible first step uses exactly the amount xrx_r, so the price moves all the way to the upper boundary pup_u. Indeed,

xvxr=LpL(1p1pu)=Lpu.x_v-x_r =\frac{L}{\sqrt{p}}-L\left(\frac{1}{\sqrt{p}}-\frac{1}{\sqrt{p_u}}\right) =\frac{L}{\sqrt{p_u}}.

Hence, after this partial execution, the updated price is precisely pup_u. The effective amount of token YY absorbed in this first step is

L(pup),L\left(\sqrt{p_u}-\sqrt{p}\right),

so the corresponding gross input is

L(pup)1ϕ.\frac{L\left(\sqrt{p_u}-\sqrt{p}\right)}{1-\phi}.

The remaining unsatisfied output request is

Δxoutxr.\Delta x_{\mathrm{out}}-x_r.

At this point, the protocol crosses the upper initialized tick iui_u, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iui_u, and then continues the trade in the next interval to the right.


Case 2: exact input in token XX

Suppose now that the trader sends a gross amount Δxin>0\Delta x_{\mathrm{in}}>0 of token XX and wants to receive token YY. 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.\Delta x_{\mathrm{eff}}=(1-\phi)\Delta x_{\mathrm{in}}.

If

xr+Δxeffxmax,x_r+\Delta x_{\mathrm{eff}} \le x_{\max},

equivalently,

Δxeffxmaxxr,\Delta x_{\mathrm{eff}} \le x_{\max}-x_r,

then the trade is completed within the current interval. The new virtual balance of token XX is

xv=xv+Δxeff,x_v' = x_v + \Delta x_{\mathrm{eff}},

and the updated price becomes

p=(Lxv+Δxeff)2.p'=\left(\frac{L}{x_v+\Delta x_{\mathrm{eff}}}\right)^2.

The output amount of token YY is then

Δyout=yvyv=LpLp=L(pp).\Delta y_{\mathrm{out}} = y_v-y_v' = L\sqrt{p}-L\sqrt{p'} = L\left(\sqrt{p}-\sqrt{p'}\right).

If instead

xr+Δxeff>xmax,x_r+\Delta x_{\mathrm{eff}} > x_{\max},

then the current interval can absorb only the effective amount

δxeff=xmaxxr=L(1p1p).\delta x_{\mathrm{eff}} = x_{\max}-x_r = L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p}}\right).

The corresponding gross input spent in this first step is

δxin=δxeff1ϕ,\delta x_{\mathrm{in}}=\frac{\delta x_{\mathrm{eff}}}{1-\phi},

and the fee collected is

ϕδxin=ϕ1ϕδxeff.\phi\,\delta x_{\mathrm{in}} = \frac{\phi}{1-\phi}\,\delta x_{\mathrm{eff}}.

After this partial execution, the virtual balance of token XX becomes

xv+δxeff=Lp+L(1p1p)=Lp,x_v+\delta x_{\mathrm{eff}} =\frac{L}{\sqrt{p}}+L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p}}\right) =\frac{L}{\sqrt{p_\ell}},

so the price reaches the lower boundary pp_\ell, as expected.

The remaining gross input that must still be processed is

Δxinδxin=Δxinxmaxxr1ϕ.\Delta x_{\mathrm{in}}-\delta x_{\mathrm{in}} = \Delta x_{\mathrm{in}}-\frac{x_{\max}-x_r}{1-\phi}.

The protocol then crosses the lower initialized tick ii_\ell, updates the current tick index, the total active liquidity, and the fee-outside variables associated with ii_\ell, and continues the remaining part of the trade in the next interval to the left.


Case 3: exact output in token YY

Suppose that the trader wants to receive an amount Δyout>0\Delta y_{\mathrm{out}}>0 of token YY. This trade moves the price downward.

If

Δyoutyr,\Delta y_{\mathrm{out}} \le y_r,

then the entire trade can be executed inside the current interval. The new virtual balance of token YY is

yv=yvΔyout,y_v' = y_v - \Delta y_{\mathrm{out}},

and therefore the updated price is

p=(yvΔyoutL)2.p'=\left(\frac{y_v-\Delta y_{\mathrm{out}}}{L}\right)^2.

The corresponding effective input in token XX is

Δxeff=xvxv=LpLp,\Delta x_{\mathrm{eff}} = x_v'-x_v = \frac{L}{\sqrt{p'}}-\frac{L}{\sqrt{p}},

so the gross input paid by the trader is

Δxin=Δxeff1ϕ,\Delta x_{\mathrm{in}}=\frac{\Delta x_{\mathrm{eff}}}{1-\phi},

and the fee collected in token XX is

ϕΔxin.\phi\,\Delta x_{\mathrm{in}}.

If instead

Δyout>yr,\Delta y_{\mathrm{out}} > y_r,

then the current interval does not contain enough token YY to satisfy the full request. The maximal feasible first step consumes exactly the amount yry_r, so the price moves to the lower boundary pp_\ell. Indeed,

yvyr=LpL(pp)=Lp.y_v-y_r =L\sqrt{p}-L\left(\sqrt{p}-\sqrt{p_\ell}\right) =L\sqrt{p_\ell}.

Thus the updated price is precisely pp_\ell. The effective amount of token XX required in this first step is

L(1p1p),L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p}}\right),

and the corresponding gross input is

L(1p1p)1ϕ.\frac{L\left(\frac{1}{\sqrt{p_\ell}}-\frac{1}{\sqrt{p}}\right)}{1-\phi}.

The remaining unsatisfied output request is

Δyoutyr.\Delta y_{\mathrm{out}}-y_r.

The protocol then crosses the lower initialized tick ii_\ell, updates the current tick index, the total active liquidity, and the fee-outside variables associated with ii_\ell, and continues the trade in the next interval to the left.


Case 4: exact input in token YY

Finally, suppose that the trader sends a gross amount Δyin>0\Delta y_{\mathrm{in}}>0 of token YY and wants to receive token XX. This trade moves the price upward.

The effective amount that participates in the swap is

Δyeff=(1ϕ)Δyin.\Delta y_{\mathrm{eff}}=(1-\phi)\Delta y_{\mathrm{in}}.

If

yr+Δyeffymax,y_r+\Delta y_{\mathrm{eff}} \le y_{\max},

equivalently,

Δyeffymaxyr,\Delta y_{\mathrm{eff}} \le y_{\max}-y_r,

then the trade is completed within the current interval. The new virtual balance of token YY is

yv=yv+Δyeff,y_v' = y_v + \Delta y_{\mathrm{eff}},

so the updated price becomes

p=(yv+ΔyeffL)2.p'=\left(\frac{y_v+\Delta y_{\mathrm{eff}}}{L}\right)^2.

The output amount of token XX is

Δxout=xvxv=LpLp.\Delta x_{\mathrm{out}} = x_v-x_v' = \frac{L}{\sqrt{p}}-\frac{L}{\sqrt{p'}}.

If instead

yr+Δyeff>ymax,y_r+\Delta y_{\mathrm{eff}} > y_{\max},

then the current interval can absorb only the effective amount

δyeff=ymaxyr=L(pup).\delta y_{\mathrm{eff}} = y_{\max}-y_r = L\left(\sqrt{p_u}-\sqrt{p}\right).

The corresponding gross input spent in this first step is

δyin=δyeff1ϕ,\delta y_{\mathrm{in}}=\frac{\delta y_{\mathrm{eff}}}{1-\phi},

and the fee collected is

ϕδyin=ϕ1ϕδyeff.\phi\,\delta y_{\mathrm{in}} = \frac{\phi}{1-\phi}\,\delta y_{\mathrm{eff}}.

After this partial execution, the virtual balance of token YY becomes

yv+δyeff=Lp+L(pup)=Lpu,y_v+\delta y_{\mathrm{eff}} =L\sqrt{p}+L\left(\sqrt{p_u}-\sqrt{p}\right) =L\sqrt{p_u},

so the price reaches the upper boundary pup_u.

The remaining gross input that must still be processed is

Δyinδyin=Δyinymaxyr1ϕ.\Delta y_{\mathrm{in}}-\delta y_{\mathrm{in}} = \Delta y_{\mathrm{in}}-\frac{y_{\max}-y_r}{1-\phi}.

The protocol then crosses the upper initialized tick iui_u, updates the current tick index, the total active liquidity, and the fee-outside variables associated with iui_u, 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:

  1. the trade is completed inside the current interval [p,pu)[p_\ell,p_u); or
  2. 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,X=\mathrm{CTN}, \qquad Y=\mathrm{USDC},

trading fee

ϕ=0.003,\phi=0.003,

and tick spacing equal to 11. Suppose the current price is

p0=2500USDC/CTN.p_0=2500 \quad \text{USDC/CTN}.

Assume that exactly two liquidity providers have opened positions.

The first provider chooses a price interval whose lower and upper tick indexes are

i1=76137,i3=80151,i_1=76137, \qquad i_3=80151,

so the corresponding tick prices are

t1=1.0001761372024.988581,t3=1.0001801513025.099528.t_1 = 1.0001^{76137}\approx 2024.988581, \qquad t_3 = 1.0001^{80151}\approx 3025.099528.

Since the current price p0=2500p_0=2500 lies inside the interval [t1,t3][t_1,t_3], this provider must deposit both tokens. Suppose that they choose to deposit 44 CTN. Then, from the in-range formula for the real balance of token XX,

x(p)=L(1p1pb),x(p)=L\left(\frac{1}{\sqrt{p}}-\frac{1}{\sqrt{p_b}}\right),

we obtain

4=L1(125001t3),4 = L_1\left(\frac{1}{\sqrt{2500}}-\frac{1}{\sqrt{t_3}}\right),

and therefore

L1=415013025.0995282199.638149.L_1 = \frac{4}{\dfrac{1}{50}-\dfrac{1}{\sqrt{3025.099528}}} \approx 2199.638149.

The corresponding USDC deposit is

y1=L1(2500t1)2199.638149(502024.988581)10998.469836.y_1 = L_1\left(\sqrt{2500}-\sqrt{t_1}\right) \approx 2199.638149\,(50-\sqrt{2024.988581}) \approx 10998.469836.

Hence, the first provider opens the position

P1=[t1,t3],L12199.638149,P_1=[t_1,t_3], \qquad L_1\approx 2199.638149,

with initial deposit

4 CTNand10998.469836 USDC.4 \text{ CTN} \qquad\text{and}\qquad 10998.469836 \text{ USDC}.

Now consider a second provider. Let their lower and upper tick indexes be

i2=79029,i4=81891,i_2=79029, \qquad i_4=81891,

so that

t2=1.0001790292704.047169,t4=1.0001818913600.005212.t_2 = 1.0001^{79029}\approx 2704.047169, \qquad t_4 = 1.0001^{81891}\approx 3600.005212.

Since the current price p0=2500p_0=2500 satisfies p0<t2p_0<t_2, this second position is opened below its active interval. Therefore, the provider needs to deposit only token XX. Suppose they deposit 66 CTN. Then, from the below-range formula,

x=L(1pa1pb),x=L\left(\frac{1}{\sqrt{p_a}}-\frac{1}{\sqrt{p_b}}\right),

we obtain

6=L2(1t21t4),6 = L_2\left(\frac{1}{\sqrt{t_2}}-\frac{1}{\sqrt{t_4}}\right),

and thus

L2=612704.04716913600.0052122340.142070.L_2 = \frac{6}{\dfrac{1}{\sqrt{2704.047169}}-\dfrac{1}{\sqrt{3600.005212}}} \approx 2340.142070.

Hence, the second provider opens the position

P2=[t2,t4],L22340.142070,P_2=[t_2,t_4], \qquad L_2\approx 2340.142070,

with initial deposit

6 CTNand0 USDC.6 \text{ CTN} \qquad\text{and}\qquad 0 \text{ USDC}.

At the initial price p0p_0, we have

t1<p0<t2,t_1 < p_0 < t_2,

so the current active interval is [t1,t2][t_1,t_2], and only the first position is active there. Therefore, the active liquidity at the initial state is

Ltot=L1.L_{\mathrm{tot}}=L_1.

Trade 1

Suppose now that a trader wants to buy 0.80.8 CTN from the pool. Since the trader is receiving token XX, this is the exact-output-in-XX case.

At the initial price p0=2500p_0=2500, the virtual reserves of the active interval [t1,t2][t_1,t_2] are

xv=L1p0=2199.6381495043.992763,x_v=\frac{L_1}{\sqrt{p_0}} =\frac{2199.638149}{50} \approx 43.992763,

and

yv=L1p0=2199.63814950109981.907438.y_v=L_1\sqrt{p_0} =2199.638149\cdot 50 \approx 109981.907438.

The requested output amount is

Δxout(1)=0.8.\Delta x_{\mathrm{out}}^{(1)}=0.8.

Since the input fee is charged on token YY, the required gross USDC input is given by the local exact-output formula

Δyin(1)=Δxout(1)yv(1ϕ)(xvΔxout(1)).\Delta y_{\mathrm{in}}^{(1)} = \frac{\Delta x_{\mathrm{out}}^{(1)}\,y_v} {(1-\phi)\bigl(x_v-\Delta x_{\mathrm{out}}^{(1)}\bigr)}.

Substituting the numerical values, we obtain

Δyin(1)=0.8109981.9074380.997(43.9927630.8)2043.172761.\Delta y_{\mathrm{in}}^{(1)} = \frac{0.8\cdot 109981.907438} {0.997\,(43.992763-0.8)} \approx 2043.172761.

The trading fee charged in this swap is therefore

ϕΔyin(1)=0.0032043.1727616.129518.\phi\,\Delta y_{\mathrm{in}}^{(1)} = 0.003\cdot 2043.172761 \approx 6.129518.

After the trade, the virtual balance of token XX becomes

xv=xvΔxout(1)43.9927630.8=43.192763,x_v' = x_v-\Delta x_{\mathrm{out}}^{(1)} \approx 43.992763-0.8 = 43.192763,

and hence the updated price is

p1=(L1xv)2=(2199.63814943.192763)22593.465733.p_1 = \left(\frac{L_1}{x_v'}\right)^2 = \left(\frac{2199.638149}{43.192763}\right)^2 \approx 2593.465733.

Thus, after Trade 1, the current price is

p12593.465733,p_1\approx 2593.465733,

which still lies inside the interval [t1,t2][t_1,t_2].

Because only token-YY fees were generated, the global fee-growth variable in token YY becomes

fgY=6.129518L16.1295182199.6381490.002786603.f_g^Y = \frac{6.129518}{L_1} \approx \frac{6.129518}{2199.638149} \approx 0.002786603.

At this stage, all token-XX fee-growth variables remain zero.


Trade 2

Suppose now that another trader deposits

4000 USDC4000 \text{ USDC}

into the pool in order to buy CTN. This is the exact-input-in-YY case.

At price p1p_1, the real balance of token YY available inside the current interval [t1,t2][t_1,t_2] is

yr=L1(p1t1)13035.513079.y_r = L_1\left(\sqrt{p_1}-\sqrt{t_1}\right) \approx 13035.513079.

The maximum real balance of token YY that can be reached inside the same interval is

ymax=L1(t2t1)15398.743781.y_{\max} = L_1\left(\sqrt{t_2}-\sqrt{t_1}\right) \approx 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

(1ϕ)4000=0.9974000=3988.(1-\phi)\cdot 4000 = 0.997\cdot 4000 = 3988.

Therefore,

yr+(1ϕ)400013035.513079+3988=17023.513079>ymax=15398.743781.y_r + (1-\phi)\cdot 4000 \approx 13035.513079 + 3988 = 17023.513079 > y_{\max}=15398.743781.

So the trade cannot be completed entirely inside the interval [t1,t2][t_1,t_2].


First step of Trade 2: from p1p_1 to t2t_2

The largest effective amount of USDC that the current interval can absorb is

ymaxyr15398.74378113035.513079=2363.230702.y_{\max}-y_r \approx 15398.743781 - 13035.513079 = 2363.230702.

Hence, the gross USDC amount used in the first step is

b1=ymaxyr1ϕ=2363.2307020.9972370.341727.b_1 = \frac{y_{\max}-y_r}{1-\phi} = \frac{2363.230702}{0.997} \approx 2370.341727.

The fee charged in this first step is

0.003b10.0032370.3417277.111025.0.003\,b_1 \approx 0.003\cdot 2370.341727 \approx 7.111025.

The corresponding CTN output of this first step is

a1=L1(1p11t2)0.892398.a_1 = L_1\left(\frac{1}{\sqrt{p_1}}-\frac{1}{\sqrt{t_2}}\right) \approx 0.892398.

As expected, after this first step the price reaches the upper boundary of the current interval:

p=t22704.047169.p=t_2\approx 2704.047169.

At that moment, the protocol crosses the initialized tick i2i_2. Since position P2P_2 begins at t2t_2, the active liquidity is updated from

Ltot=L1L_{\mathrm{tot}}=L_1

to

Ltot=L1+L24539.780218.L_{\mathrm{tot}}=L_1+L_2 \approx 4539.780218.

Also, because tick i2i_2 has just been crossed from left to right, the outside-fee variable at that tick is updated to the current global fee growth:

foY(i2)fgY.f_o^Y(i_2)\leftarrow f_g^Y.

Before the crossing, the global fee growth is

fgY=6.129518+7.111025L10.006019419.f_g^Y = \frac{6.129518+7.111025}{L_1} \approx 0.006019419.

Therefore,

foY(i2)0.006019419.f_o^Y(i_2)\approx 0.006019419.
Second step of Trade 2: from t2t_2 to p3p_3

After the first step, the remaining gross USDC input is

b2=4000b140002370.341727=1629.658273.b_2 = 4000-b_1 \approx 4000-2370.341727 = 1629.658273.

Its effective amount is

(1ϕ)b2=0.9971629.6582731624.769298.(1-\phi)b_2 = 0.997\cdot 1629.658273 \approx 1624.769298.

Now the trade continues in the interval [t2,t3][t_2,t_3], whose active liquidity is

L1+L24539.780218.L_1+L_2 \approx 4539.780218.

The maximum real USDC balance available in that interval is

ymax(2)=(L1+L2)(t3t2)13621.389191.y_{\max}^{(2)} = (L_1+L_2)\left(\sqrt{t_3}-\sqrt{t_2}\right) \approx 13621.389191.

Since

1624.769298<13621.389191,1624.769298 < 13621.389191,

the remaining part of the trade is fully completed inside [t2,t3][t_2,t_3].

At the start of this second step, the virtual balance of token YY is

yv=(L1+L2)t2.y_v=(L_1+L_2)\sqrt{t_2}.

After adding the effective USDC input, the new virtual balance is

yv=(L1+L2)t2+(1ϕ)b2.y_v' = (L_1+L_2)\sqrt{t_2} + (1-\phi)b_2.

Therefore, the updated price is

p3=(yvL1+L2)2=(t2+(1ϕ)b2L1+L2)22741.396770.p_3 = \left(\frac{y_v'}{L_1+L_2}\right)^2 = \left(\sqrt{t_2}+\frac{(1-\phi)b_2}{L_1+L_2}\right)^2 \approx 2741.396770.

The CTN output of the second step is

a2=(L1+L2)(1t21p3)0.596759.a_2 = (L_1+L_2)\left(\frac{1}{\sqrt{t_2}}-\frac{1}{\sqrt{p_3}}\right) \approx 0.596759.

Hence, the total CTN received in Trade 2 is

a1+a20.892398+0.596759=1.489157 CTN.a_1+a_2 \approx 0.892398+0.596759 = 1.489157 \text{ CTN}.

The fee charged in the second step is

0.003b20.0031629.6582734.888975.0.003\,b_2 \approx 0.003\cdot 1629.658273 \approx 4.888975.

Thus, after Trade 2, the global fee growth in token YY becomes

fgY=0.006019419+4.888975L1+L20.006019419+0.001076919=0.007096338.f_g^Y = 0.006019419 + \frac{4.888975}{L_1+L_2} \approx 0.006019419 + 0.001076919 = 0.007096338.

At the end of the two trades, the current price is

p32741.396770,p_3 \approx 2741.396770,

which lies in the interval

[t2,t3).[t_2,t_3).

Table 5-4. Summary of price movements and fee-variable updates in Trades 1 and 2

Throughout this example, only token-YY fees are generated, so all token-XX fee-growth variables remain equal to zero.

StepPrice movementFee collected (USDC)fgYf_g^YfoY(i2)f_o^Y(i_2)faY(i2)f_a^Y(i_2)fbY(i2)f_b^Y(i_2)
Initial statep0[t1,t2)p_0\in[t_1,t_2)0000000000
Trade 1p0p1p_0 \to p_16.1295186.1295180.0027866030.00278660300000.0027866030.002786603
Trade 2, step 1p1t2p_1 \to t_27.1110257.1110250.0060194190.00601941900000.0060194190.006019419
Tick crosst2t_2000.0060194190.0060194190.0060194190.006019419000.0060194190.006019419
Trade 2, step 2t2p3t_2 \to p_34.8889754.8889750.0070963380.0070963380.0060194190.0060194190.0010769190.0010769190.0060194190.006019419

Fees collected by the two providers

Since the final price satisfies

t2<p3<t3,t_2 < p_3 < t_3,

position P1=[t1,t3]P_1=[t_1,t_3] is active at the final price, and so is position P2=[t2,t4]P_2=[t_2,t_4].

Because all fees in the example were collected in token YY, we compute only the token-YY fees earned by each provider.

For the first provider, the fee growth inside the interval [t1,t3][t_1,t_3] is

fin,1Y=fgYfbY(i1)faY(i3).f_{\mathrm{in},1}^Y = f_g^Y - f_b^Y(i_1) - f_a^Y(i_3).

Since t1<p3<t3t_1<p_3<t_3, and neither i1i_1 nor i3i_3 was crossed during the two trades, we have

fbY(i1)=0,faY(i3)=0.f_b^Y(i_1)=0, \qquad f_a^Y(i_3)=0.

Hence,

fin,1Y=fgY0.007096338,f_{\mathrm{in},1}^Y=f_g^Y\approx 0.007096338,

and therefore the total token-YY fees earned by the first provider are

F1Y=L1fin,1Y2199.6381490.00709633815.609375 USDC.F_1^Y = L_1\,f_{\mathrm{in},1}^Y \approx 2199.638149\cdot 0.007096338 \approx 15.609375 \text{ USDC}.

For the second provider, the fee growth inside [t2,t4][t_2,t_4] is

fin,2Y=fgYfbY(i2)faY(i4).f_{\mathrm{in},2}^Y = f_g^Y - f_b^Y(i_2) - f_a^Y(i_4).

At the final price we have t2<p3<t4t_2<p_3<t_4, so

fbY(i2)=foY(i2)0.006019419,faY(i4)=0.f_b^Y(i_2)=f_o^Y(i_2)\approx 0.006019419, \qquad f_a^Y(i_4)=0.

Therefore,

fin,2Y=0.0070963380.006019419=0.001076919,f_{\mathrm{in},2}^Y = 0.007096338 - 0.006019419 = 0.001076919,

and the second provider has earned

F2Y=L2fin,2Y2340.1420700.0010769192.520143 USDC.F_2^Y = L_2\,f_{\mathrm{in},2}^Y \approx 2340.142070\cdot 0.001076919 \approx 2.520143 \text{ USDC}.

Observe that

F1Y+F2Y15.609375+2.520143=18.129518 USDC,F_1^Y + F_2^Y \approx 15.609375 + 2.520143 = 18.129518 \text{ USDC},

which coincides with the total fees collected in the two trades:

6.129518+7.111025+4.888975=18.129518.6.129518 + 7.111025 + 4.888975 = 18.129518.

Impermanent loss of the first provider

We now compare the current value of each position with the value that the provider would have had by simply holding the originally deposited assets.

For the first provider, the initial deposit was

4 CTNand10998.469836 USDC.4 \text{ CTN} \qquad\text{and}\qquad 10998.469836 \text{ USDC}.

If those assets had been held outside the pool, then at the final price p3p_3 their value would be

W1(p3)=4p3+10998.46983642741.396770+10998.46983621964.056918 USDC.W_1(p_3) = 4p_3 + 10998.469836 \approx 4\cdot 2741.396770 + 10998.469836 \approx 21964.056918 \text{ USDC}.

On the other hand, the actual balances of the first position at price p3p_3 are

x1(p3)=L1(1p31t3)2.018457 CTN,x_1(p_3) = L_1\left(\frac{1}{\sqrt{p_3}}-\frac{1}{\sqrt{t_3}}\right) \approx 2.018457 \text{ CTN},

and

y1(p3)=L1(p3t1)16185.985532 USDC.y_1(p_3) = L_1\left(\sqrt{p_3}-\sqrt{t_1}\right) \approx 16185.985532 \text{ USDC}.

Therefore, the value of the position itself, excluding fees, is

V1(p3)=x1(p3)p3+y1(p3)2.0184572741.396770+16185.98553221719.377409 USDC.V_1(p_3) = x_1(p_3)\,p_3 + y_1(p_3) \approx 2.018457\cdot 2741.396770 + 16185.985532 \approx 21719.377409 \text{ USDC}.

The impermanent-loss amount is thus

W1(p3)V1(p3)21964.05691821719.377409244.679509 USDC.W_1(p_3)-V_1(p_3) \approx 21964.056918 - 21719.377409 \approx 244.679509 \text{ USDC}.

Equivalently, the relative impermanent loss is

V1(p3)W1(p3)10.011140,\frac{V_1(p_3)}{W_1(p_3)}-1 \approx -0.011140,

that is, approximately 1.1140%-1.1140\%.

If the uncollected fees are added back, the total marked-to-market value becomes

V1(p3)+F1Y21719.377409+15.609375=21734.986784 USDC.V_1(p_3)+F_1^Y \approx 21719.377409 + 15.609375 = 21734.986784 \text{ USDC}.

Thus, even after including fees, the first provider remains below the value of passively holding the initial assets by approximately

21964.05691821734.986784=229.070134 USDC.21964.056918 - 21734.986784 = 229.070134 \text{ USDC}.

Impermanent loss of the second provider

The second provider initially deposited only token XX, namely

6 CTN.6 \text{ CTN}.

Therefore, if they had simply held their initial assets outside the pool, their value at price p3p_3 would be

W2(p3)=6p362741.39677016448.380622 USDC.W_2(p_3)=6p_3 \approx 6\cdot 2741.396770 \approx 16448.380622 \text{ USDC}.

At the final price p3p_3, which lies inside the interval [t2,t4][t_2,t_4], the real balances of the second position are

x2(p3)=L2(1p31t4)5.692386 CTN,x_2(p_3) = L_2\left(\frac{1}{\sqrt{p_3}}-\frac{1}{\sqrt{t_4}}\right) \approx 5.692386 \text{ CTN},

and

y2(p3)=L2(p3t2)837.527546 USDC.y_2(p_3) = L_2\left(\sqrt{p_3}-\sqrt{t_2}\right) \approx 837.527546 \text{ USDC}.

Hence, the value of the position itself, excluding fees, is

V2(p3)=x2(p3)p3+y2(p3)5.6923862741.396770+837.52754616442.616292 USDC.V_2(p_3) = x_2(p_3)\,p_3 + y_2(p_3) \approx 5.692386\cdot 2741.396770 + 837.527546 \approx 16442.616292 \text{ USDC}.

Therefore, the impermanent-loss amount is

W2(p3)V2(p3)16448.38062216442.6162925.764330 USDC,W_2(p_3)-V_2(p_3) \approx 16448.380622 - 16442.616292 \approx 5.764330 \text{ USDC},

and the corresponding relative impermanent loss is

V2(p3)W2(p3)10.000350,\frac{V_2(p_3)}{W_2(p_3)}-1 \approx -0.000350,

that is, approximately 0.0350%-0.0350\%.

If the earned fees are included, then the total marked-to-market value becomes

V2(p3)+F2Y16442.616292+2.520143=16445.136435 USDC.V_2(p_3)+F_2^Y \approx 16442.616292 + 2.520143 = 16445.136435 \text{ USDC}.

Hence, even after including fees, the second provider remains below the hold benchmark by approximately

16448.38062216445.136435=3.244187 USDC.16448.380622 - 16445.136435 = 3.244187 \text{ 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)[t_1,t_2). After Trade 2 crosses tick t2t_2, the second position becomes active and the local liquidity jumps from L1L_1 to L1+L2L_1+L_2.

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 YY, and tick i2i_2 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.