How do DEX’s work? Understand Uniswap v1 by deep diving into the math and code. Learn the xy=k AMM curve.

Most of us are used to purchasing crypto by way of centralized exchanges – Binance, Coinbase, and many others. Usually occasions, we resolve to the identical platforms when buying and selling between completely different cryptocurrencies.

Nonetheless, centralized exchanges are rife with issues. They’ll get hacked and lose all their consumer’s cash, or worse but, the corporate behind the alternate can shut up store and run away with all the cash.

This may occasionally appear excessive, however this isn’t fiction.



Mt.Gox

Mt.Gox was the main Bitcoin alternate from 2010-2014. At it is peak, it was chargeable for 70% of all Bitcoin transactions. In early 2014, the corporate reported they had been ‘lacking’ lots of of 1000’s of Bitcoin, and declared chapter. As we speak, these misplaced Bitcoin are value billions of {dollars}.

Within the following years, Mt.Gox confronted a number of lawsuits, a few of that are nonetheless happening 8 years later. You can read more about what happened with Mt.Gox here



QuadrigaCX

QuadrigaCX, a centralized alternate primarily based in Canada, went underneath in 2018. The founding father of Quadriga mysteriously ‘died’ and all of the crypto on the platform disappeared with him. Customers reported nearly $200 million in misplaced funds.

The Ontario Securities Commision carried out thorough analysis into the actions of the corporate, and declared that the founding father of Quadriga was, merely, a fraud.

You can read more about what happened with Quadriga here


This isn’t an exhaustive listing by any means, but it surely offers you an concept. Web3 has a standard saying

Not your keys, not your cash

which signifies that when you do not personal your non-public keys, and as a substitute belief a centralized alternate to handle them for you, you do not actually personal your cryptocurrency cash. That is true.



The Beginning of Decentralized Exchanges

The thought of a decentralized alternate is easy – permit customers to commerce their crypto instantly on-chain by way of good contracts with out giving up management of their non-public keys.

Whereas it sounds easy, the fact is far more sophisticated. Briefly, decentralized exchanges are a wonderful piece of arithmetic and software program mixed collectively. We hope by the tip of this text you’ll share the identical feeling.

The start of recent decentralized exchanges was primarily led by Uniswap. Not solely is Uniswap the main decentralized alternate on Ethereum, it’s THE main dApp on Ethereum basically.

After Vitalik Buterin posted a weblog publish on Path Independence in 2017, Hayden Adams was impressed to attempt to implement Vitalik’s concepts in what finally grew to become Uniswap.

After spending over a 12 months engaged on the code, Hayden lastly introduced and launched Uniswap in November 2018. You can read more about the history of Uniswap in this blog post by the founder.

On this article, we’ll try to go over the arithmetic that permit for Uniswap to exist and performance, and hope to provide you an perception into why it is so superb.



Why is it sophisticated?

You is perhaps questioning – “why cannot we simply recreate a centralized alternate on-chain?”

Effectively, you may, but it surely’s not ok.

Centralized exchanges usually work on an order-book system. Alice places up a list saying she is keen to promote 100 of ‘TokenA’ for 50 of ‘TokenB’, and the itemizing is added to the order guide. In some unspecified time in the future, if Bob comes alongside and says he needs to purchase 100 of ‘TokenA’ for 50 of ‘TokenB’ – their orders are matched collectively, and the commerce is executed.

Order-book primarily based exchanges had been tried on Ethereum, with essentially the most important instance being 0xProject however as a result of excessive fuel required for all of the storage and matching algorithms, it was difficult to draw customers.

There was want for a brand new strategy, a approach to permit customers to swap between any two tokens arbitrarily while not having an orderbook. Moreover, cookie factors if customers may truly earn cash by utilizing Uniswap.



Uniswap V1, V2, V3

As of January 2022, three variations of Uniswap have been launched.

The primary model was launched in November 2018 and it allowed solely swaps between ether and a token. Chained swaps had been additionally attainable to permit token-token swaps. Chained swaps would permit for a TokenA <> TokenB swap by first swapping certainly one of them for ETH, after which swapping the ETH for the second token.

V2 was launched in March 2020 and it was an enormous enchancment of V1 that allowed direct swaps between any ERC20 tokens, in addition to chained swaps between any pairs.

