Skip to content

Commit

Permalink
CDAuctioneer: tests for activate/deactivate/setAuctionParameters, fix…
Browse files Browse the repository at this point in the history
… issues
  • Loading branch information
0xJem committed Jan 2, 2025
1 parent 31162f8 commit 7826525
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 44 deletions.
73 changes: 69 additions & 4 deletions src/policies/CDAuctioneer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -289,16 +289,33 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
// ========== ADMIN FUNCTIONS ========== //

/// @inheritdoc IConvertibleDepositAuctioneer
/// @dev This function is gated to the ROLE_HEART role
/// @dev This function performs the following:
/// - TODO
///
/// This function reverts if:
/// - The caller does not have the ROLE_HEART role
/// - The new tick size is 0
/// - The new min price is 0
///
/// @param newTarget The new target for OHM sold per day
/// @param newSize The new tick size
/// @param newMinPrice The new minimum price
/// @return remainder The remainder of OHM that cannot be sold
function setAuctionParameters(
uint256 newTarget,
uint256 newSize,
uint256 newMinPrice
) external override onlyRole(ROLE_HEART) returns (uint256 remainder) {
// Tick size must be non-zero
if (newSize == 0) revert CDAuctioneer_InvalidParams("tick size");

// Min price must be non-zero
if (newMinPrice == 0) revert CDAuctioneer_InvalidParams("min price");

// TODO should this be newTarget instead of state.target?
// TODO Should the newTarget - dayState.convertible be used instead?
// TODO how to handle if deactivated?
remainder = (state.target > dayState.convertible) ? state.target - dayState.convertible : 0;
// remainder = (state.target > dayState.convertible) ? state.target - dayState.convertible : 0;

state = State(
newTarget,
Expand All @@ -308,30 +325,78 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
state.lastUpdate,
state.timeToExpiry
);

// If this is the first run, capacity and price must be set
// We know that this is the first run, since min price cannot be 0, and bidding will not result in a tick price below the min price
if (currentTick.price == 0) {
currentTick.capacity = newSize;
currentTick.price = newMinPrice;
}

// Emit event
emit AuctionParametersUpdated(newTarget, newSize, newMinPrice);
}

/// @inheritdoc IConvertibleDepositAuctioneer
/// @dev This function is gated to the ROLE_ADMIN role
/// @dev This function will revert if:
/// - The caller does not have the ROLE_ADMIN role
/// - The new time to expiry is 0
///
/// @param newTime The new time to expiry
function setTimeToExpiry(uint48 newTime) external override onlyRole(ROLE_ADMIN) {
// Value must be non-zero
if (newTime == 0) revert CDAuctioneer_InvalidParams("time to expiry");

state.timeToExpiry = newTime;

// Emit event
emit TimeToExpiryUpdated(newTime);
}

/// @inheritdoc IConvertibleDepositAuctioneer
/// @dev This function is gated to the ROLE_ADMIN role
/// @dev This function will revert if:
/// - The caller does not have the ROLE_ADMIN role
/// - The new tick step is 0
///
/// @param newStep The new tick step
function setTickStep(uint256 newStep) external override onlyRole(ROLE_ADMIN) {
// Value must be non-zero
if (newStep == 0) revert CDAuctioneer_InvalidParams("tick step");

state.tickStep = newStep;

// Emit event
emit TickStepUpdated(newStep);
}

/// @notice Activate the contract functionality
/// @dev This function is gated to the ROLE_EMERGENCY_SHUTDOWN role
function activate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) {
// If the contract is already active, do nothing
if (locallyActive) return;

// Set the contract to active
locallyActive = true;

// Also set the lastUpdate to the current block timestamp
// Otherwise, getUpdatedTick() will calculate a long period of time having passed
state.lastUpdate = uint48(block.timestamp);

// Emit event
emit Activated();
}

/// @notice Deactivate the contract functionality
/// @dev This function is gated to the ROLE_EMERGENCY_SHUTDOWN role
function deactivate() external onlyRole(ROLE_EMERGENCY_SHUTDOWN) {
// If the contract is already inactive, do nothing
if (!locallyActive) return;

// Set the contract to inactive
locallyActive = false;

// Emit event
emit Deactivated();
}

// ========== MODIFIERS ========== //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {OlympusConvertibleDepository} from "src/modules/CDEPO/OlympusConvertible
import {OlympusConvertibleDepositPositions} from "src/modules/CDPOS/OlympusConvertibleDepositPositions.sol";
import {RolesAdmin} from "src/policies/RolesAdmin.sol";
import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";
import {IConvertibleDepositAuctioneer} from "src/policies/interfaces/IConvertibleDepositAuctioneer.sol";

