Introduction
Uniswap V2 is a protocol that lets you exchange tokens.
Anyone can permissionlessly create a pair of two tokens ( and ).
This pair holds balances of the two tokens, known as reserves ( and ). Anyone can permissionlessly add tokens to the reserves, making them available for token swaps.
A token swap involves adding tokens to one reserve , and removing tokens from the other .
In the Uniswap V2 protocol, each swap must satisfy the following condition known as the Constant Product Invariant, else the swap transaction is reverted:
The same is sometimes formulated more popularly as:
which represents the product of current token reserves, and the state of reserves resulting from a swap having to maintain it:
This simple looking rule creates the intuitive user experience of exchanging tokens at rates that largely match user expectations (e.g. "selling for ") based on exchange rates found elsewhere in non-smart-contract systems like centralized exchanges or trading apps.
There are plenty of excellent articles out there that explain how Uniswap V2 works, by taking the formula as a given, and then showing e.g. the math for how one can derive the formula for calculating token prices, or the amount of received in exchange for , etc.
These articles are valuable, but this is not one of them.
The goal of this article is to explain why the rule "" is not an arbitrary design choice, but rather, concretely arises from how we generally, rationally price two assets in terms of each other, whether it be pricing in terms of , or apples in terms of oranges.
Pricing tokens in terms of each other
To make two tokens and tradable and let users exchange token for and vice-versa, we need supplies of the two tokens that can be traded over.
So, let's start with a pair of tokens and , with reserves (tradable balances) and respectively. What should be the exchange rate between the two tokens?
The U.S. dollar is a highly common denomination of monetary value. Going forward, we'll use "monetary value" and "USD value" interchangeably.
When we swap one token for another, we're not trying to increase our wealth. We want to keep our wealth the same while converting the currency it is denominated in.
So, in exchange for adding some USD worth in the reserve , we should be getting out the same USD worth of out of the reserve .
Therefore, the reserves and should be such that:
And so, the ratio represents not just "the number of tokens per one token", but also "the number of tokens having the same monetary worth as one token".
So, for a given incoming , should we calculate the to be given out as the following?
Note, we denominate with a minus sign since the change in reserves is a decrease.
This fraction is also referred to as the spot price of token in terms of tokens. The converse is the spot price of token in terms of token .
Issues with spot price as the price mechanism
We have the pair of tokens and with reserves and and .
A trader wants to swap in () to purchase (). The trader receives the following in return:
The new state of the pair's reserves is and .
But notice how much the trader would receive if they trade this back in:
Interesting! The trader decides to trade their newly gained back for . The new reserves are and
The trader has effectively drained the pair's reserves for , which is unacceptable.
So, what's going wrong? Let's make the problem clearer.
In the initial state, swapping in gets out:
The updated reserves are:
Now, swapping in again gets out:
Notice how swapping in the first yielded more compared to the next . This trend continues as we swap successive .
Let's now see what would happen if we'd have swapped at once:
We got out more by simply swapping in at once instead of two successive swaps.
Each swap in of the smallest possible in exchange for getting out the corresponding changes the reserves; it increases and decreases , which makes smaller.
Therefore, each successive trade of the smallest possible should be evaluated at the reserve ratio based on the changed reserves resulting from the prior trade. As we've seen, the reserve ratio continues to decrease on each successive trade. You get out successively less for every marginal swapped in for it.
And so, in our example of swapping in , we end up giving out more than we should have because we calculate for the entire amount using the ratio of the reserves right at the start of the trade, where the ratio is at its highest!
The right calculation for would, instead, evaluate the formula
at infinitely many tiny steps. Each step takes in an infinitely tiny amount of and calculates at the current reserve ratio. After each step, both reserves are updated. The next is calculated using the updated reserves. The total to give out is the sum of these infinitely many outputs.
Deriving the Constant Product Invariant
The reserves and are not independent values. The whole point of the reserves being tradable is that adding some into the reserve yields some out of the reserve.
Therefore, is a function of :
Specifically, we care about a function that relates the and reserves in the way that the previous section just established: the change in from a given change in should be based on a continuously adjusting reserve ratio.
We don't know what this looks like, exactly. In fact, our goal is to find so that, given an increase in the reserve, we can calculate the correct corresponding to give out from the reserve:
Note that for an arbitrarily small , the calculation simply corresponds to i.e. the change in resulting from an arbitrarily small change in .
So, for a given set of current reserves of and , the formula:
gives us the change in for an arbitrarily small change in .
Therefore, though we don't know what the function looks like, we do know what the derivative of i.e. the function ("f prime") looks like!
This derivative tells us the rate at which changes in a given current state of reserves and (which itself is a function of ), and an arbitrarily small change in the reserve.
We can now recover from via integration, and then we'll have a concrete way of calculating the right amount that should be taken out from the reserve for a given increase in the reserve.
Given , we have:
since refers to the rate at which changes for an arbitrarily small change in .
Now, integrate both sides with respect to
The right hand side evaluates as
For the left hand side, note is the derivative of when is a function of . Therefore:
Putting both sides together:
That looks familiar! We've derived the famous Constant Product Invariant. Now, what does it mean?
We've been trying to find a way to calculate the "right" to give out of the reserve in exchange for an increase in the reserve.
We established earlier that the "right" means that for an incoming , we calculate the corresponding as the sum of the outputs of infinitely many runs of the following:
where each run takes in an infinitely tiny amount of and calculates the corresponding output at the reserve ratio resulting from the previous run , at reserve values and .
However, performing this operation is not straightforward, since the in each run of the formula is arbitrarily small and does not have a definite numerical value. And so, we can't put a definite numerical value to corresponding output, and just sum up all the infinite outputs.
But then, we established that the reserve is a function of the reserve, and therefore, upon finding such that , we can calculate the "right" to give out in exchange for an incoming as follows:
We then concretely derived the function as a direct consequence of our initial understanding of how for an incoming should be calculated with a constantly adjusting reserve ratio in mind.
The function we found that dictates what the reserves should be for any given reserve is:
where can be thought of as the product of the two reserves. This product is first established when a liquidity provider seeds a UniswapV2 pair with balances of token and , and this changes every time a liquidity provider adds to or removes liquidity (i.e. their share of the reserves) from the pair.
Therefore, when no liquidity provider is adding or removing liquidity from the pair, the function describes all the values that the and reserves are allowed to take.
That is, and are allowed to change in all the ways that keeps the same i.e. constant. And apart from adding and removing liquidity by liquidity providers, and change during swaps. This is why swaps are considered valid only if:
As long as swaps obey this invariant, we know that the swap is taking out the right amount of in exchange for an incoming , and the trade is happening, intuitively, near the spot prices of the two tokens and , comparable with non-smart-contract systems like centralized exchanges or trading apps.