Decentralized finance (DeFi) has been gaining a lot of momentum in recent years, and Automated Market Maker (AMM) has been a key technology driving this trend. AMMs are decentralized exchanges (DEXs) that allow users to trade cryptocurrencies without the need for an intermediary. However, a breed of AMM called Constant Product Market Maker (CPMM) is the dominant implementation DeFi space.
How CPMMs are useful
CPMMs are useful for several reasons:
- Efficient liquidity provision: CPMMs are designed to provide liquidity more efficiently than traditional AMMs. They do this by using a constant product formula which ensures that the liquidity pool is always in a balanced state, no matter how much the prices fluctuate. This results in lower slippage for users and more predictable pricing for traders.
- Lower impermanent loss: Traditional AMMs can have a problem called impermanent loss (IL) where liquidity providers can experience a loss if the price of the asset changes. CPMMs, on the other hand, have a lower IL, as the constant product formula helps to minimize the impact of price fluctuations on liquidity providers.
- Better token price stability: CPMMs are better suited for stablecoin markets as they are less sensitive to price fluctuations, ensuring a more stable token price.
A Code Sample
Here is an example of a simple CPMM smart contract, written in the Vyper programming language:
# @version >=0.3.3
from vyper.interfaces import ERC20
token0: public(address)
token1: public(address)
reserve0: public(uint256)
reserve1: public(uint256)
totalSupply: public(uint256)
balanceOf: HashMap[address, uint256]
@external
def __init__(_token0: address, _token1: address):
self.token0 = _token0
self.token1 = _token1
@internal
def _mint(_to: address, _amount: uint256):
self.balanceOf[_to] += _amount
self.totalSupply += _amount
@internal
def _burn(_from: address, _amount: uint256):
self.balanceOf[_from] -= _amount
self.totalSupply -= _amount
@internal
def _update(_reserve0: uint256, _reserve1: uint256):
self.reserve0 = _reserve0
self.reserve1 = _reserve1
@external
def swap(_tokenIn: address, _amountIn: uint256) -> uint256:
assert _tokenIn == self.token0 or _tokenIn == self.token1, "invalid token"
assert _amountIn > 0, "amount in is zero"
tokenIn: ERC20 = empty(ERC20)
tokenOut: ERC20 = empty(ERC20)
reserveIn: uint256 = 0
reserveOut: uint256 = 0
if _tokenIn == self.token0:
tokenIn = ERC20(self.token0)
tokenOut = ERC20(self.token1)
reserveIn = self.reserve0
reserveOut = self.reserve1
else:
tokenIn = ERC20(self.token1)
tokenOut = ERC20(self.token0)
reserveIn = self.reserve1
reserveOut = self.reserve0
tokenIn.transferFrom(msg.sender, self, _amountIn)
amountInWithFee: uint256 = (_amountIn * 997) / 1000
amountOut: uint256 = (reserveOut * amountInWithFee) / (reserveIn + amountInWithFee)
tokenOut.transfer(msg.sender, amountOut)
self._update(ERC20(self.token0).balanceOf(self), ERC20(self.token1).balanceOf(self))
return amountOut
@external
def addLiquidity(_amount0: uint256, _amount1: uint256) -> uint256:
ERC20(self.token0).transferFrom(msg.sender, self, _amount0)
ERC20(self.token1).transferFrom(msg.sender, self, _amount1)
if self.reserve0 > 0 or self.reserve1 > 0:
assert self.reserve0 * _amount1 == self.reserve1 * _amount0, "x / y != dx / dy"
shares: uint256 = 0
if self.totalSupply == 0:
shares = isqrt(_amount0 * _amount1)
else:
shares = min(
(_amount0 * self.totalSupply) / self.reserve0,
(_amount0 * self.totalSupply) / self.reserve0,
)
assert shares > 0, "shares are zero"
self._mint(msg.sender, shares)
return shares
@external
def removeLiquidity(_shares: uint256) -> (uint256, uint256):
bal0: uint256 = ERC20(self.token0).balanceOf(self)
bal1: uint256 = ERC20(self.token1).balanceOf(self)
amount0: uint256 = (_shares * bal0) / self.totalSupply
amount1: uint256 = (_shares * bal1) / self.totalSupply
assert amount0 > 0 and amount1 > 0, "amount0 or amount is zero"
self._burn(msg.sender, _shares)
self._update(bal0 - amount0, bal1 - amount1)
ERC20(self.token0).transfer(msg.sender, amount0)
ERC20(self.token1).transfer(msg.sender, amount1)
return (amount0, amount1)
This example is quite basic, most real-world AMM smart contracts use more complex mechanisms to calculate prices and determine the amounts of assets that are exchanged in a trade.
In the code above the contract provide the functionality to deposit, withdraw and trade assets on a pool, the trading functionality uses the current state of the pool to determine the amounts of assets that are exchanged, you could use a different mechanism to price the assets like a constant product or a bonding curve.
Please keep in mind that, this is just a sample code, it should not be used in production as is, it lacks many important aspects such
Top comments (0)