Uniswap is a simple smart contract interface for swapping ERC20 tokens and in general it’s pretty awesome. The story behind it is really inspirational to me and just recently Hayden Adams tweeted that $10million of liquidity was added in a 24 hour period – surely pretty succesful by any measure!
Some of the features of Uniswap include:
- Supplies on-chain liquidity to other smart contracts
- Ease of use
- Gas efficiency (10x less than Bancor)
- Decentralised/censorship resistant
And just as a reminder:
Liquidity describes the degree to which an asset or security can be quickly bought or sold in the market at a price reflecting its intrinsic value.Investopedia
Each Uniswap pool holds Eth and another token and trades are exectuted against these reserves.
By supplying tokens to the pooled reserve (being a liquidity provider) you get a proportional share of transaction fees via a liquidity token.
Prices are set automatically using eqn:
x * y = k or in terms of tokens:
eth_pool * token_pool = invariant.
The invariant is constant during trades but DOES change when liquidity is added or removed from pool. (So not really an invariant?!)
Liquidity tokens are minted to track the relative proportion of total reserves that each liquidity provider has supplied.
Fees are taken during a token swap and are added to the liquidity reserves. Since total reserves are increased without adding any additional share tokens, this increases that value of all liquidity tokens equally. This functions as a payout to liquidity providers that can be collected by burning shares. (It’s also the reason that the invariant increases at the end of every trade)
When a liquidity provider joins the pool the amount of liquidity tokens minted are calcualted by:
(Initial pool liquidity is equal to initial Eth value provided) total_liquidity = self.totalSupply eth_reserve = self.balance - msg.value liquidity_minted = msg.value * total_liquidity / eth_reserve
Liqudity tokens can be burned at any time to return a proportional share of the markets liquidity to the provider:
removeLiquidity(amount...): total_liquidity = self.totalSupply token_reserve = self.token.balanceOf(self) eth_amount = amount * self.balance / total_liquidity token_amount = amount * token_reserve / total_liquidity self.balances[msg.sender] -= amount self.totalSupply = total_liquidity - amount send(msg.sender, eth_amount) return eth_amount, token_amount
Eth -> Token: Traders Eth is added to pool and Token is removed. So Eth amount increases, token amount decreases. Token becomes more expensive.
Token -> Eth: Traders Token is added to pool and Eth is removed. So Eth amount decreases, token amound decreases. Eth becomes more expensive.
Arbitrage seems to be the key to so many DeFi applications! I think the following is nice description of what it is:
Arbitrage trading is a strategy that can be best understood as a trader that takes advantage of the price differential that exists between two markets. In the case of cryptocurrency, this price differential can be found in the differences in price of a digital asset between cryptocurrency exchanges. If a trader identified an opportunity for arbitrage trading, then they would purchase a digital asset in one exchange, and then sell it on another cryptocurrency exchange.Mycryptopedia
To me it’s all about icentives – basically in the form of greed! There’s always someone looking to make money from an opportunity. In this case they execute a trade to make a profit but by making they trade they change the price which basically corrects it to the market price – that’s cool!
- the global price of ETH-USD moves enough away from the pool price, an arbitrage opportunity exists and is corrected
- When the initial liquidity is provided the exchange rate is set. If the ratio of liquidity provided isn’t realistic arbitrage traders will correct at the expense of initial liquidity provider.
Some Examples With Numbers
Invariant is set on initial deposit to a new pool. For example 10 ETH and 500 FUN are deposited into new ETH/FUN pool. Invariant is set to:
ETH_pool * FUN_pool = invariant 10 * 500 = 5000
Now for an ETH -> FUN trade:
Buyer sends 1ETH Fee = 1 ETH / 400 = 0.0025 ETH (0.25% fee) ETH_pool = 10 + 1 - 0.0025 = 10.9975 FUN_pool = 5000/10.9975 = 454.65 (invariant/ETH_pool) Buyer receives: 500 - 454.65 = 45.35 FUN Fee is added back to pool: ETH_pool = 10.9975 + 0.0025 = 11 FUN_pool = 454.65 New invariant = 11 * 454.65 = 5001.15 Executed price = 45.35 FUN/ETH But now price has changed: Fee = 0.0025 ETH ETH_pool = 11.9975 FUN_pool = 5001.15/11.9975 = 416.85 Buyer receives: 454.65 - 416.85 = 37.8 Executed price = 37.8 FUN/ETH
Price slippage refers to the difference between the expected price before a transaction is executed and the actual price at which it is executed.Bancor Support
Easiest for me to think of spot price and actual price.
A trade that is large relative to the size of the total size of the liquidity pool will cause price slippage.
Same example as above: ETH_pool * FUN_pool = invariant 10 * 500 = 5000 Spot price for ETH -> FUN = 500/10 = 50 1ETH Purchase: Executed price = 45.35 FUN/ETH 10ETH Purchase: Executed price = 24.969 FUN/ETH Fee = 10 ETH / 400 = 0.025 ETH (0.25% fee) ETH_pool = 10 + 10 - 0.025 = 19.975 FUN_pool = 5000/19.975 = 250.31 Buyer receives: 500 - 250.31 = 249.69 FUN Fee is added back to pool: ETH_pool = 19.975 + 0.025 = 20 FUN_pool = 250.31 New invariant = 20 * 250.31 = 5006.2 Executed price = 24.969 FUN/ETH
I found this comment from Vitalik on this EthResearch post interesting:
The point is not for this kind of exchange to be the only exchange; the point is for it to be one type among many. It offers the benefits of executing a complete trade in one transaction, and extreme user-friendliness even to smart contracts, which are very real benefits and will at least sometimes exceed the costs of slippage to some users. I am ok with just accepting that this kind of approach will not be acceptable to whales who want to liquidate large amounts of things; that’s not the target market.