V3 was launched in Might 2021 and it considerably improved capital effectivity, which allowed liquidity suppliers to take away an even bigger portion of their liquidity from swimming pools and nonetheless hold getting the identical rewards (or squeeze the capital in smaller worth ranges and rise up to 4000x of income).

For the needs of this tutorial, we will likely be specializing in the design of Uniswap V1, and within the following stage we’ll truly implement a barely simplified model of it that enables swapping between ether and a token.



Market Makers

Uniswap is an Automated Market Maker. Let’s attempt to perceive what meaning.

Market Makers are entities that present liquidity (property) to buying and selling markets. In non-orderbook programs, liquidity is what permits buying and selling to be attainable. Which means if you wish to promote BTC to purchase ETH, the alternate will need to have an ETH stability you should buy from in alternate for BTC. Some buying and selling pairs have very excessive liquidity (eg. BTC <> ETH buying and selling pair), however some have extraordinarily low or no liquidity in any respect (eg. rip-off tokens, or newly created tokens).

A DEX will need to have sufficient liquidity to operate and function an alternative choice to centralized exchanges.

One approach to get that liquidity is that the builders (or buyers) put in their very own cash and change into market makers. Nonetheless, this isn’t reasonable as they would want an enormous amount of cash to offer sufficient liquidity for all attainable buying and selling pairs. Furthermore, this hurts decentralization, because the builders/buyers would maintain all the ability available in the market.

One other approach, which Uniswap carried out, was to let anybody be a market maker – and that is what makes Uniswap an automated market maker. Any consumer can deposit funds to a particular buying and selling pair and add liquidity, and in alternate earn cash for doing so by way of buying and selling charges taken from the customers.



Practical Necessities

Contemplating what now we have learnt, we have to permit for the next performance no less than to construct an automatic market maker:

  • Anybody can add liquidity to change into a liquidity supplier
  • Liquidity suppliers can take away their liquidity and get again their crypto at any time when they need
  • Customers can swap between property current within the buying and selling pool, assuming there may be sufficient liquidity
  • Customers are charged a small buying and selling charges, that will get distributed amongst the liquidity suppliers to allow them to earn for offering liquidity



XY = Okay

On the core of Uniswap is one math operate:

x * y = okay

Assume now we have a buying and selling pair for ETH <> LW3 Token

x = reserve stability of ETH within the buying and selling pool

y = reserve stability of LW3 Token within the buying and selling pool

okay = a continuing

This system is chargeable for calculating costs, deciding how a lot LW3 Token could be obtained in alternate for a specific amount of ETH, or vice versa.

NOTE: It would not matter if we use x to symbolize the reserve of ETH or LW3 Token so long as y represents the other.

The system states that okay is a continuing it doesn’t matter what the reserves (x and y) are. Each swap made will increase the reserve of both ETH or LW3 Token and reduces the reserve of the opposite.

Let’s attempt to write that as a system:

(x + Δx) * (y - Δy) = okay

the place Δx is the quantity being supplied by the consumer on the market, and Δy is the quantity the consumer is receiving from the DEX in alternate for Δx.

Since okay is a continuing, we will evaluate the above two formulation to get:

x * y = (x + Δx) * (y - Δy)

Now, earlier than a swap happens, we all know the values of x, y, and Δx (given by consumer). We’re involved in calculating Δy – which is the quantity of ETH or LW3 Token the consumer will obtain.

We will simplify the above equation to resolve for Δy, and we get the next system:

Δy = (y * Δx) / (x + Δx)

Let’s attempt to code this up in Solidity.

operate calculateOutputAmount(uint inputAmount, uint inputReserve, uint outputReserve) non-public pure returns (uint) {
    uint outputAmount = (outputReserve * inputAmount) / (inputReserve + inputAmount);
    return outputAmount;
}
Enter fullscreen mode

Exit fullscreen mode

Assume now we have 100 ETH and 200 LW3 Token within the contract.

What would occur if I need to swap 1 ETH for LW3 Tokens? Let’s do the mathematics.

inputAmount = 1 ETH
inputReserve = 100 ETH
outputReserve = 200 LW3 Tokens

=> outputAmount = 1.98019802 LW3 Tokens

What would occur if as a substitute I needed to swap 2 LW3 Tokens for ETH?

inputAmount = 2 LW3 Tokens
inputReserve = 200 LW3 Tokens
outputReserve = 100 ETH

