Professional Documents
Culture Documents
Abstract
We formalize the constant product market maker model (aka, x ×
y = k model) [2], and formally analyze the integer rounding errors of the
implementation in the Uniswap smart contract [1].
1
x0 = x + ∆x = (1 + α)x = x
1−β
1
y 0 = y − ∆y = y = (1 − β)y
1+α
∆x ∆y
where α = and β = . Also, we have:
x y
β
∆x = x
1−β
α
∆y = y
1+α
1
Now consider a fee for each token trade. Let 0 ≤ ρ < 1 be a fee, e.g.,
ρ = 0.003 for 0.3% fee schedule.
1
1 + β( − 1)
γ
x0ρ = x + ∆x = (1 + α)x = x
1−β
1
yρ0 = y − ∆y = y = (1 − β)y
1 + αγ
∆x ∆y
where α = ,β= , and γ = 1 − ρ. Also, we have:
x y
β 1
∆x = · ·x
1−β γ
αγ
∆y = ·y
1 + αγ
Note that we have the same formula with the previous one if there is no fee, i.e.,
γ = 1. Also, note that the product of x and y slightly increases for each trade
due to the fee. That is, x0ρ × yρ0 > x × y when ρ > 0, while x0ρ × yρ0 = x × y when
ρ = 0 (no fee).
In the contract implementation [1] of this model, the token X denotes Ether
and the token Y denotes the token to trade.
Furthermore, one can invest and divest, sharing the exchange token reserves,
which we will formalize later using the concept of liquidity.
Since the implementation uses the integer arithmetic, we will also formally
analyze the approximation error caused by the integer rounding, showing that
the error is bounded and does not lead to violation of the critical properties1
denoted by the mathematical model.
2 Updating Liquidity
We formalize two functions addLiquidity and removeLiquidity that mints
and burns the liquidity, respectively. We first formalize their mathematical def-
inition, addLiquidityspec and removeLiquidityspec , that uses the real arith-
metic. Then, we formalize their implementation, addLiquiditycode and removeLiquiditycode ,
1 For example, it is not possible for a malicious user to make “free” money by exploiting
2
that uses the integer arithmetic, and analyze the approximation errors due to
the integer rounding.
2.1.1 addLiquidityspec
We formulate the mathematical definition of minting liquidity.
Definition 1. addLiquidityspec takes as input ∆e > 0 and updates the state
as follows:
addLiquidityspec (∆e)
(e, t, l) −−−−−−−−−−−−−→ (e0 , t0 , l0 )
where
e0 = (1 + α)e
t0 = (1 + α)t
l0 = (1 + α)l
∆e
and α = .
e
Here, an investor deposits both ∆e ether (wei) and ∆t = t0 − t tokens, and
mints ∆l = l0 − l liquidity. The invariant is that the ratio of e : t : l is preserved,
and k = e × t increases, as formulated in the following theorem.
addLiquidityspec (∆e)
Theorem 1. Let (e, t, l) −−−−−−−−−−−−−→ (e0 , t0 , l0 ). Let k = e × t and k 0 =
e0 × t0 . Then, we have the following:
1. e : t : l = e0 : t0 : l0
2. k < k 0
0 2
k0 l
3. =
k l
2.1.2 addLiquiditycode
In the implementation using the integer arithmetic, we have to approximate t0
and l0 that are not an integer. We formulate the approximation.
Definition 2. addLiquiditycode takes as input an integer ∆e > 0 ∈ Z and
updates the state as follows:
addLiquiditycode (∆e)
(e, t, l) ∈ Z3 −−−−−−−−−−−−−→ (e00 , t00 , l00 ) ∈ Z3
3
where
e00 = e + ∆e = (1 + α)e
∆e × t
t00 = t + + 1 = b(1 + α)tc + 1
e
∆e × l
l00 = l + = b(1 + α)lc
e
∆e 2
and α = .
e
e00 = e0
t00 = bt0 c + 1
l00 = bl0 c
and
1. e < e0 = e00
2. t < t0 < t00 ≤ t0 + 1
3. l0 − 1 < l00 ≤ l0
4. k < k 0 < k 00
00 2
l k 00
5. <
l k
That is, t0 is approximated to a larger value t00 but no larger than 1 (0 <
t − t0 ≤ 1), while l0 is approximated to a smaller value l00 but no smaller than 1
00
(−1 < l00 − l0 ≤ 0). This approximation scheme implies that k 0 is approximated
to a strictly larger value k 00 , which is desired. This means that an investor may
deposit more (up to 1) tokens than needed, but may mint less (up to -1) liquidity
than the mathematical value.
4
2.2.1 removeLiquidityspec
We formulate the mathematical definition of burning liquidity, being dual to
minting liquidity.
Definition 3. removeLiquidityspec takes as input 0 < ∆l < l and updates the
state as follows:
removeLiquidityspec (∆l)
(e, t, l) −−−−−−−−−−−−−−−→ (e0 , t0 , l0 )
where
e0 = (1 − α)e
t0 = (1 − α)t
l0 = (1 − α)l
∆l
and α = .
l
Here, an investor burns ∆l liquidity, and withdraws ∆e = e − e0 ether (wei)
and ∆t = t − t0 tokens. The invariant is dual to that of minting liquidity.
removeLiquidityspec (∆l)
Theorem 3. Let (e, t, l) −−−−−−−−−−−−−−−→ (e0 , t0 , l0 ). Let k = e × t and
k 0 = e0 × t0 . Then, we have the following:
1. e : t : l = e0 : t0 : l0
2. k 0 < k
0 2
k0 l
3. =
k l
The duality of addLiquidityspec and removeLiquidityspec is formulated in
the following theorem.
Theorem 4. If addLiquidityspec is subsequently followed by removeLiquidityspec
as follows:
addLiquidityspec (∆e) removeLiquidityspec (∆l)
(e0 , t0 , l0 ) −−−−−−−−−−−−−→ (e1 , t1 , l1 ) −−−−−−−−−−−−−−−→ (e2 , t2 , l2 )
3. l0 = l2
5
2.2.2 removeLiquiditycode
In the implementation using the integer arithmetic, we have to approximate e0
and t0 that are not an integer. We formulate the approximation.
Definition 4. removeLiquiditycode takes as input an integer 0 < ∆l < l and
updates the state as follows:
removeLiquiditycode (∆l)
(e, t, l) ∈ Z3 −−−−−−−−−−−−−−−→ (e00 , t00 , l00 ) ∈ Z3
where
00 ∆l × e
e =e− = d(l − α)ee
l
∆l × t
t00 = t − = d(1 − α)te
l
l00 = l − ∆l = (1 − α)l
∆l
and α = .
l
removeLiquidityspec (∆l) removeLiquiditycode (∆l)
Theorem 5. Let (e, t, l) −−−−−−−−−−−−−−−→ (e0 , t0 , l0 ). Let (e, t, l) −−−−−−−−−−−−−−−→
(e00 , t00 , l00 ). Let k = e × k, k 0 = e0 × k 0 , and k 00 = e00 × t00 . Then, we have:
e00 = de0 e
t00 = dt0 e
l00 = l0
and
1. e0 ≤ e00 ≤ e
2. t0 ≤ t00 ≤ t
3. l00 = l0 < l
4. k 0 ≤ k 00 ≤ k
00 2
l k 00
5. ≤
l k
That is, e0 and t0 are simply approximated to their ceiling e00 = de0 e and
t = dt0 e, which satisfies the desired property k 00 ≤ k. In other words, an
00
investor may withdraw less amounts of deposit (e − de0 e and t − dt0 e) than the
mathematical values (e − e0 and t − t0 ).
One of the desirable properties is that an investor cannot make a “free”
money by exploiting the integer rounding errors, which is formulated below.
6
Theorem 6. If addLiquiditycode is subsequently followed by removeLiquiditycode
as follows:
addLiquiditycode (∆e) removeLiquiditycode (∆l)
(e0 , t0 , l0 ) −−−−−−−−−−−−−→ (e1 , t1 , l1 ) −−−−−−−−−−−−−−−→ (e2 , t2 , l2 )
and ∆l = l1 − l0 , then we have:
1. e0 < e2
2. t0 < t2
3. l0 = l2
3.1 getInputPrice
We formalize getInputPrice in this section, and getOutputPrice in the next
section.
3.1.1 getInputPricespec
Definition 5. Let ρ be the trade fee. getInputPricespec takes as input ∆x > 0,
x, and y, and outputs ∆y such that:
αγ
getInputPricespec (∆x)(x, y) = ∆y = y
1 + αγ
∆x
where α = and γ = 1 − ρ. Also, we have:
x
x0 = x + ∆x = (1 + α)x
1
y 0 = y − ∆y = y
1 + αγ
Theorem 7. Suppose getInputPricespec (x0 − x)(x, y) = y − y 0 . Let k = x × y
and k 0 = x0 × y 0 . Then, we have:
1. x < x0
2. y > y 0
3. k < k 0
7
3.1.2 getInputPricecode
Definition 6. Let ρ be the trade fee. getInputPricecode takes as input ∆x > 0,
x, and y ∈ Z, and outputs ∆y ∈ Z such that:
αγ
getInputPricecode (∆x)(x, y) = ∆y = y
1 + αγ
∆x
where α = and γ = 1 − ρ. Also, we have:
x
x00 = x + ∆x = (1 + α)x
1
y 00 = y − ∆y = y
1 + αγ
3.2 getOutputPrice
We formalize getOutputPrice, a companion to getInputPrice.
3.2.1 getOutputPricespec
Definition 7. Let ρ be the trade fee. getOutputPricespec takes as input 0 <
∆y < y, x, and y, and outputs ∆x such that:
β 1
getOutputPricespec (∆y)(x, y) = ∆x = · ·x
1−β γ
∆y
where β = < 1 and γ = 1 − ρ. Also, we have:
y
1
1 + β(− 1)
0 γ
x = x + ∆x = ·x
1−β
y 0 = y − ∆y = (1 − β)y
8
Theorem 9. Suppose getOutputPricespec (y − y 0 )(x, y) = x0 − x. Let k = x × y
and k 0 = x0 × y 0 .
1. x < x0
2. y 0 < y
3. k < k 0
Theorem 10. getInputPrice is dual to getOutputPrice. That is,
3.2.2 getOutputPricecode
Definition 8. Let ρ be the trade fee. getOutputPricecode takes as input 0 <
∆y < y, x, and y ∈ Z, and outputs ∆x ∈ Z such that:
β 1
getOutputPricespec (∆y)(x, y) = ∆x = · ·x +1
1−β γ
∆y
where β = < 1 and γ = 1 − ρ. Also, we have:
y
1 + β( 1 − 1)
γ
x00 = x + ∆x =
· x
+1
1−β
y 00 = y − ∆y = (1 − β)y
9
4 Trading Tokens
Now we formalize the token exchange functions that update the exchange state.
4.1 ethToToken
In this section, we present a formal specification of ethToToken (including swap
and transfer).
4.1.1 ethToTokenspec
ethToTokenspec takes an input ∆e(∆e > 0) and updates the state as follows:
ethToTokenspec (∆e)
(e, t, l) −−−−−−−−−−−−→ (e0 , t0 , l)
where
e0 = e + ∆e
t0 = t − getInputPricespec (∆e, e, t)
4.1.2 ethToTokencode
ethToTokencode takes an integer input ∆e(∆e > 0) and updates the state as
follows:
ethToTokencode (∆e)
(e, t, l) −−−−−−−−−−−−→ (e00 , t00 , l)
where
e00 = e + ∆e
t00 = t − getInputPricecode (∆e, e, t) = dt0 e
4.2 ethToTokenExact
In this section, we present a formal specification of ethToTokenExact (including
swap and transfer).
4.2.1 ethToTokenExactspec
ethToTokenExactspec takes an input ∆t(0 < ∆t < t) and updates the state as
follows:
ethToTokenExactspec (∆t)
(e, t, l) −−−−−−−−−−−−−−−→ (e0 , t0 , l)
where
t0 = t − ∆t
e0 = e + getOutputPricespec (∆t, e, t)
10
4.2.2 ethToTokenExactcode
ethToTokenExactcode takes an integer input ∆t(0 < ∆t < t) and updates the
state as follows:
ethToTokenExactcode (∆t)
(e, t, l) −−−−−−−−−−−−−−−→ (e00 , t00 , l)
where
t00 = t − ∆t
e00 = e + getOutputPricecode (∆t, e, t)
4.3 tokenToEth
In this section, we present a formal specification of tokenToEth (including swap
and transfer).
4.3.1 tokenToEthspec
tokenToEthspec takes an input ∆t(∆t > 0) and updates the state as follows:
tokenToEthspec (∆t)
(e, t, l) −−−−−−−−−−−→ (e0 , t0 , l)
where
t0 = t + ∆t
e0 = e − getInputPricespec (∆t, t, e)
4.3.2 tokenToEthcode
tokenToEthcode takes an integer input ∆t(∆t > 0) and updates the state as
follows:
tokenToEthcode (∆e)
(e, t, l) −−−−−−−−−−−−→ (e00 , t00 , l)
where
t00 = t + ∆t
e00 = e − getInputPricecode (∆t, t, e) = de0 e
4.4 tokenToEthExact
In this section, we present a formal specification of tokenToEthExact (including
swap and transfer).
11
4.4.1 tokenToEthExactspec
tokenToEthExactspec takes an input ∆e(0 < ∆e < e) and updates the state as
follows:
tokenToEthExactspec (∆e)
(e, t, l) −−−−−−−−−−−−−−−→ (e0 , t0 , l)
where
e0 = e − ∆e
t0 = t + getOutputPricespec (∆e, t, e)
4.4.2 tokenToEthExactcode
tokenToEthExactcode takes an integer input ∆e(0 < ∆e < e) and updates the
state as follows:
tokenToEthExactcode (∆e)
(e, t, l) −−−−−−−−−−−−−−−→ (e00 , t00 , l)
where
e00 = e − ∆e
t00 = t + getOutputPricecode (∆e, t, e)
4.5 tokenToToken
In this section, we present a formal specification of tokenToToken (including
swap and transfer). Suppose there are two exchange contracts A and B, whose
states are (eA , tA , lA ) and (eB , tB , lB ) respectively.
4.5.1 tokenToTokenspec
tokenToTokenspec takes an input ∆tA (> 0) and updates the states as follows:
tokenToTokenspec (∆tA )
{(eA , tA , lA ), (eB , tB , lB )} −−−−−−−−−−−−−−→ {(e0A , t0A , lA ), (e0B , t0B , lB )}
where
t0A = tA + ∆tA
∆eAspec = getInputPricespec (∆tA , tA , eA )
e0A = e − ∆eAspec
e0B = eB + ∆eAspec
∆tBspec = getInputPricespec (∆eAspec , eB , tB )
t0B = tB − ∆tBspec
12
4.5.2 tokenToTokencode
tokenToTokencode takes an integer input ∆tA (> 0) and updates the states as
follows:
tokenToTokencode (∆tA )
{(eA , tA , lA ), (eB , tB , lB )} −−−−−−−−−−−−−−→ {(e00A , t00A , lA ), (e00B , t00B , lB )}
where
t00A = tA + ∆tA
∆eAcode = getInputPricecode (∆tA , tA , eA )
e00A = e − ∆eAcode
e00B = eB + ∆eAcode
∆tBcode = getInputPricecode (∆eAcode , eB , tB )
t00B = tB − ∆tBcode
4.6 tokenToTokenExact
In this section, we present a formal specification of tokenToTokenExact (in-
cluding swap and transfer). Suppose there are two exchange contracts A and
B, whose states are (eA , tA , lA ) and (eB , tB , lB ) respectively.
4.6.1 tokenToTokenExactspec
tokenToTokenExactspec takes an input ∆tB (0 < ∆tB < tB ) and updates the
states as follows:
tokenToTokenExactspec (∆tB )
{(eA , tA , lA ), (eB , tB , lB )} −−−−−−−−−−−−−−−−−→ {(e0A , t0A , lA ), (e0B , t0B , lB )}
where
t0B = tB − ∆tB
∆eBspec = getOutputPricespec (∆tB , eB , tB )
e0B = eB + ∆eBspec
e0A = eA − ∆eBspec
∆tAspec = getOutputPricespec (∆eBspec , tA , eA )
t0A = tA + ∆tAspec
4.6.2 tokenToTokenExactcode
tokenToTokenExactcode takes an integer input ∆tB (0 < ∆tB < tB ) and up-
dates the states as follows:
tokenToTokenExactcode (∆tB )
{(eA , tA , lA ), (eB , tB , lB )} −−−−−−−−−−−−−−−−−→ {(e00A , t00A , lA ), (e00B , t00B , lB )}
13
where
t00B = tB − ∆tB
∆eBcode = getOutputPricecode (∆tB , eB , tB )
e00B = eB + ∆eBcode
e00A = eA − ∆eBcode
∆tAcode = getOutputPricecode (∆eBcode , tA , eA )
t00A = tA + ∆tAcode
References
[1] Hayden Adams. Uniswap contract. https://github.com/Uniswap/
contracts-vyper.
14