Como criar uma soma constante AMM

porMatheusem20/07/2022

Nesse artigo iremos aprender a como criar uma soma constante AMM através de contrato inteligente.

Soma Constante AMM

Soma constante AMM X + Y = K

Os tokens são negociados um a um.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract CSAMM {
IERC20 public immutable token0;
IERC20 public immutable token1;
uint public reserve0;
uint public reserve1;
uint public totalSupply;
mapping(address => uint) public balanceOf;
constructor(address _token0, address _token1) {
// NOTA: Este contrato pressupõe que token0 e token1
// ambos têm os mesmos decimais
token0 = IERC20(_token0);
token1 = IERC20(_token1);
}
function _mint(address _to, uint _amount) private {
balanceOf[_to] += _amount;
totalSupply += _amount;
}
function _burn(address _from, uint _amount) private {
balanceOf[_from] -= _amount;
totalSupply -= _amount;
}
function _update(uint _res0, uint _res1) private {
reserve0 = _res0;
reserve1 = _res1;
}
function swap(address _tokenIn, uint _amountIn) external returns (uint amountOut) {
require(
_tokenIn == address(token0) || _tokenIn == address(token1),
"token inválido"
);
bool isToken0 = _tokenIn == address(token0);
(IERC20 tokenIn, IERC20 tokenOut, uint resIn, uint resOut) = isToken0
? (token0, token1, reserve0, reserve1)
: (token1, token0, reserve1, reserve0);
tokenIn.transferFrom(msg.sender, address(this), _amountIn);
uint amountIn = tokenIn.balanceOf(address(this)) - resIn;
// 0.3% gas fee
amountOut = (amountIn * 997) / 1000;
(uint res0, uint res1) = isToken0
? (resIn + amountIn, resOut - amountOut)
: (resOut - amountOut, resIn + amountIn);
_update(res0, res1);
tokenOut.transfer(msg.sender, amountOut);
}
function addLiquidity(uint _amount0, uint _amount1) external returns (uint shares) {
token0.transferFrom(msg.sender, address(this), _amount0);
token1.transferFrom(msg.sender, address(this), _amount1);
uint bal0 = token0.balanceOf(address(this));
uint bal1 = token1.balanceOf(address(this));
uint d0 = bal0 - reserve0;
uint d1 = bal1 - reserve1;
/*
a = Montante em
L = liquidez total
s = ações para cunhar
T = oferta total
s deve ser proporcional ao aumento de L para L + a
(L + a) / L = (T + s) / T
s = a * T / L
*/
if (totalSupply > 0) {
shares = ((d0 + d1) * totalSupply) / (reserve0 + reserve1);
} else {
shares = d0 + d1;
}
require(shares > 0, "shares = 0");
_mint(msg.sender, shares);
_update(bal0, bal1);
}
function removeLiquidity(uint _shares) external returns (uint d0, uint d1) {
/*
a = quantidade de saída
L = liquidez total
s = ações
T = oferta total
a / L = s / T
a = L * s / T
= (reserve0 + reserve1) * s / T
*/
d0 = (reserve0 * _shares) / totalSupply;
d1 = (reserve1 * _shares) / totalSupply;
_burn(msg.sender, _shares);
_update(reserve0 - d0, reserve1 - d1);
if (d0 > 0) {
token0.transfer(msg.sender, d0);
}
if (d1 > 0) {
token1.transfer(msg.sender, d1);
}
}
}
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint amount);
event Approval(address indexed owner, address indexed spender, uint amount);
}

Testar no Remix