-
-
Save andrecronje/3ea4e6b9d23b2b8967955f30ab8e0462 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.6; | |
interface erc20 { | |
function totalSupply() external view returns (uint256); | |
function transfer(address recipient, uint amount) external returns (bool); | |
function decimals() external view returns (uint8); | |
function symbol() external view returns (string memory); | |
function balanceOf(address) external view returns (uint); | |
function transferFrom(address sender, address recipient, uint amount) external returns (bool); | |
function approve(address spender, uint value) external returns (bool); | |
} | |
library Math { | |
function sqrt(uint y) internal 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; | |
} | |
} | |
} | |
contract StableV1Pair { | |
string public name; | |
string public symbol; | |
uint8 public constant decimals = 6; | |
uint public totalSupply = 0; | |
mapping(address => mapping (address => uint)) public allowance; | |
mapping(address => uint) public balanceOf; | |
event Transfer(address indexed from, address indexed to, uint amount); | |
event Approval(address indexed owner, address indexed spender, uint amount); | |
address public immutable token0; | |
address public immutable token1; | |
uint immutable decimals0; | |
uint immutable decimals1; | |
uint public reserve0; | |
uint public reserve1; | |
function _safeTransfer(address token,address to,uint256 value) internal { | |
(bool success, bytes memory data) = | |
token.call(abi.encodeWithSelector(erc20.transfer.selector, to, value)); | |
require(success && (data.length == 0 || abi.decode(data, (bool)))); | |
} | |
function _safeTransferFrom(address token, address from, address to, uint256 value) internal { | |
(bool success, bytes memory data) = | |
token.call(abi.encodeWithSelector(erc20.transferFrom.selector, from, to, value)); | |
require(success && (data.length == 0 || abi.decode(data, (bool)))); | |
} | |
constructor(address _token0, address _token1) { | |
token0 = _token0; | |
token1 = _token1; | |
decimals0 = 10**(erc20(_token0).decimals()-6); | |
decimals1 = 10**(erc20(_token1).decimals()-6); | |
name = string(abi.encodePacked("Stable AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); | |
symbol = string(abi.encodePacked("sAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); | |
} | |
uint _unlocked = 1; | |
modifier lock() { | |
require(_unlocked == 1); | |
_unlocked = 0; | |
_; | |
_unlocked = 1; | |
} | |
function _update(uint balance0, uint balance1) internal { | |
reserve0 = balance0; | |
reserve1 = balance1; | |
} | |
function calc_add_liquidity(uint amount0, uint amount1) external view returns (uint liquidity) { | |
(uint _reserve0, uint _reserve1) = (reserve0, reserve1); | |
(uint _balance0, uint _balance1) = ((_reserve0+amount0), (_reserve1+amount1)); | |
uint _totalSupply = totalSupply; | |
if (_totalSupply == 0) { | |
liquidity = _lp(amount0/decimals0, amount1/decimals1); | |
} else { | |
liquidity = _lp(_balance0/decimals0, _balance1/decimals1) - _lp(_reserve0/decimals0, _reserve1/decimals1); | |
} | |
} | |
function add_liquidity(uint amount0, uint amount1, uint min_liquidity, address to) external lock returns (uint liquidity) { | |
(uint _reserve0, uint _reserve1) = (reserve0, reserve1); | |
(uint _balance0, uint _balance1) = ((_reserve0+amount0), (_reserve1+amount1)); | |
if (totalSupply == 0) { | |
liquidity = _lp(amount0/decimals0, amount1/decimals1); | |
} else { | |
liquidity = _lp(_balance0/decimals0, _balance1/decimals1) - _lp(_reserve0/decimals0, _reserve1/decimals1); | |
} | |
require(liquidity > min_liquidity, '< _min_liquidity'); | |
_safeTransferFrom(token0, msg.sender, address(this), amount0); | |
_safeTransferFrom(token1, msg.sender, address(this), amount1); | |
_mint(to, liquidity); | |
_update(_balance0, _balance1); | |
} | |
function remove_liquidity(uint lp, address to) external lock returns (uint amount0, uint amount1) { | |
(uint _reserve0, uint _reserve1) = (reserve0, reserve1); | |
uint _totalSupply = totalSupply; | |
amount0 = _reserve0 * lp / _totalSupply; | |
amount1 = _reserve1 * lp / _totalSupply; | |
_burn(msg.sender, lp); | |
_safeTransfer(token0, to, amount0); | |
_safeTransfer(token1, to, amount1); | |
_update(_reserve0 - amount0, _reserve1 - amount1); | |
} | |
function exchange(uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address to) external lock { | |
(uint _reserve0, uint _reserve1) = (reserve0, reserve1); | |
(address _token0, address _token1) = (token0, token1); | |
uint _balance0 = _reserve0 + amount0In - amount0Out; | |
uint _balance1 = _reserve1 + amount1In - amount1Out; | |
require(_curve(_balance0/decimals0, _balance1/decimals1) > _curve(_reserve0/decimals0, _reserve1/decimals1), 'curve'); | |
if (amount0In > 0) _safeTransferFrom(_token0, msg.sender, address(this), amount0In); | |
if (amount1In > 0) _safeTransferFrom(_token1, msg.sender, address(this), amount1In); | |
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); | |
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); | |
_update(_balance0, _balance1); | |
} | |
function _lp(uint x, uint y) internal pure returns (uint) { | |
return Math.sqrt(Math.sqrt(_curve(x, y))) * 2; | |
} | |
function _curve(uint x, uint y) internal pure returns (uint) { | |
return x * y * (x**2+y**2) / 2; | |
} | |
function _mint(address dst, uint amount) internal { | |
totalSupply += amount; | |
balanceOf[dst] += amount; | |
emit Transfer(address(0), dst, amount); | |
} | |
function _burn(address dst, uint amount) internal { | |
totalSupply -= amount; | |
balanceOf[dst] -= amount; | |
emit Transfer(dst, address(0), amount); | |
} | |
function approve(address spender, uint amount) external returns (bool) { | |
allowance[msg.sender][spender] = amount; | |
emit Approval(msg.sender, spender, amount); | |
return true; | |
} | |
function transfer(address dst, uint amount) external returns (bool) { | |
_transferTokens(msg.sender, dst, amount); | |
return true; | |
} | |
function transferFrom(address src, address dst, uint amount) external returns (bool) { | |
address spender = msg.sender; | |
uint spenderAllowance = allowance[src][spender]; | |
if (spender != src && spenderAllowance != type(uint).max) { | |
uint newAllowance = spenderAllowance - amount; | |
allowance[src][spender] = newAllowance; | |
emit Approval(src, spender, newAllowance); | |
} | |
_transferTokens(src, dst, amount); | |
return true; | |
} | |
function _transferTokens(address src, address dst, uint amount) internal { | |
balanceOf[src] -= amount; | |
balanceOf[dst] += amount; | |
emit Transfer(src, dst, amount); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment