diff --git a/contracts/ERC20.sol b/contracts/ERC20.sol index 9e97290..165fc86 100644 --- a/contracts/ERC20.sol +++ b/contracts/ERC20.sol @@ -3,24 +3,23 @@ pragma solidity ^0.8.0; import "./IERC20Metadata.sol"; -contract ERC20 is IERC20Metadata{ - +contract ERC20 is IERC20Metadata { string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; - mapping(address=>uint) private ownership; - mapping(address => mapping(address=>uint256)) private allowances; + mapping(address => uint256) private ownership; + mapping(address => mapping(address => uint256)) private allowances; event Mint(address to, uint256 amount); event Burn(address from, uint256 amount); - modifier validAddress (address myAddress) { + modifier validAddress(address myAddress) { require(myAddress != address(0), "Invalid address"); _; } - constructor (string memory _name, string memory _symbol) { + constructor(string memory _name, string memory _symbol) { decimals = 18; name = _name; symbol = _symbol; diff --git a/contracts/Factory.sol b/contracts/Factory.sol index da6c484..ab75840 100644 --- a/contracts/Factory.sol +++ b/contracts/Factory.sol @@ -6,30 +6,27 @@ import "./Pair.sol"; import "./IERC20.sol"; contract Factory is IFactory { + mapping(address => mapping(address => address)) private pairMapping; + address[] public allPairs; - mapping(address => mapping(address => address)) private pairMapping; - address[] public allPairs; + function allPairsLength() external view returns (uint256) { + return allPairs.length; + } - function allPairsLength() external view returns (uint) { - return allPairs.length; - } + function getPair(address tokenA, address tokenB) external view returns (address) { + return pairMapping[tokenA][tokenB]; + } - function getPair(address tokenA, address tokenB) external view returns (address){ - return pairMapping[tokenA][tokenB]; - } - - function createPair(address tokenA, address tokenB) external returns (address newPair){ - - require(tokenA != tokenB, "Same token"); - (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - require(token0 != address(0), "Invalid token address"); - require(pairMapping[token0][token1] == address(0), "Pair exists"); - - newPair = address(new Pair(token0, token1, "LP TOKEN", "LPT")); - allPairs.push(newPair); - pairMapping[token0][token1] = newPair; - pairMapping[token1][token0] = newPair; - emit PairCreated(token0, token1, newPair); - } + function createPair(address tokenA, address tokenB) external returns (address newPair) { + require(tokenA != tokenB, "Same token"); + (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), "Invalid token address"); + require(pairMapping[token0][token1] == address(0), "Pair exists"); + newPair = address(new Pair(token0, token1, "LP TOKEN", "LPT")); + allPairs.push(newPair); + pairMapping[token0][token1] = newPair; + pairMapping[token1][token0] = newPair; + emit PairCreated(token0, token1, newPair); + } } diff --git a/contracts/IERC20Metadata.sol b/contracts/IERC20Metadata.sol index fcedef5..201e2cb 100644 --- a/contracts/IERC20Metadata.sol +++ b/contracts/IERC20Metadata.sol @@ -11,18 +11,18 @@ import "./IERC20.sol"; * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} \ No newline at end of file + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} diff --git a/contracts/IFactory.sol b/contracts/IFactory.sol index 7d2e1f1..99be81c 100644 --- a/contracts/IFactory.sol +++ b/contracts/IFactory.sol @@ -2,10 +2,13 @@ pragma solidity ^0.8.0; interface IFactory { - event PairCreated(address indexed token0, address indexed token1, address pair); + event PairCreated(address indexed token0, address indexed token1, address pair); - function getPair(address tokenA, address tokenB) external view returns (address pair); - function allPairs(uint) external view returns (address pair); - function allPairsLength() external view returns (uint); - function createPair(address tokenA, address tokenB) external returns (address pair); -} \ No newline at end of file + function getPair(address tokenA, address tokenB) external view returns (address pair); + + function allPairs(uint256) external view returns (address pair); + + function allPairsLength() external view returns (uint256); + + function createPair(address tokenA, address tokenB) external returns (address pair); +} diff --git a/contracts/IPair.sol b/contracts/IPair.sol index 1514254..1f1b241 100644 --- a/contracts/IPair.sol +++ b/contracts/IPair.sol @@ -4,27 +4,47 @@ pragma solidity ^0.8.0; import "./IERC20Metadata.sol"; interface IPair is IERC20Metadata { - event ProvideLiquidity(address indexed sender, uint amount0, uint amount1); - event RemoveLiquidity(address indexed sender, uint amount0, uint amount1); - event Swap( - address indexed sender, - uint amount0In, - uint amount1In, - uint amount0Out, - uint amount1Out + event ProvideLiquidity(address indexed sender, uint256 amount0, uint256 amount1); + event RemoveLiquidity(address indexed sender, uint256 amount0, uint256 amount1); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out + ); + + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint256); + + function factory() external view returns (address); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function getReserves() + external + view + returns ( + uint112 reserve0, + uint112 reserve1, + uint32 blockTimestampLast ); - event Sync(uint112 reserve0, uint112 reserve1); + function kLast() external view returns (uint256); + + function provideLiquidity(uint256 amount0In, uint256 amount1In) external; + + function removeLiquidity(uint256 amount0Out, uint256 amount1Out) external; - function MINIMUM_LIQUIDITY() external pure returns (uint); - function factory() external view returns (address); - function token0() external view returns (address); - function token1() external view returns (address); - function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); - function kLast() external view returns (uint); + function swap( + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out + ) external; - function provideLiquidity(uint amount0In, uint amount1In) external; - function removeLiquidity(uint amount0Out, uint amount1Out) external; - function swap(uint amount0In, uint amount1In, uint amount0Out, uint amount1Out) external; - function sync() external; -} \ No newline at end of file + function sync() external; +} diff --git a/contracts/Math.sol b/contracts/Math.sol index 470aa1f..f9fb366 100644 --- a/contracts/Math.sol +++ b/contracts/Math.sol @@ -7,220 +7,220 @@ pragma solidity ^0.8.0; * @dev Standard math utilities missing in the Solidity language. */ library Math { - enum Rounding { - Down, // Toward negative infinity - Up, // Toward infinity - Zero // Toward zero + enum Rounding { + Down, // Toward negative infinity + Up, // Toward infinity + Zero // Toward zero + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a == 0 ? 0 : (a - 1) / b + 1; + } + + /** + * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) + * with further edits by Uniswap Labs also under MIT license. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2^256 + prod0. + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division. + if (prod1 == 0) { + return prod0 / denominator; + } + + // Make sure the result is less than 2^256. Also prevents denominator == 0. + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. + // See https://cs.stackexchange.com/q/138556/92363. + + // Does not overflow because the denominator cannot be zero at this stage in the function. + uint256 twos = denominator & (~denominator + 1); + assembly { + // Divide denominator by twos. + denominator := div(denominator, twos) + + // Divide [prod1 prod0] by twos. + prod0 := div(prod0, twos) + + // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. + twos := add(div(sub(0, twos), twos), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * twos; + + // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such + // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2^4. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works + // in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2^8 + inverse *= 2 - denominator * inverse; // inverse mod 2^16 + inverse *= 2 - denominator * inverse; // inverse mod 2^32 + inverse *= 2 - denominator * inverse; // inverse mod 2^64 + inverse *= 2 - denominator * inverse; // inverse mod 2^128 + inverse *= 2 - denominator * inverse; // inverse mod 2^256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is + // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; } - - /** - * @dev Returns the largest of two numbers. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256) { - return a >= b ? a : b; + } + + /** + * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator, + Rounding rounding + ) internal pure returns (uint256) { + uint256 result = mulDiv(x, y, denominator); + if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { + result += 1; } - - /** - * @dev Returns the smallest of two numbers. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; + return result; + } + + /** + * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down. + * + * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). + */ + function sqrt(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; } - /** - * @dev Returns the average of two numbers. The result is rounded towards - * zero. - */ - function average(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b) / 2 can overflow. - return (a & b) + (a ^ b) / 2; + // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. + // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have + // `msb(a) <= a < 2*msb(a)`. + // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`. + // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. + // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a + // good first aproximation of `sqrt(a)` with at least 1 correct bit. + uint256 result = 1; + uint256 x = a; + if (x >> 128 > 0) { + x >>= 128; + result <<= 64; } - - /** - * @dev Returns the ceiling of the division of two numbers. - * - * This differs from standard division with `/` in that it rounds up instead - * of rounding down. - */ - function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b - 1) / b can overflow on addition, so we distribute. - return a == 0 ? 0 : (a - 1) / b + 1; + if (x >> 64 > 0) { + x >>= 64; + result <<= 32; } - - /** - * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) - * with further edits by Uniswap Labs also under MIT license. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 result) { - unchecked { - // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use - // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2^256 + prod0. - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(x, y, not(0)) - prod0 := mul(x, y) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - - // Handle non-overflow cases, 256 by 256 division. - if (prod1 == 0) { - return prod0 / denominator; - } - - // Make sure the result is less than 2^256. Also prevents denominator == 0. - require(denominator > prod1); - - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// - - // Make division exact by subtracting the remainder from [prod1 prod0]. - uint256 remainder; - assembly { - // Compute remainder using mulmod. - remainder := mulmod(x, y, denominator) - - // Subtract 256 bit number from 512 bit number. - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) - } - - // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. - // See https://cs.stackexchange.com/q/138556/92363. - - // Does not overflow because the denominator cannot be zero at this stage in the function. - uint256 twos = denominator & (~denominator + 1); - assembly { - // Divide denominator by twos. - denominator := div(denominator, twos) - - // Divide [prod1 prod0] by twos. - prod0 := div(prod0, twos) - - // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. - twos := add(div(sub(0, twos), twos), 1) - } - - // Shift in bits from prod1 into prod0. - prod0 |= prod1 * twos; - - // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such - // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for - // four bits. That is, denominator * inv = 1 mod 2^4. - uint256 inverse = (3 * denominator) ^ 2; - - // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works - // in modular arithmetic, doubling the correct bits in each step. - inverse *= 2 - denominator * inverse; // inverse mod 2^8 - inverse *= 2 - denominator * inverse; // inverse mod 2^16 - inverse *= 2 - denominator * inverse; // inverse mod 2^32 - inverse *= 2 - denominator * inverse; // inverse mod 2^64 - inverse *= 2 - denominator * inverse; // inverse mod 2^128 - inverse *= 2 - denominator * inverse; // inverse mod 2^256 - - // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. - // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is - // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 - // is no longer required. - result = prod0 * inverse; - return result; - } + if (x >> 32 > 0) { + x >>= 32; + result <<= 16; } - - /** - * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator, - Rounding rounding - ) internal pure returns (uint256) { - uint256 result = mulDiv(x, y, denominator); - if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { - result += 1; - } - return result; + if (x >> 16 > 0) { + x >>= 16; + result <<= 8; } - - /** - * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down. - * - * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). - */ - function sqrt(uint256 a) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. - // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have - // `msb(a) <= a < 2*msb(a)`. - // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`. - // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. - // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a - // good first aproximation of `sqrt(a)` with at least 1 correct bit. - uint256 result = 1; - uint256 x = a; - if (x >> 128 > 0) { - x >>= 128; - result <<= 64; - } - if (x >> 64 > 0) { - x >>= 64; - result <<= 32; - } - if (x >> 32 > 0) { - x >>= 32; - result <<= 16; - } - if (x >> 16 > 0) { - x >>= 16; - result <<= 8; - } - if (x >> 8 > 0) { - x >>= 8; - result <<= 4; - } - if (x >> 4 > 0) { - x >>= 4; - result <<= 2; - } - if (x >> 2 > 0) { - result <<= 1; - } - - // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, - // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at - // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision - // into the expected uint128 result. - unchecked { - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - return min(result, a / result); - } + if (x >> 8 > 0) { + x >>= 8; + result <<= 4; + } + if (x >> 4 > 0) { + x >>= 4; + result <<= 2; + } + if (x >> 2 > 0) { + result <<= 1; } - /** - * @notice Calculates sqrt(a), following the selected rounding direction. - */ - function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { - uint256 result = sqrt(a); - if (rounding == Rounding.Up && result * result < a) { - result += 1; - } - return result; + // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, + // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at + // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision + // into the expected uint128 result. + unchecked { + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + return min(result, a / result); + } + } + + /** + * @notice Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + uint256 result = sqrt(a); + if (rounding == Rounding.Up && result * result < a) { + result += 1; } + return result; + } } diff --git a/contracts/Pair.sol b/contracts/Pair.sol index 6734706..44a81bb 100644 --- a/contracts/Pair.sol +++ b/contracts/Pair.sol @@ -12,113 +12,134 @@ import "./Math.sol"; * to within the pool. */ contract Pair is IPair, ERC20 { - uint256 public constant MINIMUM_LIQUIDITY = 1000; - - address public immutable factory; - address public immutable token0; - address public immutable token1; - - /// All these variable should be in the same storage space, similar to UniswapV2 - uint112 public reserve0; - uint112 public reserve1; - uint32 public blockTimestampLast; - - constructor(address token0_, address token1_, string memory name_, string memory symbol_) ERC20(name_, symbol_) { - factory = msg.sender; - token0 = token0_; - token1 = token1_; - } - - function getReserves() public view returns (uint112 reserve0_, uint112 reserve1_, uint32 blockTimestampLast_) { - reserve0_ = reserve0; - reserve1_ = reserve1; - blockTimestampLast_ = blockTimestampLast; - } - - function kLast() public view returns (uint256) { - (uint256 balance0, uint256 balance1, ) = getReserves(); - return balance0 * balance1; - } - - /// Requires user to approve transfer beforehand - function provideLiquidity(uint256 amount0In, uint256 amount1In) public { - address user = msg.sender; - (uint256 balance0, uint256 balance1, ) = getReserves(); - - uint256 liquidity; - if (totalSupply == 0) { - /// shamelessly stolen from UniswapV2 model like most things here - liquidity = Math.sqrt(amount0In * amount1In) - MINIMUM_LIQUIDITY; - mint(address(0), MINIMUM_LIQUIDITY); - } else { - liquidity = Math.min( - amount0In * totalSupply / balance0, - amount1In * totalSupply / balance1 - ); - } - - mint(user, liquidity); - balance0 += amount0In; - balance1 += amount1In; - _updateReserve(balance0, balance1); - - IERC20(token0).transferFrom(user, address(this), amount0In); - IERC20(token1).transferFrom(user, address(this), amount1In); - emit ProvideLiquidity(user, amount0In, amount1In); - } - - function removeLiquidity(uint256 amount0Out, uint256 amount1Out) public { - address user = msg.sender; - (uint256 balance0, uint256 balance1, ) = getReserves(); - - uint256 liquidity = Math.max( - Math.ceilDiv(amount0Out * totalSupply, balance0), - Math.ceilDiv(amount1Out * totalSupply, balance1) - ); - - burn(user, liquidity); - balance0 -= amount0Out; - balance1 -= amount1Out; - _updateReserve(balance0, balance1); - - IERC20(token0).transfer(user, amount0Out); - IERC20(token1).transfer(user, amount1Out); - emit RemoveLiquidity(user, amount0Out, amount1Out); + uint256 public constant MINIMUM_LIQUIDITY = 1000; + + address public immutable factory; + address public immutable token0; + address public immutable token1; + + /// All these variable should be in the same storage space, similar to UniswapV2 + uint112 public reserve0; + uint112 public reserve1; + uint32 public blockTimestampLast; + + constructor( + address token0_, + address token1_, + string memory name_, + string memory symbol_ + ) ERC20(name_, symbol_) { + factory = msg.sender; + token0 = token0_; + token1 = token1_; + } + + function getReserves() + public + view + returns ( + uint112 reserve0_, + uint112 reserve1_, + uint32 blockTimestampLast_ + ) + { + reserve0_ = reserve0; + reserve1_ = reserve1; + blockTimestampLast_ = blockTimestampLast; + } + + function kLast() public view returns (uint256) { + (uint256 balance0, uint256 balance1, ) = getReserves(); + return balance0 * balance1; + } + + /// Requires user to approve transfer beforehand + function provideLiquidity(uint256 amount0In, uint256 amount1In) public { + address user = msg.sender; + (uint256 balance0, uint256 balance1, ) = getReserves(); + + uint256 liquidity; + if (totalSupply == 0) { + /// shamelessly stolen from UniswapV2 model like most things here + liquidity = Math.sqrt(amount0In * amount1In) - MINIMUM_LIQUIDITY; + mint(address(0), MINIMUM_LIQUIDITY); + } else { + liquidity = Math.min( + (amount0In * totalSupply) / balance0, + (amount1In * totalSupply) / balance1 + ); } - /// Requires user to approve transfer beforehand - function swap(uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out) public { - address user = msg.sender; - (uint256 balance0, uint256 balance1, ) = getReserves(); - - uint256 oldK = balance0 * balance1; - - balance0 += amount0In; - balance1 += amount1In; - balance0 -= amount0Out; - balance1 -= amount1Out; - require(balance0 * balance1 >= oldK, "Pair.swap: k decreases"); - _updateReserve(balance0, balance1); - - if (amount0In > 0) IERC20(token0).transferFrom(user, address(this), amount0In); - if (amount1In > 0) IERC20(token1).transferFrom(user, address(this), amount1In); - if (amount0Out > 0) IERC20(token0).transfer(user, amount0Out); - if (amount1Out > 0) IERC20(token1).transfer(user, amount1Out); - emit Swap(user, amount0In, amount1In, amount0Out, amount1Out); - } - - function sync() public { - _updateReserve( - IERC20(token0).balanceOf(address(this)), - IERC20(token1).balanceOf(address(this)) - ); - } - - function _updateReserve(uint256 balance0, uint256 balance1) internal { - require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, "Pair._updateReserve: overflow"); - reserve0 = uint112(balance0); - reserve1 = uint112(balance1); - blockTimestampLast = uint32(block.timestamp % (2 ** 32)); - emit Sync(reserve0, reserve1); - } -} \ No newline at end of file + mint(user, liquidity); + balance0 += amount0In; + balance1 += amount1In; + _updateReserve(balance0, balance1); + + IERC20(token0).transferFrom(user, address(this), amount0In); + IERC20(token1).transferFrom(user, address(this), amount1In); + emit ProvideLiquidity(user, amount0In, amount1In); + } + + function removeLiquidity(uint256 amount0Out, uint256 amount1Out) public { + address user = msg.sender; + (uint256 balance0, uint256 balance1, ) = getReserves(); + + uint256 liquidity = Math.max( + Math.ceilDiv(amount0Out * totalSupply, balance0), + Math.ceilDiv(amount1Out * totalSupply, balance1) + ); + + burn(user, liquidity); + balance0 -= amount0Out; + balance1 -= amount1Out; + _updateReserve(balance0, balance1); + + IERC20(token0).transfer(user, amount0Out); + IERC20(token1).transfer(user, amount1Out); + emit RemoveLiquidity(user, amount0Out, amount1Out); + } + + /// Requires user to approve transfer beforehand + function swap( + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out + ) public { + address user = msg.sender; + (uint256 balance0, uint256 balance1, ) = getReserves(); + + uint256 oldK = balance0 * balance1; + + balance0 += amount0In; + balance1 += amount1In; + balance0 -= amount0Out; + balance1 -= amount1Out; + require(balance0 * balance1 >= oldK, "Pair.swap: k decreases"); + _updateReserve(balance0, balance1); + + if (amount0In > 0) IERC20(token0).transferFrom(user, address(this), amount0In); + if (amount1In > 0) IERC20(token1).transferFrom(user, address(this), amount1In); + if (amount0Out > 0) IERC20(token0).transfer(user, amount0Out); + if (amount1Out > 0) IERC20(token1).transfer(user, amount1Out); + emit Swap(user, amount0In, amount1In, amount0Out, amount1Out); + } + + function sync() public { + _updateReserve( + IERC20(token0).balanceOf(address(this)), + IERC20(token1).balanceOf(address(this)) + ); + } + + function _updateReserve(uint256 balance0, uint256 balance1) internal { + require( + balance0 <= type(uint112).max && balance1 <= type(uint112).max, + "Pair._updateReserve: overflow" + ); + reserve0 = uint112(balance0); + reserve1 = uint112(balance1); + blockTimestampLast = uint32(block.timestamp % (2**32)); + emit Sync(reserve0, reserve1); + } +} diff --git a/contracts/PairHelper.sol b/contracts/PairHelper.sol index 73ed22c..cb1b024 100644 --- a/contracts/PairHelper.sol +++ b/contracts/PairHelper.sol @@ -7,63 +7,70 @@ import "./Pair.sol"; /* * Instead of putting helper functions in the core Pair contract, * these function are put in a separate library to keep the Pair contract - * succinct, in exchange extra gas for approximately 1 extra storage + * succinct, in exchange extra gas for approximately 1 extra storage * call in getReserves() is needed (only a guess) */ library PairHelper { - function provideLiquidityByToken(Pair pair, uint256 amount) external { - uint256 totalSupply = pair.totalSupply(); - require(totalSupply > 0, "Pair.pLBT: totalSupply = 0"); + function provideLiquidityByToken(Pair pair, uint256 amount) external { + uint256 totalSupply = pair.totalSupply(); + require(totalSupply > 0, "Pair.pLBT: totalSupply = 0"); - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.provideLiquidity( - Math.ceilDiv(amount * balance0, totalSupply), - Math.ceilDiv(amount * balance1, totalSupply) - ); - } + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.provideLiquidity( + Math.ceilDiv(amount * balance0, totalSupply), + Math.ceilDiv(amount * balance1, totalSupply) + ); + } - function removeLiquidityByToken(Pair pair, uint256 amount) external { - uint256 totalSupply = pair.totalSupply(); + function removeLiquidityByToken(Pair pair, uint256 amount) external { + uint256 totalSupply = pair.totalSupply(); - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.removeLiquidity( - amount * balance0 / totalSupply, - amount * balance1 / totalSupply - ); - } + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.removeLiquidity((amount * balance0) / totalSupply, (amount * balance1) / totalSupply); + } - /* - * Formula for optimal amount1Out: - * balance0 * balance1 <= (balance0 + amount0In) * (balance1 + amount1Out) - * balance0 * balance1 / (balance0 + amount0In) <= balance1 - amount1Out - * balance1 - balance0 * balance1 / (balance0 + amount0In) >= amount1Out - */ - function swapExactIn0(Pair pair, uint256 amount0In) external { - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.swap(amount0In, 0, 0, balance1 - Math.ceilDiv(balance0 * balance1, balance0 + amount0In)); - } + /* + * Formula for optimal amount1Out: + * balance0 * balance1 <= (balance0 + amount0In) * (balance1 + amount1Out) + * balance0 * balance1 / (balance0 + amount0In) <= balance1 - amount1Out + * balance1 - balance0 * balance1 / (balance0 + amount0In) >= amount1Out + */ + function swapExactIn0(Pair pair, uint256 amount0In) external { + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.swap(amount0In, 0, 0, balance1 - Math.ceilDiv(balance0 * balance1, balance0 + amount0In)); + } - /// Similar to swapExactIn0 - function swapExactIn1(Pair pair, uint256 amount1In) external { - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.swap(0, amount1In, balance0 - Math.ceilDiv(balance0 * balance1, balance1 + amount1In), 0); - } + /// Similar to swapExactIn0 + function swapExactIn1(Pair pair, uint256 amount1In) external { + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.swap(0, amount1In, balance0 - Math.ceilDiv(balance0 * balance1, balance1 + amount1In), 0); + } - /* - * Formula for optimal amount1In: - * balance0 * balance1 <= (balance0 - amount0Out) * (balance1 + amount1In) - * balance0 * balance1 / (balance0 - amount0Out) <= balance1 + amount1In - * balance0 * balance1 / (balance0 - amount0Out) - balance1 <= amount1In - */ - function swapExactOut0(Pair pair, uint256 amount0Out) external { - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.swap(0, Math.ceilDiv(balance0 * balance1, balance0 - amount0Out) - balance1, amount0Out, 0); - } - - /// Similar to swapExactOut0 - function swapExactOut1(Pair pair, uint256 amount1Out) external { - (uint256 balance0, uint256 balance1, ) = pair.getReserves(); - pair.swap(Math.ceilDiv(balance0 * balance1, balance1 - amount1Out) - balance0, 0, 0, amount1Out); - } -} \ No newline at end of file + /* + * Formula for optimal amount1In: + * balance0 * balance1 <= (balance0 - amount0Out) * (balance1 + amount1In) + * balance0 * balance1 / (balance0 - amount0Out) <= balance1 + amount1In + * balance0 * balance1 / (balance0 - amount0Out) - balance1 <= amount1In + */ + function swapExactOut0(Pair pair, uint256 amount0Out) external { + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.swap( + 0, + Math.ceilDiv(balance0 * balance1, balance0 - amount0Out) - balance1, + amount0Out, + 0 + ); + } + + /// Similar to swapExactOut0 + function swapExactOut1(Pair pair, uint256 amount1Out) external { + (uint256 balance0, uint256 balance1, ) = pair.getReserves(); + pair.swap( + Math.ceilDiv(balance0 * balance1, balance1 - amount1Out) - balance0, + 0, + 0, + amount1Out + ); + } +} diff --git a/contracts/PairHelperClient.sol b/contracts/PairHelperClient.sol index d0c92d3..d1c0cf8 100644 --- a/contracts/PairHelperClient.sol +++ b/contracts/PairHelperClient.sol @@ -32,8 +32,8 @@ contract PairHelperClient { function swapExactOut0(uint256 amount0Out) external { pair.swapExactOut0(amount0Out); } - + function swapExactOut1(uint256 amount1Out) external { pair.swapExactOut1(amount1Out); } -} \ No newline at end of file +}