=> outputAmount = 0.999 ETH

These quantities are very near the 1:2 ratio of tokens current within the contract reserves, however barely smaller. Why?

The product system we use for worth calculations is definitely a hyperbola.

The hyperbola by no means intersects at x=0 or y=0 – because of this neither of the reserves will ever be 0 simply as a product of buying and selling! Reserves are infinite



Slippage

Since we do not get tokens within the actual ratio of reserves, this results in an attention-grabbing implication of the mathematics. The worth operate causes slippage within the worth.

The larger the quantity of tokens being traded relative to their reserve values, the decrease the value could be.

As an example I needed to attempt to drain out the complete pool, and promote 200 ETH.

inputAmount = 200 ETH
inputReserve = 100 ETH
outputReserve = 200 LW3 Tokens

=> outputAmount = 133.333 LW3 Tokens

As you may see, once we’re making an attempt to empty out the pool, we’re solely getting near a half of what we count on.

Some might even see this as a flaw of automated market makers that observe x*y = okay, but it surely’s truly not. It’s the identical mechanism that protects swimming pools from being fully drained. This additionally aligns with the legislation of provide and demand: the upper the demand relative to the provision, the extra expensive it’s to purchase that offer.



Who units the preliminary worth?

When a brand new cryptocurrency is created, there isn’t a liquidity for buying and selling pairs involving that token. As such, there isn’t a approach to calculate the value for it.

Subsequently, the primary particular person including liquidity to the pool will get to set a worth. Including liquidity entails including tokens from each side of the buying and selling pair – you can’t add liquidity for only one facet.

When the primary particular person provides liquidity, it creates a reserve stability and units the preliminary x and y values. From that time onward, we will do worth calculations when swapping between tokens.

A easy implementation of the addLiquidity operate in Solidity would look one thing like this:

operate addLiquidity(uint256 tokenAmount) public payable {
    IERC20 token = IERC20(tokenAddress);
    token.transferFrom(msg.sender, handle(this), tokenAmount);
}
Enter fullscreen mode

Exit fullscreen mode

This operate accepts ETH and a token from the consumer.

Nonetheless, this implementation is incomplete!

A second particular person can come alongside, and add liquidity in a very completely different ratio of reserves which might massively have an effect on worth calculations. We don’t need to permit for such worth manipulations, and we wish costs on the decentralized alternate to be as near these on centralized exchanges as attainable.

So we should be certain that anybody including extra liquidity to the pool is doing so in the identical proportion as that already established within the pool. We solely need to permit arbitrary ratios when the pool is totally empty.

This results in an implementation that appears like this:

operate addLiquidity(uint tokenAmount) public payable {
    // assuming a hypothetical operate
    // that returns the stability of the 
    // token within the contract
    if (getReserve() == 0) {
        IERC20 token = IERC20(tokenAddress);
        token.transferFrom(msg.sender, handle(this), tokenAmount);
    } else {
        uint ethReserve = handle(this).stability - msg.worth;
        uint tokenReserve = getReserve();
        uint proportionalTokenAmount = (msg.worth * tokenReserve) / ethReserve;
        require(tokenAmount >= proportionalTokenAmount, "incorrect ratio of tokens supplied");

        IERC20 token = IERC20(tokenAddress);
        token.transferFrom(msg.sender, handle(this), proportionalTokenAmount);
    }
}
Enter fullscreen mode

Exit fullscreen mode



LP Tokens

To date now we have mentioned how you can add liquidity, and how you can do worth calculations for swaps. However what if a liquidity supplier needs to withdraw their liquidity from the pool?

We’d like a approach to reward the liquidity suppliers for his or her tokens, as with out them different customers wouldn’t have been in a position to carry out swaps. No one would put tokens in a third-party contract if they don’t seem to be getting one thing out of it.

The one good answer for that is to gather a small payment on every token swap and distribute the charges amongst the liquidity suppliers, primarily based on how a lot liquidity they supplied.

If somebody supplied 50% of the pool’s liquidity, they need to obtain 50% of the charges. Is sensible.

There’s a fairly elegant answer to do that: Liquidity Supplier Tokens (LP Tokens)

LP Tokens work as shares.

  1. You get LP-tokens in alternate to your liquidity
  2. Quantity of tokens you get is proportional to your share of the liquidity within the pool
  3. Charges are distributed proportional to what number of LP-tokens you personal
  4. LP-tokens could be exchanged again for the liquidity + earned charges

