// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; pragma experimental ABIEncoderV2; /** * @title IWellFunction * @notice Defines a relationship between token reserves and LP token supply. * @dev Well Functions can contain arbitrary logic, but should be deterministic * if expected to be used alongside a Pump. When interacing with a Well or * Well Function, always verify that the Well Function is valid. */ interface IWellFunction { /** * @notice Calculates the `j`th reserve given a list of `reserves` and `lpTokenSupply`. * @param reserves A list of token reserves. The jth reserve will be ignored, but a placeholder must be provided. * @param j The index of the reserve to solve for * @param lpTokenSupply The supply of LP tokens * @param data Well function data provided on every call * @return reserve The resulting reserve at the jth index */ function calcReserve( uint[] memory reserves, uint j, uint lpTokenSupply, bytes calldata data ) external view returns (uint reserve); /** * @notice Gets the LP token supply given a list of reserves. * @param reserves A list of token reserves * @param data Well function data provided on every call * @return lpTokenSupply The resulting supply of LP tokens */ function calcLpTokenSupply( uint[] memory reserves, bytes calldata data ) external view returns (uint lpTokenSupply); /** * @notice Returns the name of the Well function. * @dev Used in Well building. */ function name() external view returns (string memory); /** * @notice Returns the symbol of the Well function. * @dev Used in Well building. */ function symbol() external view returns (string memory); } // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } } library LibMath { error PRBMath_MulDiv_Overflow(uint x, uint y, uint denominator); /** * @param a numerator * @param b denominator * @dev Division, round to nearest integer (AKA round-half-up). * * Skip explicit checks for division by zero as Solidity will natively revert. * * Implementation: * https://github.com/cryptoticket/openzeppelin-solidity/blob/04e62a7a1ece4832bee411ca5de024d2ce0b15e6/contracts/math/RoundedDivMath.sol#L31 */ function roundedDiv(uint a, uint b) internal pure returns (uint) { uint halfB = (b % 2 == 0) ? (b / 2) : (b / 2 + 1); return (a % b >= halfB) ? (a / b + 1) : (a / b); } /** * @notice Computes the `n`th root of a number `a` using the Newton--Raphson method. * @param a The number to compute the root of * @param n The root to compute * @return root The `n`th root of `a` * @dev TODO: more testing - https://ethereum.stackexchange.com/questions * /38468/calculate-the-nth-root-of-an-arbitrary-uint-using-solidity * https://en.wikipedia.org/wiki/Nth_root_algorithm * This is used in {ConstantProduct.Sol}, where the number of tokens are * restricted to 16. even roots are much cheaper to compute than uneven, * thus we recursively call sqrt(). * */ function nthRoot(uint a, uint n) internal pure returns (uint root) { assert(n > 1); if (a == 0) return 0; if (n % 2 == 0) { if (n == 2) return sqrt(a); // shortcut for square root if (n == 4) return sqrt(sqrt(a)); if (n == 8) return sqrt(sqrt(sqrt(a))); if (n == 16) return sqrt(sqrt(sqrt(sqrt(a)))); } // The scale factor is a crude way to turn everything into integer calcs. // Actually do ((10 ^ n) * x) ^ (1/n) uint a0 = (10 ** n) * a; uint xNew = 10; uint x; while (xNew != x) { x = xNew; uint t0 = x ** (n - 1); if (x * t0 > a0) { xNew = x - (x - a0 / t0) / n; } else { xNew = x + (a0 / t0 - x) / n; } } root = (xNew + 5) / 10; } /** * @notice computes the square root of a given number * @param a The number to compute the square root of * @return z The square root of x * @dev * This function is based on the Babylonian method of computing square roots * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method * Implementation from: https://github.com/Gaussian-Process/solidity-sqrt/blob/main/src/FixedPointMathLib.sol * based on https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol */ function sqrt(uint a) internal pure returns (uint z) { assembly { // This segment is to get a reasonable initial estimate for the Babylonian method. // If the initial estimate is bad, the number of correct bits increases ~linearly // each iteration instead of ~quadratically. // The idea is to get z*z*y within a small factor of x. // More iterations here gets y in a tighter range. Currently, we will have // y in [256, 256*2^16). We ensure y>= 256 so that the relative difference // between y and y+1 is small. If x < 256 this is not possible, but those cases // are easy enough to verify exhaustively. z := 181 // The 'correct' value is 1, but this saves a multiply later let y := a // Note that we check y>= 2^(k + 8) but shift right by k bits each branch, // this is to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), // and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of x, or about 20bps. // The estimate sqrt(x) = (181/1024) * (x+1) is off by a factor of ~2.83 both when x=1 // and when x = 256 or 1/256. In the worst case, this needs seven Babylonian iterations. z := shr(18, mul(z, add(y, 65536))) // A multiply is saved from the initial z := 181 // Run the Babylonian method seven times. This should be enough given initial estimate. // Possibly with a quadratic/cubic polynomial above we could get 4-6. z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) z := shr(1, add(z, div(a, z))) // See https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division. // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This check ensures we return floor. // The solmate implementation assigns zRoundDown := div(x, z) first, but // since this case is rare, we choose to save gas on the assignment and // repeat division in the rare case. // If you don't care whether floor or ceil is returned, you can skip this. if lt(div(a, z), z) { z := div(a, z) } } } } /** * @author Publius * @title Gas efficient Constant Product pricing function for Wells with 2 tokens. * * Constant Product Wells with 2 tokens use the formula: * `b_0 * b_1 = s^2` * * Where: * `s` is the supply of LP tokens * `b_i` is the reserve at index `i` */ contract ConstantProduct2 is IWellFunction { using LibMath for uint; uint constant EXP_PRECISION = 1e12; /// @dev `s = (b_0 * b_1)^(1/2)` function calcLpTokenSupply( uint[] calldata reserves, bytes calldata ) external pure override returns (uint lpTokenSupply) { lpTokenSupply = (reserves[0] * reserves[1] * EXP_PRECISION).sqrt(); } /// @dev `b_j = s^2 / b_{i | i != j}` function calcReserve( uint[] calldata reserves, uint j, uint lpTokenSupply, bytes calldata ) external pure override returns (uint reserve) { // Note: potential optimization is to use unchecked math here reserve = lpTokenSupply ** 2 / EXP_PRECISION; reserve = LibMath.roundedDiv(reserve, reserves[j == 1 ? 0 : 1]); } function name() external pure override returns (string memory) { return "Constant Product"; } function symbol() external pure override returns (string memory) { return "CP"; } }