Skip to content

Instantly share code, notes, and snippets.

@andrecronje
Last active August 28, 2021 07:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrecronje/3ea4e6b9d23b2b8967955f30ab8e0462 to your computer and use it in GitHub Desktop.
Save andrecronje/3ea4e6b9d23b2b8967955f30ab8e0462 to your computer and use it in GitHub Desktop.
// 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