diff --git a/src/L2/ExponentialPremiumPriceOracle.sol b/src/L2/ExponentialPremiumPriceOracle.sol index 05f8dda1..40a4dcb2 100644 --- a/src/L2/ExponentialPremiumPriceOracle.sol +++ b/src/L2/ExponentialPremiumPriceOracle.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ~0.8.17; import "./StablePriceOracle.sol"; @@ -6,27 +7,24 @@ import {EDAPrice} from "src/lib/EDAPrice.sol"; import "solady/utils/FixedPointMathLib.sol"; import {Test, console} from "forge-std/Test.sol"; - contract ExponentialPremiumPriceOracle is StablePriceOracle { uint256 public immutable startPremium; uint256 public immutable endValue; + uint256 constant PRECISION = 1e18; constructor(uint256[] memory rentPrices, uint256 startPremium_, uint256 totalDays) StablePriceOracle(rentPrices) { startPremium = startPremium_; endValue = startPremium >> totalDays; } - - uint256 constant PRECISION = 1e18; - /** * @dev Returns the pricing premium in internal base units. */ + function _premium(string memory, uint256 expires, uint256) internal view override returns (uint256) { expires = expires + GRACE_PERIOD; if (expires > block.timestamp) { return 0; } - uint256 elapsed = block.timestamp - expires; uint256 premium = decayedPremium(elapsed); if (premium > endValue) { @@ -34,17 +32,17 @@ contract ExponentialPremiumPriceOracle is StablePriceOracle { } return 0; } - /** * @dev Returns the premium price at current time elapsed * @param elapsed time past since expiry */ + function decayedPremium(uint256 elapsed) public view returns (uint256) { + /// @dev The half-life of the premium price decay uint256 secondsInPeriod = 1 days; - uint256 perPeriodDecayPercentWad = FixedPointMathLib.WAD/2; + /// @dev 50% decay per period in wad format + uint256 perPeriodDecayPercentWad = FixedPointMathLib.WAD / 2; uint256 premium = EDAPrice.currentPrice(startPremium, elapsed, secondsInPeriod, perPeriodDecayPercentWad); return premium; } - } - diff --git a/src/lib/EDAPrice.sol b/src/lib/EDAPrice.sol index 02d107e8..9e5c8b5c 100644 --- a/src/lib/EDAPrice.sol +++ b/src/lib/EDAPrice.sol @@ -4,36 +4,36 @@ pragma solidity >=0.8.0; 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 + /// @dev reverts if perPeriodDecayPercentWad >= 1e18 /// @dev reverts if uint256 secondsInPeriod = 0 /// @dev reverts if startPrice * multiplier overflows /// @dev reverts if lnWad(percentWadRemainingPerPeriod) * ratio) overflows + /// @param startPrice the starting price of the auction /// @param secondsElapsed the seconds elapsed since auction start /// @param secondsInPeriod the seconds over which the price should decay perPeriodDecayPercentWad /// @param perPeriodDecayPercentWad the percent the price should decay during secondsInPeriod, 100% = 1e18 + /// @return price the current auction price + function currentPrice( uint256 startPrice, uint256 secondsElapsed, uint256 secondsInPeriod, uint256 perPeriodDecayPercentWad ) internal pure returns (uint256) { - // uint256 ratio = FixedPointMathLib.divWadDown(secondsElapsed, secondsInPeriod); - // uint256 percentWadRemainingPerPeriod = FixedPointMathLib.WAD - perPeriodDecayPercentWad; - uint256 ratio = FixedPointMathLib.divWad(secondsElapsed, secondsInPeriod); + 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 + 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/visualization/Price.py b/src/visualization/Price.py deleted file mode 100644 index de10ed93..00000000 --- a/src/visualization/Price.py +++ /dev/null @@ -1,54 +0,0 @@ -import math -import matplotlib.pyplot as plt - -PRECISION = 10 ** 18 -SECONDS_PER_DAY = 86400 - -def decayed_premium(start_premium, elapsed_seconds, seconds_in_period, per_period_decay_percent_wad): - ratio = elapsed_seconds / seconds_in_period - - percent_wad_remaining_per_period = (PRECISION - per_period_decay_percent_wad) / PRECISION - multiplier = (percent_wad_remaining_per_period ** ratio) - - price = (start_premium * multiplier) - return price - -def calculate_prices(start_premium, end_value, num_days, seconds_in_period, per_period_decay_percent_wad): - elapsed_times = [] - prices = [] - - for day in range(num_days): - for i in range(10): - elapsed_time = (day + i / 10) * SECONDS_PER_DAY - - premium = decayed_premium(start_premium, elapsed_time, seconds_in_period, per_period_decay_percent_wad) - - price = max(premium, end_value) / PRECISION - elapsed_times.append(day + i / 10) - prices.append(price) - - return elapsed_times, prices - -start = int(input("Input a value for the start premium: ")) -total = int(input("How many days would it take for the price to reach its end value: ")) -num = int(input("Input a value for the number of days over which the price should decay: ")) - -start_premium = start * (10 ** 18) -total_days = total -seconds_in_period = SECONDS_PER_DAY -per_period_decay_percent = 50 - -per_period_decay_percent_wad = int(per_period_decay_percent * PRECISION / 100) -end_value = start_premium >> total_days - -num_days = num - -elapsed_times, prices = calculate_prices(start_premium, end_value, num_days, seconds_in_period, per_period_decay_percent_wad) -plt.figure(figsize=(start, num)) -plt.plot(elapsed_times, prices, marker='o') -plt.xlabel('Elapsed Time (days)') -plt.ylabel('Premium Price') -plt.title('Pricing Chart') -plt.grid(True) -plt.xticks(range(num_days + 1)) -plt.show() \ No newline at end of file