diff --git a/src/L2/ExponentialPremiumPriceOracle.sol b/src/L2/ExponentialPremiumPriceOracle.sol index d466a30..05f8dda 100644 --- a/src/L2/ExponentialPremiumPriceOracle.sol +++ b/src/L2/ExponentialPremiumPriceOracle.sol @@ -3,15 +3,17 @@ pragma solidity ~0.8.17; import "./StablePriceOracle.sol"; import {GRACE_PERIOD} from "src/util/Constants.sol"; import {EDAPrice} from "src/lib/EDAPrice.sol"; -import {FixedPointMathLib} from "src/lib/FixedPointMathLib.sol"; +import "solady/utils/FixedPointMathLib.sol"; +import {Test, console} from "forge-std/Test.sol"; + contract ExponentialPremiumPriceOracle is StablePriceOracle { - uint256 immutable startPremium; - uint256 immutable endValue; + uint256 public immutable startPremium; + uint256 public immutable endValue; - constructor(uint256[] memory rentPrices, uint256 startPremium, uint256 totalDays) StablePriceOracle(rentPrices) { - startPremium = startPremium; - endValue = startPremium_ >> totalDays; + constructor(uint256[] memory rentPrices, uint256 startPremium_, uint256 totalDays) StablePriceOracle(rentPrices) { + startPremium = startPremium_; + endValue = startPremium >> totalDays; } uint256 constant PRECISION = 1e18; diff --git a/src/lib/EDAPrice.sol b/src/lib/EDAPrice.sol index 02ff623..02d107e 100644 --- a/src/lib/EDAPrice.sol +++ b/src/lib/EDAPrice.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; -import {FixedPointMathLib} from "src/lib/FixedPointMathLib.sol"; +import {Test, console} from "forge-std/Test.sol"; +import "solady/utils/FixedPointMathLib.sol"; + library EDAPrice { /// @notice returns the current price of an exponential price decay auction defined by the passed params @@ -20,14 +22,18 @@ library EDAPrice { uint256 secondsInPeriod, uint256 perPeriodDecayPercentWad ) internal pure returns (uint256) { - uint256 ratio = FixedPointMathLib.divWadDown(secondsElapsed, secondsInPeriod); + // uint256 ratio = FixedPointMathLib.divWadDown(secondsElapsed, secondsInPeriod); + // uint256 percentWadRemainingPerPeriod = FixedPointMathLib.WAD - perPeriodDecayPercentWad; + uint256 ratio = FixedPointMathLib.divWad(secondsElapsed, secondsInPeriod); uint256 percentWadRemainingPerPeriod = FixedPointMathLib.WAD - perPeriodDecayPercentWad; + // percentWadRemainingPerPeriod can be safely cast because < 1e18 // ratio can be safely cast because will not overflow unless ratio > int256.max, // which would require secondsElapsed > int256.max, i.e. > 5.78e76 or 1.8e69 years - uint256 multiplier = percentWadRemainingPerPeriod ** ratio; - // casting to uint256 is safe because percentWadRemainingPerPeriod is non negative - uint256 price = startPrice * uint256(multiplier); - return (price / FixedPointMathLib.WAD); + int256 multiplier = FixedPointMathLib.powWad(int256(percentWadRemainingPerPeriod), int256(ratio)); + uint256 price = (startPrice * uint256(multiplier)) / FixedPointMathLib.WAD; + return price; } + + } \ No newline at end of file diff --git a/src/lib/FixedPointMathLib.sol b/src/lib/FixedPointMathLib.sol deleted file mode 100644 index 7d5e782..0000000 --- a/src/lib/FixedPointMathLib.sol +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Arithmetic library with operations for fixed-point numbers. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) -/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) -library FixedPointMathLib { - /*////////////////////////////////////////////////////////////// - SIMPLIFIED FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - uint256 internal constant MAX_UINT256 = 2**256 - 1; - - uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. - - function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. - } - - function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. - } - - function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. - } - - function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. - } - - /*////////////////////////////////////////////////////////////// - LOW LEVEL FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function mulDivDown( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } - - // Divide x * y by the denominator. - z := div(mul(x, y), denominator) - } - } - - function mulDivUp( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } - - // If x * y modulo the denominator is strictly greater than 0, - // 1 is added to round up the division of x * y by the denominator. - z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) - } - } - - function rpow( - uint256 x, - uint256 n, - uint256 scalar - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - switch x - case 0 { - switch n - case 0 { - // 0 ** 0 = 1 - z := scalar - } - default { - // 0 ** n = 0 - z := 0 - } - } - default { - switch mod(n, 2) - case 0 { - // If n is even, store scalar in z for now. - z := scalar - } - default { - // If n is odd, store x in z for now. - z := x - } - - // Shifting right by 1 is like dividing by 2. - let half := shr(1, scalar) - - for { - // Shift n right by 1 before looping to halve it. - n := shr(1, n) - } n { - // Shift n right by 1 each iteration to halve it. - n := shr(1, n) - } { - // Revert immediately if x ** 2 would overflow. - // Equivalent to iszero(eq(div(xx, x), x)) here. - if shr(128, x) { - revert(0, 0) - } - - // Store x squared. - let xx := mul(x, x) - - // Round to the nearest number. - let xxRound := add(xx, half) - - // Revert if xx + half overflowed. - if lt(xxRound, xx) { - revert(0, 0) - } - - // Set x to scaled xxRound. - x := div(xxRound, scalar) - - // If n is even: - if mod(n, 2) { - // Compute z * x. - let zx := mul(z, x) - - // If z * x overflowed: - if iszero(eq(div(zx, x), z)) { - // Revert if x is non-zero. - if iszero(iszero(x)) { - revert(0, 0) - } - } - - // Round to the nearest number. - let zxRound := add(zx, half) - - // Revert if zx + half overflowed. - if lt(zxRound, zx) { - revert(0, 0) - } - - // Return properly scaled zxRound. - z := div(zxRound, scalar) - } - } - } - } - } - - /*////////////////////////////////////////////////////////////// - GENERAL NUMBER UTILITIES - //////////////////////////////////////////////////////////////*/ - - function sqrt(uint256 x) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - let y := x // We start y at x, which will help us make our initial estimate. - - z := 181 // The "correct" value is 1, but this saves a multiplication later. - - // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad - // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. - - // We check y >= 2^(k + 8) but shift right by k bits - // each branch 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) - } - - // Goal was to get z*z*y within a small factor of x. More iterations could - // get y in a tighter range. Currently, we will have y in [256, 256*2^16). - // We ensured y >= 256 so that the relative difference between y and y+1 is small. - // That's not possible if x < 256 but we can just verify those cases exhaustively. - - // 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 sqrt(x), or about 20bps. - - // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range - // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. - - // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate - // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. - - // There is no overflow risk here since y < 2^136 after the first branch above. - z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. - - // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - - // If x+1 is a perfect square, the Babylonian method cycles between - // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. - // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division - // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. - // If you don't care whether the floor or ceil square root is returned, you can remove this statement. - z := sub(z, lt(div(x, z), z)) - } - } - - function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Mod x by y. Note this will return - // 0 instead of reverting if y is zero. - z := mod(x, y) - } - } - - function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Divide x by y. Note this will return - // 0 instead of reverting if y is zero. - r := div(x, y) - } - } - - function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Add 1 to x * y if x % y > 0. Note this will - // return 0 instead of reverting if y is zero. - z := add(gt(mod(x, y), 0), div(x, y)) - } - } -} \ No newline at end of file