Nesse artigo iremos aprender a como criar um produto constante AMM através de contrato inteligente.
Produto Constante AMM
Produto constante AMM XY = K
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract CPAMM {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) {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 _reserve0, uint _reserve1) private {reserve0 = _reserve0;reserve1 = _reserve1;}function swap(address _tokenIn, uint _amountIn) external returns (uint amountOut) {require(_tokenIn == address(token0) || _tokenIn == address(token1),"token inválido");require(_amountIn > 0, "quantia = 0");bool isToken0 = _tokenIn == address(token0);(IERC20 tokenIn, IERC20 tokenOut, uint reserveIn, uint reserveOut) = isToken0? (token0, token1, reserve0, reserve1): (token1, token0, reserve1, reserve0);tokenIn.transferFrom(msg.sender, address(this), _amountIn);/*Quanto dy para dx?xy = k(x + dx)(y - dy) = ky - dy = k / (x + dx)y - k / (x + dx) = dyy - xy / (x + dx) = dy(yx + ydx - xy) / (x + dx) = dyydx / (x + dx) = dy*/// 0.3% gas feeuint amountInWithFee = (_amountIn * 997) / 1000;amountOut = (reserveOut * amountInWithFee) / (reserveIn + amountInWithFee);tokenOut.transfer(msg.sender, amountOut);_update(token0.balanceOf(address(this)), token1.balanceOf(address(this)));}function addLiquidity(uint _amount0, uint _amount1) external returns (uint shares) {token0.transferFrom(msg.sender, address(this), _amount0);token1.transferFrom(msg.sender, address(this), _amount1);/*Quanto dx, dy adicionar?xy = k(x + dx)(y + dy) = k'Sem alteração de preço, antes e depois de adicionar liquidezx / y = (x + dx) / (y + dy)x(y + dy) = y(x + dx)x * dy = y * dxx / y = dx / dydy = y / x * dx*/if (reserve0 > 0 || reserve1 > 0) {require(reserve0 * _amount1 == reserve1 * _amount0, "x / y != dx / dy");}/*Quantas ações para cunhar?f(x, y) = valor da liquidezNós vamos definir f(x, y) = sqrt(xy)L0 = f(x, y)L1 = f(x + dx, y + dy)T = ações totaiss = ações para cunharTotal de ações deve aumentar proporcionalmente ao aumento da liquidezL1 / L0 = (T + s) / TL1 * T = L0 * (T + s)(L1 - L0) * T / L0 = s*//*Reivindicar (Claim)(L1 - L0) / L0 = dx / x = dy / yProof--- Equação 1 ---(L1 - L0) / L0 = (sqrt((x + dx)(y + dy)) - sqrt(xy)) / sqrt(xy)dx / dy = x / y então substitua dy = dx * y / x--- Equação 2 ---Equação 1 = (sqrt(xy + 2ydx + dx^2 * y / x) - sqrt(xy)) / sqrt(xy)Multiplique por sqrt(x) / sqrt(x)Equação 2 = (sqrt(x^2y + 2xydx + dx^2 * y) - sqrt(x^2y)) / sqrt(x^2y)= (sqrt(y)(sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / (sqrt(y)sqrt(x^2))sqrt(y) em cima e embaixo se cancela--- Equação 3 ---Equação 2 = (sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / (sqrt(x^2)= (sqrt((x + dx)^2) - sqrt(x^2)) / sqrt(x^2)= ((x + dx) - x) / x= dx / xDesde dx / dy = x / y,dx / x = dy / yFinalmente(L1 - L0) / L0 = dx / x = dy / y*/if (totalSupply == 0) {shares = _sqrt(_amount0 * _amount1);} else {shares = _min((_amount0 * totalSupply) / reserve0,(_amount1 * totalSupply) / reserve1);}require(shares > 0, "shares = 0");_mint(msg.sender, shares);_update(token0.balanceOf(address(this)), token1.balanceOf(address(this)));}function removeLiquidity(uint _shares)externalreturns (uint amount0, uint amount1){/*Reivindicar (Claim)dx, dy = quantidade de liquidez para removerdx = s / T * xdy = s / T * yProvaVamos encontrar dx, dy tal quev / L = s / Twherev = f(dx, dy) = sqrt(dxdy)L = liquidez total = sqrt(xy)s = açõesT = oferta total--- Equação 1 ---v = s / T * Lsqrt(dxdy) = s / T * sqrt(xy)A quantidade de liquidez a ser removida não deve alterar o preço, portantodx / dy = x / ysubstituir dy = dx * y / xsqrt(dxdy) = sqrt(dx * dx * y / x) = dx * sqrt(y / x)Divida os dois lados da Equação 1 com sqrt(y / x)dx = s / T * sqrt(xy) / sqrt(y / x)= s / T * sqrt(x^2) = s / T * xDa mesma maneirady = s / T * y*/// bal0 >= reserve0// bal1 >= reserve1uint bal0 = token0.balanceOf(address(this));uint bal1 = token1.balanceOf(address(this));amount0 = (_shares * bal0) / totalSupply;amount1 = (_shares * bal1) / totalSupply;require(amount0 > 0 && amount1 > 0, "amount0 ou amount1 = 0");_burn(msg.sender, _shares);_update(bal0 - amount0, bal1 - amount1);token0.transfer(msg.sender, amount0);token1.transfer(msg.sender, amount1);}function _sqrt(uint y) private pure returns (uint z) {if (y > 3) {z = y;uint x = y / 2 + 1;while (x < z) {z = x;x = (y / x + x) / 2;}} else if (y != 0) {z = 1;}}function _min(uint x, uint y) private pure returns (uint) {return x <= y ? x : y;}}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);}