Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Premium Price with EDA integration #33

Merged
merged 5 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 12 additions & 78 deletions src/L2/ExponentialPremiumPriceOracle.sol
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
//SPDX-License-Identifier: MIT
pragma solidity ~0.8.17;

import "./StablePriceOracle.sol";
import {GRACE_PERIOD} from "src/util/Constants.sol";
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 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;
endValue = startPremium >> totalDays;
}

uint256 constant PRECISION = 1e18;
uint256 constant bit1 = 999989423469314432; // 0.5 ^ 1/65536 * (10 ** 18)
uint256 constant bit2 = 999978847050491904; // 0.5 ^ 2/65536 * (10 ** 18)
uint256 constant bit3 = 999957694548431104;
uint256 constant bit4 = 999915390886613504;
uint256 constant bit5 = 999830788931929088;
uint256 constant bit6 = 999661606496243712;
uint256 constant bit7 = 999323327502650752;
uint256 constant bit8 = 998647112890970240;
uint256 constant bit9 = 997296056085470080;
uint256 constant bit10 = 994599423483633152;
uint256 constant bit11 = 989228013193975424;
uint256 constant bit12 = 978572062087700096;
uint256 constant bit13 = 957603280698573696;
uint256 constant bit14 = 917004043204671232;
uint256 constant bit15 = 840896415253714560;
uint256 constant bit16 = 707106781186547584;

/**
* @dev Returns the pricing premium in internal base units.
Expand All @@ -53,64 +40,11 @@ contract ExponentialPremiumPriceOracle is StablePriceOracle {
* @param elapsed time past since expiry
*/
function decayedPremium(uint256 elapsed) public view returns (uint256) {
uint256 daysPast = (elapsed * PRECISION) / 1 days;
uint256 intDays = daysPast / PRECISION;
uint256 premium = startPremium >> intDays;
uint256 partDay = (daysPast - intDays * PRECISION);
uint256 fraction = (partDay * (2 ** 16)) / PRECISION;
uint256 totalPremium = _addFractionalPremium(fraction, premium);
return totalPremium;
}

function _addFractionalPremium(uint256 fraction, uint256 premium) internal pure returns (uint256) {
if (fraction & (1 << 0) != 0) {
premium = (premium * bit1) / PRECISION;
}
if (fraction & (1 << 1) != 0) {
premium = (premium * bit2) / PRECISION;
}
if (fraction & (1 << 2) != 0) {
premium = (premium * bit3) / PRECISION;
}
if (fraction & (1 << 3) != 0) {
premium = (premium * bit4) / PRECISION;
}
if (fraction & (1 << 4) != 0) {
premium = (premium * bit5) / PRECISION;
}
if (fraction & (1 << 5) != 0) {
premium = (premium * bit6) / PRECISION;
}
if (fraction & (1 << 6) != 0) {
premium = (premium * bit7) / PRECISION;
}
if (fraction & (1 << 7) != 0) {
premium = (premium * bit8) / PRECISION;
}
if (fraction & (1 << 8) != 0) {
premium = (premium * bit9) / PRECISION;
}
if (fraction & (1 << 9) != 0) {
premium = (premium * bit10) / PRECISION;
}
if (fraction & (1 << 10) != 0) {
premium = (premium * bit11) / PRECISION;
}
if (fraction & (1 << 11) != 0) {
premium = (premium * bit12) / PRECISION;
}
if (fraction & (1 << 12) != 0) {
premium = (premium * bit13) / PRECISION;
}
if (fraction & (1 << 13) != 0) {
premium = (premium * bit14) / PRECISION;
}
if (fraction & (1 << 14) != 0) {
premium = (premium * bit15) / PRECISION;
}
if (fraction & (1 << 15) != 0) {
premium = (premium * bit16) / PRECISION;
}
uint256 secondsInPeriod = 1 days;
uint256 perPeriodDecayPercentWad = FixedPointMathLib.WAD/2;
uint256 premium = EDAPrice.currentPrice(startPremium, elapsed, secondsInPeriod, perPeriodDecayPercentWad);
Keshavrajsinghal marked this conversation as resolved.
Show resolved Hide resolved
return premium;
}

}

39 changes: 39 additions & 0 deletions src/lib/EDAPrice.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
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
Keshavrajsinghal marked this conversation as resolved.
Show resolved Hide resolved
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 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
Keshavrajsinghal marked this conversation as resolved.
Show resolved Hide resolved
int256 multiplier = FixedPointMathLib.powWad(int256(percentWadRemainingPerPeriod), int256(ratio));
uint256 price = (startPrice * uint256(multiplier)) / FixedPointMathLib.WAD;
return price;
}


}
54 changes: 54 additions & 0 deletions src/visualization/Price.py
Keshavrajsinghal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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()
Loading