// solhint-disable max-states-count
contract ConvertibleDepositAuctioneerTest is Test {
Expand All @@ -33,7 +34,6 @@ contract ConvertibleDepositAuctioneerTest is Test {
MockERC4626 public vault;

address public recipient = address(0x1);
address public recipientTwo = address(0x2);
address public heart = address(0x3);
address public admin = address(0x4);
address public emergency = address(0x5);
Expand All @@ -47,7 +47,7 @@ contract ConvertibleDepositAuctioneerTest is Test {
uint256 public constant TICK_STEP = 9e17; // 90%
uint256 public constant MIN_PRICE = 15e18;
uint256 public constant TARGET = 20e9;
uint256 public constant TIME_TO_EXPIRY = 1 days;
uint48 public constant TIME_TO_EXPIRY = 1 days;

function setUp() public {
vm.warp(INITIAL_BLOCK);
Expand Down Expand Up @@ -99,12 +99,19 @@ contract ConvertibleDepositAuctioneerTest is Test {
) internal {
IConvertibleDepositAuctioneer.State memory state = auctioneer.getState();

assertEq(state.target, target_);
assertEq(state.tickSize, tickSize_);
assertEq(state.minPrice, minPrice_);
assertEq(state.tickStep, tickStep_);
assertEq(state.timeToExpiry, timeToExpiry_);
assertEq(state.lastUpdate, lastUpdate_);
assertEq(state.target, target_, "target");
assertEq(state.tickSize, tickSize_, "tickSize");
assertEq(state.minPrice, minPrice_, "minPrice");
assertEq(state.tickStep, tickStep_, "tickStep");
assertEq(state.timeToExpiry, timeToExpiry_, "timeToExpiry");
assertEq(state.lastUpdate, lastUpdate_, "lastUpdate");
}

function _assertCurrentTick(uint256 capacity_, uint256 price_) internal {
IConvertibleDepositAuctioneer.Tick memory tick = auctioneer.getCurrentTick();

assertEq(tick.capacity, capacity_, "capacity");
assertEq(tick.price, price_, "price");
}

// ========== MODIFIERS ========== //
Expand All @@ -121,18 +128,30 @@ contract ConvertibleDepositAuctioneerTest is Test {
_;
}

modifier givenAddressHasReserveToken(address to_, uint256 amount_) {
function _mintReserveToken(address to_, uint256 amount_) internal {
reserveToken.mint(to_, amount_);
}

modifier givenAddressHasReserveToken(address to_, uint256 amount_) {
_mintReserveToken(to_, amount_);
_;
}

modifier givenReserveTokenSpendingIsApproved(
function _approveReserveTokenSpending(
address owner_,
address spender_,
uint256 amount_
) {
) internal {
vm.prank(owner_);
reserveToken.approve(spender_, amount_);
}

modifier givenReserveTokenSpendingIsApproved(
address owner_,
address spender_,
uint256 amount_
) {
_approveReserveTokenSpending(owner_, spender_, amount_);
_;
}

Expand Down Expand Up @@ -176,4 +195,21 @@ contract ConvertibleDepositAuctioneerTest is Test {
_setAuctionParameters(target_, tickSize_, minPrice_);
_;
}

function _bid(address owner_, uint256 deposit_) internal {
vm.prank(owner_);
auctioneer.bid(deposit_);
}

modifier givenRecipientHasBid(uint256 deposit_) {
// Mint
_mintReserveToken(recipient, deposit_);

// Approve spending
_approveReserveTokenSpending(recipient, address(auctioneer), deposit_);

// Bid
_bid(recipient, deposit_);
_;
}
}
19 changes: 17 additions & 2 deletions src/test/policies/ConvertibleDepositAuctioneer/activate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ contract ConvertibleDepositAuctioneerActivateTest is ConvertibleDepositAuctionee
// when the contract is already activated
// [X] the state is unchanged
// [X] it does not emit an event
// [ ] it does not change the last update
// [X] it does not change the last update
// when the contract is not activated
// [X] it activates the contract
// [X] it emits an event
// [ ] it sets the last update to the current block timestamp
// [X] it sets the last update to the current block timestamp

function test_callerDoesNotHaveEmergencyShutdownRole_reverts(address caller_) public {
// Ensure caller is not emergency address
Expand All @@ -30,15 +30,28 @@ contract ConvertibleDepositAuctioneerActivateTest is ConvertibleDepositAuctionee
}

function test_contractActivated() public givenContractActive {
uint48 lastUpdate = uint48(block.timestamp);

// Warp to change the block timestamp
vm.warp(lastUpdate + 1);

// Call function
vm.prank(emergency);
auctioneer.activate();

// Assert state
assertEq(auctioneer.locallyActive(), true);
// lastUpdate has not changed
assertEq(auctioneer.getState().lastUpdate, lastUpdate);
}

function test_contractInactive() public givenContractInactive {
uint48 lastUpdate = uint48(block.timestamp);
uint48 newBlock = lastUpdate + 1;

// Warp to change the block timestamp
vm.warp(newBlock);

// Expect event
vm.expectEmit(true, true, true, true);
emit Activated();
Expand All @@ -49,5 +62,7 @@ contract ConvertibleDepositAuctioneerActivateTest is ConvertibleDepositAuctionee

// Assert state
assertEq(auctioneer.locallyActive(), true);
// lastUpdate has changed
assertEq(auctioneer.getState().lastUpdate, newBlock);
}
}
16 changes: 15 additions & 1 deletion src/test/policies/ConvertibleDepositAuctioneer/deactivate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,28 @@ contract ConvertibleDepositAuctioneerDeactivateTest is ConvertibleDepositAuction
auctioneer.deactivate();
}

function test_contractInactive() public givenContractInactive {
function test_contractInactive() public givenContractActive givenContractInactive {
uint48 lastUpdate = uint48(block.timestamp);

// Warp to change the block timestamp
vm.warp(lastUpdate + 1);

// Call function
vm.prank(emergency);
auctioneer.deactivate();

// Assert state
assertEq(auctioneer.locallyActive(), false);
// lastUpdate has not changed
assertEq(auctioneer.getState().lastUpdate, lastUpdate);
}

function test_contractActive() public givenContractActive {
uint48 lastUpdate = uint48(block.timestamp);

// Warp to change the block timestamp
vm.warp(lastUpdate + 1);

// Expect event
vm.expectEmit(true, true, true, true);
emit Deactivated();
Expand All @@ -47,5 +59,7 @@ contract ConvertibleDepositAuctioneerDeactivateTest is ConvertibleDepositAuction

// Assert state
assertEq(auctioneer.locallyActive(), false);
// lastUpdate has not changed
assertEq(auctioneer.getState().lastUpdate, lastUpdate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ contract ConvertibleDepositAuctioneerUpdatedTickTest is ConvertibleDepositAuctio
// given the initial auction parameters have not been set
// [X] it reverts
// given the contract is inactive
// [ ] it reverts
// [X] it reverts
// given a bid has never been received
// [ ] it calculates the new capacity based on the time since contact activation
// given less than 1 day has passed
Expand Down Expand Up @@ -44,4 +44,49 @@ contract ConvertibleDepositAuctioneerUpdatedTickTest is ConvertibleDepositAuctio
// Call function
auctioneer.getUpdatedTick();
}

function test_contractInactive_reverts() public givenContractInactive {
// Expect revert
vm.expectRevert(IConvertibleDepositAuctioneer.CDAuctioneer_NotActive.selector);

// Call function
auctioneer.getUpdatedTick();
}

function test_noBidReceived()
public
givenContractActive
givenTickStep(TICK_STEP)
givenTimeToExpiry(TIME_TO_EXPIRY)
givenAuctionParametersStandard
{
uint48 daysPassed = 2 days;

// Warp to change the block timestamp
vm.warp(block.timestamp + daysPassed);

// Expected values
// Tick size = 10e9
// Current tick capacity = tick size = 10e9
// Current tick price =
// New capacity added = target * days passed = 20e9 * 2 = 40e9
// New capacity = 10e9 + 40e9 = 50e9
// Iteration 1:
// New capacity = 50e9 - 10e9 = 40e9
// Tick price = 10e9 * 1e18 / 9e17 = 10e9
// Iteration 2:
// New capacity = 40e9 - 10e9 = 30e9
// Tick price = 10e9 * 9e17 / 9e17 = 10e9
// Iteration 3:
// New capacity = 30e9 - 10e9 = 20e9
// Tick price = 10e9 * 9e17 / 9e17 = 10e9
// uint256 expectedCapacity = TARGET + TARGET * 2;
// uint256 expectedPrice = expectedCapacity - TICK_SIZE

// Call function
IConvertibleDepositAuctioneer.Tick memory tick = auctioneer.getUpdatedTick();

// Assertions
assertEq(tick.capacity, TICK_SIZE);
}
}
Loading

0 comments on commit 7826525

Please sign in to comment.