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

Add Launch Auction Price Oracle with reduced half life #70

Merged
merged 25 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7dd9033
Add parameterized auction half life
stevieraykatz Jul 21, 2024
6b37b28
Merge branch 'main' into launch-auction
stevieraykatz Aug 5, 2024
facd077
Revert to prod exp. price auction
stevieraykatz Aug 5, 2024
0e056b4
Add Launch Auction pricing contract
stevieraykatz Aug 5, 2024
edea68f
Fix tests, get ready for review
stevieraykatz Aug 5, 2024
0a6684a
Cleanup return to avoid overshadowing
stevieraykatz Aug 5, 2024
4774c14
Fix pragma
stevieraykatz Aug 5, 2024
6fb4de2
rename to avoid overshadowing
stevieraykatz Aug 5, 2024
a2f7087
Rename constant
stevieraykatz Aug 5, 2024
17a5a48
Add unit tests
stevieraykatz Aug 7, 2024
0f0589f
Lint
stevieraykatz Aug 7, 2024
d4534f7
Fix comments in test
stevieraykatz Aug 7, 2024
9f22d48
Fix tests per PR
stevieraykatz Aug 8, 2024
ea08a84
Add comment for base prices
stevieraykatz Aug 8, 2024
61c873e
lint
stevieraykatz Aug 8, 2024
1563df6
Change halflife to 1.5 hours, allow auction duration to be specified …
stevieraykatz Aug 9, 2024
f5826c9
Fix typo in natspec
stevieraykatz Aug 9, 2024
71394e9
Switch to Error from require
stevieraykatz Aug 9, 2024
74310cb
Add test for new error message
stevieraykatz Aug 9, 2024
1e46dc1
Fix unit conversion issue in bitshift operation
stevieraykatz Aug 11, 2024
01ac27f
Add launch auction integration tests
stevieraykatz Aug 12, 2024
e9b954d
reorder setup to mimic expected ordering
stevieraykatz Aug 12, 2024
eba4cfa
lint
stevieraykatz Aug 12, 2024
ac267c2
Update src/L2/LaunchAuctionPriceOracle.sol
stevieraykatz Aug 13, 2024
11936e7
Fix some comments, cleanup tests
stevieraykatz Aug 13, 2024
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
91 changes: 91 additions & 0 deletions src/L2/LaunchAuctionPriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";

import {EDAPrice} from "src/lib/EDAPrice.sol";
import {StablePriceOracle} from "src/L2/StablePriceOracle.sol";