However, there are extra necessities:

  1. Issued shares should at all times be appropriate. When another person deposits or removes liquidity after you, your shares ought to stay and preserve appropriate values.
  2. Writing information to the chain could be costly (fuel charges) – we need to cut back the maintainence prices of LP-tokens as a lot as attainable.

Think about we subject plenty of tokens – say just a few billion. The primary time somebody provides liquidity, they personal 100% of the liquidity within the pool. So can we give all of them few billion tokens?

This results in the issue that when a second particular person provides liquidity, the shares must be recalculated which is dear because of fuel charges.

Alternatively, if we select to distribute solely a portion of the tokens initially, we threat hitting the restrict, which will even finally drive us to recalculate current shares.

The one good answer appears to don’t have any provide restrict in any respect, and mint new tokens at any time when new liquidity is added. This permits for infinite progress, and if we do the mathematics fastidiously, we will be sure that issued shares stay appropriate at any time when liquidity is added or eliminated.

So, how can we calculate the quantity of LP-tokens to be minted when liquidity is added?

Uniswap V1 calculates the quantity proportionate to the ETH reserve. The next equation reveals how the quantity of latest LP-tokens is calculated relying on how a lot ETH is deposited:

amountMinted = totalAmount * (ethDeposited / ethReserve)

We will replace addLiquidity operate to mint LP-tokens when liquidity is added:

operate addLiquidity(uint tokenAmount) public payable {
    if (getReserve() == 0) {
        ...

        uint liquidity = handle(this).stability;
        _mint(msg.sender, liquidity);
    } else {
        ...
        uint liquidity = (totalSupply() * msg.worth) / ethReserve;
        _mint(msg.sender, liquidity);
    }
}
Enter fullscreen mode

Exit fullscreen mode

Now now we have LP-tokens, we will additionally use them to calculate how a lot underlying tokens to return when somebody needs to withdraw their liquidity in alternate for his or her LP-tokens.

We needn’t keep in mind how a lot they initially deposited. Since LP-tokens are proportional to quantity of ETH deposited, we will rearrange the above system to calculate the quantity of ETH to return, and proportionately calculate the quantity of tokens to return.



Charges

Now to gather charges on swaps and distribute them amongst liquidity suppliers, we’d like to consider a few issues:

  • Can we gather charges in ETH or tokens?
  • Can we pay rewards in ETH or tokens?
  • How can we gather the charges from every swap?
  • Tips on how to distribute the charges amongst all liquidity suppliers?

These could seem tough inquiries to reply, however we even have the whole lot we have to reply them.

  1. Merchants are already sending ether/tokens to the contract. As an alternative of asking for an express payment, we will simply deduct some quantity from the ether/tokens they’re sending.
  2. We will simply add the charges to the reserve stability. This implies, over time, the reserves will develop!
  3. We will gather charges within the foreign money of the asset being deposited by the dealer. Liquidity suppliers thus get a balanced quantity of ether and tokens proportional to their share of LP-tokens.

Uniswap takes a 0.03% charges from every swap. As an example we take 1% to maintain issues easy. Including charges to the contract is so simple as making just a few edits to our worth calculation system:

We had outputAmount = (outputReserve * inputAmount) / (inputReserve + inputAmount)

Now,

outputAmountWithFees = 0.99 * outputAmount

However, Solidity doesn’t help floating level operations. So for Solidity we rewrite the system as such:

outputAmountWithFees = (outputAmount * 99) / 100



Congratulations!

This was an enormous tutorial with plenty of condensed data. Congratulations on making it this far!

Whereas the mathematics and the concepts could be a little tough to initially perceive, we hope that going by going by way of the fabric and asking questions on Discord you may develop an appreciation for the way fantastically architected all of that is.

See you within the subsequent stage the place we’ll truly implement the total contract, together with an internet site, for the DEX.


Image description

This text is dropped at you by LearnWeb3 DAO. A free, complete A to Z blockchain coaching program for builders throughout the globe.

The whole lot from “What’s a Blockchain” to “Hacking good contracts” - and the whole lot in between, but additionally far more!
Be a part of us now to begin buidling with 25,000+ builders.

Website
Discord
Twitter



Add a Comment

Your email address will not be published. Required fields are marked *