/// @title Launch Auction Price Oracle
///
/// @notice The mechanism by which names are auctioned upon Basenames public launch. The RegistrarController
/// Passes the `launchTime` in place of expiry for all new names. The half life of this contract is hard-coded
/// to 1.5 hours, accomplished by bitshifting the `endValue` and by passing this period into the exponential decay
/// calculation.
///
/// Inspired by the `ExponentialPremiumPriceOracle` implemented by ENS:
/// https://github.com/ensdomains/ens-contracts/blob/staging/contracts/ethregistrar/ExponentialPremiumPriceOracle.sol
///
/// @author Coinbase (https://github.com/base-org/usernames)
contract LaunchAuctionPriceOracle is StablePriceOracle {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there be a separate PR that utilizes this new oracle?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow. In case this context helps, the flow for launch will be:

  1. Deploy this new LaunchAuctionPriceOracle along with the GA Contracts
  2. Use this oracle for the launch auction duration
  3. After it concludes, switch over to using the audited ExponentialPremiumPriceOracle

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How and when does 2. happen?

Copy link
Collaborator Author

@stevieraykatz stevieraykatz Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's set as the price oracle upon deployment of the GA Registrar Controller:

IPriceOracle prices_,

Then, we do a pair of transactions in quick succession to "turn on" registrations.

  1. Set the Launch Time using:
    function setLaunchTime(uint256 launchTime_) external onlyOwner {
  2. Set the RegistrarController as the controller for the BaseRegistrar via:
    function addController(address controller) external onlyOwner {

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Starting premium for the dutch auction.
uint256 public immutable startPremium;

/// @notice Ending value of the auction, calculated on construction.
uint256 public immutable endValue;
Comment on lines +21 to +28
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Immutable variables are not actually part of contract's storage if that's the intention of STORAGE comment.


/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice The half-life of the premium price decay in seconds.
uint256 constant PRICE_PREMIUM_HALF_LIFE = 1.5 hours;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PRICE_PREMIUM_HALF_LIFE constant can be made public


/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Thrown when the auction duration is not cleanly divisible by the auction halflife.
error InvalidDuration();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMPLEMENTATION */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Construction of the premium pricing oracle.
///
/// @param rentPrices The base prices passed to construction of the StablePriceOracle.
/// @param startPremium_ The starting price for the dutch auction, denominated in wei.
/// @param totalHours The total duration (in hours) for the dutch auction.
constructor(uint256[] memory rentPrices, uint256 startPremium_, uint256 totalHours) StablePriceOracle(rentPrices) {
startPremium = startPremium_;
if ((totalHours * 1 hours) % PRICE_PREMIUM_HALF_LIFE != 0) revert InvalidDuration();
endValue = startPremium >> ((totalHours * 1 hours) / PRICE_PREMIUM_HALF_LIFE);
}

/// @notice The internal method for calculating pricing premium
///
/// @dev This method handles three cases:
/// 1. The name is not yet expired, premium = 0.
/// 2. The name is expired and in the auction window, premium = calculated decayed premium.
/// 3. The name is expired and outside of the auction window, premium = 0.
///
/// @param expires Timestamp of when the name will expire.
///
/// @return Price premium denominated in wei.
function _premium(string memory, uint256 expires, uint256) internal view override returns (uint256) {
if (expires > block.timestamp) {
return 0;
}
uint256 elapsed = block.timestamp - expires;
uint256 premium_ = decayedPremium(elapsed);
if (premium_ > endValue) {
return premium_ - endValue;
}
return 0;
Comment on lines +70 to +78
Copy link
Collaborator

@ilikesymmetry ilikesymmetry Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am really struggling to get a clear grasp of this code and how it behaves at the boundaries even after a few rereads. What do you think of rewriting without the endValue abstraction and just focusing on using timestamps like your comment describes? Rename totalDays to auctionDuration, store it as an immutable and skip using endValue entirely (unless it's needed somewhere else?).

Suggested change
if (expires > block.timestamp) {
return 0;
}
uint256 elapsed = block.timestamp - expires;
uint256 premium_ = decayedPremium(elapsed);
if (premium_ > endValue) {
return premium_ - endValue;
}
return 0;
if (expires > block.timestamp || block.timestamp - expires > auctionDuration) {
return 0;
}
return decayedPremium(block.timestamp - expires);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I generally agree that this code could use some cleanup, it's a forked and audited contract from ENS that we're making a slight tweak to. Not sure that the cleanup is worth the risk of introducing a regression.

}

/// @notice The mechanism for calculating the decayed premium.
///
/// @param elapsed Seconds elapsed since the auction started.
///
/// @return Dacayed price premium denominated in wei.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Dacayed" -> "Decayed"

function decayedPremium(uint256 elapsed) public view returns (uint256) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the computation mechanism premium_ - endValue in _premium() correctly, our frontend should take whatever value is returned by this and subtract endValue on the UI to show users. By my estimates, endValue should be 100 ether * 0.5 ** (36 / 1.5) => 0.0000059605 ether. I recall we have some refund mechanism built into the contracts because the decay will reduce the price even further by the time the transaction validates so this isn't a big deal then.

Noting this more to confirm my own understanding, but maybe worth a dev natspec on this function that this value is an overestimate of what the user actually needs to pay given those two factors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F/E doesn't need to do anything special. The RegistrarController does the heavy lifting parsing the response. See:

/// @notice Checks the register price for a provided `name` and `duration`.
///
/// @param name The name to check the register price of.
/// @param duration The time that the name would be registered.
///
/// @return The all-in price for the name registration, denominated in wei.
function registerPrice(string memory name, uint256 duration) public view returns (uint256) {
IPriceOracle.Price memory price = rentPrice(name, duration);
return price.base + price.premium;
}

I think that the natspec here is accurate. This method only returns the pure price premium. The implementation handles the use-case specific logic.

/// @dev 50% decay per period in wad format
uint256 perPeriodDecayPercentWad = FixedPointMathLib.WAD / 2;
return EDAPrice.currentPrice(startPremium, elapsed, PRICE_PREMIUM_HALF_LIFE, perPeriodDecayPercentWad);
}
}
2 changes: 1 addition & 1 deletion src/L2/StablePriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract StablePriceOracle is IPriceOracle {

/// @notice Price Oracle constructor which sets the immutably stored prices.
///
/// @param _rentPrices An array of prices orderd in increasing length.
/// @param _rentPrices An array of prices ordered in increasing length.
constructor(uint256[] memory _rentPrices) {
price1Letter = _rentPrices[0];
price2Letter = _rentPrices[1];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";
import {IReverseRegistrar} from "src/L2/interface/IReverseRegistrar.sol";
import {L1Resolver} from "src/L1/L1Resolver.sol";
import {L2Resolver} from "src/L2/L2Resolver.sol";
import {LaunchAuctionPriceOracle} from "src/L2/LaunchAuctionPriceOracle.sol";
import {RegistrarController} from "src/L2/RegistrarController.sol";
import {Registry} from "src/L2/Registry.sol";
import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol";
Expand All @@ -27,7 +28,7 @@ import {
BASE_ETH_NAME
} from "src/util/Constants.sol";

contract IntegrationTest is Test {
contract IntegrationTestBase is Test {
address owner;
address signer;
address alice;
Expand All @@ -40,6 +41,7 @@ contract IntegrationTest is Test {
RegistrarController registrarController;
L2Resolver defaultL2Resolver;
ReverseRegistrar reverseRegistrar;
LaunchAuctionPriceOracle launchAuctionPriceOracle;
ExponentialPremiumPriceOracle exponentialPremiumPriceOracle;

AttestationValidator attestationValidator;
Expand All @@ -52,7 +54,14 @@ contract IntegrationTest is Test {
bytes32 constant REVERSE_LABEL = 0xdec08c9dbbdd0890e300eb5062089b2d4b1c40e3673bbccb5423f7b37dcf9a9c;
bytes32 constant ADDR_LABEL = 0xe5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a;

function setUp() public {
uint256 constant LAUNCH_AUCTION_START_PRICE = 100 ether;
uint256 constant LAUNCH_AUCTION_DURATION_HOURS = 36;
uint256 constant LAUNCH_TIME = 1723420800; // Monday, August 12, 2024 12:00:00 AM GMT

uint256 constant EXPIRY_AUCTION_START_PRICE = 1000 ether;
uint256 constant EXPIRY_AUCTION_DURATION_DAYS = 21;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevieraykatz do the deploy scripts need to be updated? DeployPriceOracle still uses 28 days there, 21 days here.

I'm currently having these assumptions, can you confirm if they're true for the actual deployment?

  1. the base prices will be the same for both oracles.
  2. the prod oracle will replace the launch oracle without having a sudden change in the rentPrice. Practically, this means when both premiums match, I assume you do this when both premiums are 0 after the max of both auction duration times (21 days). Also need to do this before you can re-register an account, before MIN_REGISTRATION_DURATION + GRACE_PERIOD

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that script is outdated. We use internal tooling to deploy. Exp. premium price oracle will use 21-day duration.

Copy link
Collaborator Author

@stevieraykatz stevieraykatz Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Base prices will be the same for both price oracles
  2. Yes! See integration test for expected switch over flow

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, makes sense to me then


function setUp() public virtual {
owner = makeAddr("owner");
signer = makeAddr("signer");
alice = makeAddr("alice");
Expand All @@ -61,32 +70,23 @@ contract IntegrationTest is Test {
registry = new Registry(owner);
reverseRegistrar = new ReverseRegistrar(registry, owner, BASE_REVERSE_NODE);

uint256[] memory rentPrices = new uint256[](6);
rentPrices[0] = 317_097_919_837;
rentPrices[1] = 31_709_791_983;
rentPrices[2] = 3_170_979_198;
rentPrices[3] = 317_097_919;
rentPrices[4] = 31_709_791;
rentPrices[5] = 3_170_979; //3,170,979.1983764587 = 1e14 / (365 * 24 * 3600)

exponentialPremiumPriceOracle = new ExponentialPremiumPriceOracle(rentPrices, 1e18, 21);
launchAuctionPriceOracle =
new LaunchAuctionPriceOracle(_getBasePrices(), LAUNCH_AUCTION_START_PRICE, LAUNCH_AUCTION_DURATION_HOURS);

baseRegistrar = new BaseRegistrar(registry, owner, BASE_ETH_NODE, "", "");

_establishNamespaces();

registrarController = new RegistrarController(
baseRegistrar,
exponentialPremiumPriceOracle,
IPriceOracle(launchAuctionPriceOracle),
IReverseRegistrar(address(reverseRegistrar)),
owner,
BASE_ETH_NODE,
".base.eth",
payments
);

vm.prank(owner);
baseRegistrar.addController(address(registrarController));

vm.prank(owner);
reverseRegistrar.setControllerApproval(address(registrarController), true);

Expand All @@ -98,7 +98,12 @@ contract IntegrationTest is Test {
vm.prank(owner);
reverseRegistrar.setName("rootOwner");

vm.warp(GRACE_PERIOD * 10);
vm.prank(owner);
baseRegistrar.addController(address(registrarController));

vm.prank(owner);
registrarController.setLaunchTime(LAUNCH_TIME);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevieraykatz what's the process for assuring LAUNCH_TIME is not set in the future relative to block.timestamp?

Also at launch maybe us a multicall approach or set the launch time on the registrarController before adding it to the baseRegistrar as a controller for any spam transactions trying to lodge them in between both (not sure if this is possible with the sequencer)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're unable to use multicall due to the ownership restrictions on both methods. We're going to set the launchTime to say 12:00PST and then execute the TX at 12:00 so we'll miss the first couple seconds of the auction but it won't be in the future.

vm.warp(LAUNCH_TIME);
}

function _establishNamespaces() internal {
Expand Down Expand Up @@ -134,4 +139,15 @@ contract IntegrationTest is Test {

registrarController.register{value: registerPrice}(request);
}

function _getBasePrices() internal pure returns (uint256[] memory) {
uint256[] memory rentPrices = new uint256[](6);
rentPrices[0] = 316_808_781_402;
rentPrices[1] = 31_680_878_140;
rentPrices[2] = 3_168_087_814;
rentPrices[3] = 316_808_781;
rentPrices[4] = 31_680_878;
rentPrices[5] = 3_168_087; // 3,168,808.781402895 = 1e14 / (365.25 * 24 * 3600)
return rentPrices;
}
}
74 changes: 74 additions & 0 deletions test/Integration/LaunchAuctionRegistrations.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console} from "forge-std/Test.sol";
import {IntegrationTestBase} from "./IntegrationTestBase.t.sol";
import {RegistrarController} from "src/L2/RegistrarController.sol";

contract LaunchAuctionRegistrations is IntegrationTestBase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevieraykatz Thanks so much for these. Was thinking it could help to see (lmk if I missed)

  1. How the price of an unclaimed name changes before and after switching the oracles
  2. How a auction price changes before and after oracles (I know we only have 28 day registration so this is impossible, but if it's easy to test it would be interesting to see)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I added a couple lines to test test_simulatePostAuctionConfig_register which asserts that the price for a name is the same before/after the oracle gets switched. See:
        // Get price before oracle changes
        uint256 priceBeforeSwitch = registrarController.registerPrice(name, duration);
        
        ...
        
        assertEq(priceBeforeSwitch, price);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Added new test to demonstrate the case you're interested in test_showAuctionPriceDifference. Output at the beginning of the auction:
Logs:
  Price with launch auction:  100000993812011095261
  Price with prod auction:  1000000522935317369675

output if we jump 1 day into the auction:

Logs:
  Price with launch auction:  2519690917345161
  Price with prod auction:  500000522935317368675

Basically we get what we expect. The price is 100 ether for the launch auction and 1000 ether with the prod auction contract at t = expiry + GRACE_PERIOD. At t = expiry + GRACE_PERIOD + 1 day we get a much lower launch auction price (because it decays so rapidly) and a halved prod auction (1-day half life).

string name = "alice";
uint256 duration = 365.25 days;
uint256 id = uint256(keccak256(bytes(name)));

function test_register_at_LaunchTime() public {
vm.warp(LAUNCH_TIME);
_register("launch time");
}

function test_register_oneHourAfter_LaunchTime() public {
vm.warp(LAUNCH_TIME + 1 hours);
_register("launch + 1 hour");
}

function test_register_threeHoursAfter_LaunchTime() public {
vm.warp(LAUNCH_TIME + 3 hours);
_register("launch + 3 hours");
}

function test_register_oneDayAfter_LaunchTime() public {
vm.warp(LAUNCH_TIME + 1 days);
_register("launch + 1 day");
}

function test_register_justBefore_launchAuctionEnds() public {
vm.warp(LAUNCH_TIME + (LAUNCH_AUCTION_DURATION_HOURS * 1 hours) - 1);
_register("1 second before auction ends");
}

function test_register_justAfter_launchAuctionEnds() public {
vm.warp(LAUNCH_TIME + (LAUNCH_AUCTION_DURATION_HOURS * 1 hours));
_register("auction end time");
}

function test_register_oneDayAfter_launchAuctionEnds() public {
vm.warp(LAUNCH_TIME + (LAUNCH_AUCTION_DURATION_HOURS * 1 hours) + 1 days);
_register("1 day after auction ends");
}

function _register(string memory logCase) internal {
uint256 price = registrarController.registerPrice(name, duration);
vm.deal(alice, price);

RegistrarController.RegisterRequest memory request = RegistrarController.RegisterRequest({
name: name,
owner: alice,
duration: duration,
resolver: address(defaultL2Resolver),
data: new bytes[](0),
reverseRecord: true
});

vm.expectEmit(address(registrarController));
emit RegistrarController.ETHPaymentProcessed(alice, price);

vm.prank(alice);
registrarController.register{value: price}(request);
assertEq(baseRegistrar.ownerOf(id), alice);

console.log("______________________________");
console.log("Registering at", logCase);
console.log("Timestamp: ", block.timestamp);
console.log("Price (WEI): ", price);
console.log("______________________________");
}
}
101 changes: 101 additions & 0 deletions test/Integration/PostLaunchAuctionConfig.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console} from "forge-std/Test.sol";
import {IntegrationTestBase} from "./IntegrationTestBase.t.sol";
import {RegistrarController} from "src/L2/RegistrarController.sol";
import {ExponentialPremiumPriceOracle} from "src/L2/ExponentialPremiumPriceOracle.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";

contract PostLaunchAuctionConfig is IntegrationTestBase {
string name = "alice";
uint256 duration = 365.25 days;
uint256 id = uint256(keccak256(bytes(name)));

function test_simulatePostAuctionConfig_register() public {
// Deploy original price oracle, we will do this ahead of launch
exponentialPremiumPriceOracle = new ExponentialPremiumPriceOracle(
_getBasePrices(), EXPIRY_AUCTION_START_PRICE, EXPIRY_AUCTION_DURATION_DAYS
);

// Jump forward 30 days
vm.warp(LAUNCH_TIME + 30 days);

// Set new price oracle on registrar controller (ownerOnly method)
vm.prank(owner);
registrarController.setPriceOracle(IPriceOracle(exponentialPremiumPriceOracle));

//// Register a name and check price + success
uint256 price = registrarController.registerPrice(name, duration);
vm.deal(alice, price);

RegistrarController.RegisterRequest memory request = RegistrarController.RegisterRequest({
name: name,
owner: alice,
duration: duration,
resolver: address(defaultL2Resolver),
data: new bytes[](0),
reverseRecord: true
});

vm.expectEmit(address(registrarController));
emit RegistrarController.ETHPaymentProcessed(alice, price);

vm.prank(alice);
registrarController.register{value: price}(request);
assertEq(baseRegistrar.ownerOf(id), alice);

uint256 expectedPrice = exponentialPremiumPriceOracle.price5Letter() * duration;
assertEq(price, expectedPrice);

console.log("______________________________");
console.log("Timestamp: ", block.timestamp);
console.log("Price (WEI): ", price);
console.log("______________________________");
}

function test_simulateContingency() public {
// Deploy original price oracle, we will do this ahead of launch
exponentialPremiumPriceOracle = new ExponentialPremiumPriceOracle(
_getBasePrices(), EXPIRY_AUCTION_START_PRICE, EXPIRY_AUCTION_DURATION_DAYS
);

// Jump forward 1 day
vm.warp(LAUNCH_TIME + 1 days);

// Set the launch time to 0 (no-op for launch pricing)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does no-op for launch pricing mean? Does setting launch time to 0 effectively kill the ability for people to register?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah bad comment. RegistrarController "launchTime" is used as a replacement for expiry on new names. This is what enables the launch auction (when normally names would only get an auction after expiring). I'll fix the comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed comment.

vm.prank(owner);
registrarController.setLaunchTime(0);
// Set new price oracle on registrar controller (ownerOnly method)
vm.prank(owner);
registrarController.setPriceOracle(IPriceOracle(exponentialPremiumPriceOracle));

//// Register a name and check price + success
uint256 price = registrarController.registerPrice(name, duration);
vm.deal(alice, price);

RegistrarController.RegisterRequest memory request = RegistrarController.RegisterRequest({
name: name,
owner: alice,
duration: duration,
resolver: address(defaultL2Resolver),
data: new bytes[](0),
reverseRecord: true
});

vm.expectEmit(address(registrarController));
emit RegistrarController.ETHPaymentProcessed(alice, price);

vm.prank(alice);
registrarController.register{value: price}(request);
assertEq(baseRegistrar.ownerOf(id), alice);

uint256 expectedPrice = exponentialPremiumPriceOracle.price5Letter() * duration;
assertEq(price, expectedPrice);

console.log("______________________________");
console.log("Timestamp: ", block.timestamp);
console.log("Price (WEI): ", price);
console.log("______________________________");
}
}
Loading