Skip to content

Commit

Permalink
CDAuctioneer: WIP bid() tests
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJem committed Jan 6, 2025
1 parent 3bc3259 commit 5c8ec9d
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 22 deletions.
19 changes: 12 additions & 7 deletions src/policies/CDAuctioneer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
uint256 currentTickPrice;
(currentTickCapacity, currentTickPrice, ohmOut) = _previewBid(deposit, _previousTick);

// Reject if the OHM out is 0
if (ohmOut == 0) revert CDAuctioneer_InvalidParams("converted amount");

// Reset the day state if this is the first bid of the day
if (block.timestamp / 86400 > state.lastUpdate / 86400) {
dayState = Day(0, 0);
Expand Down Expand Up @@ -260,11 +263,14 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
}

/// @inheritdoc IConvertibleDepositAuctioneer
/// @dev This function calculates the tick at the current time.
///
/// @return tick The updated tick
/// It uses the following approach:
/// - Calculate the added capacity based on the time passed since the last bid, and add it to the current capacity to get the new capacity
/// - Until the new capacity is <= to the tick size, reduce the capacity by the tick size and reduce the price by the tick step
/// - If the calculated price is ever lower than the minimum price, the new price is set to the minimum price and the capacity is set to the tick size
function getCurrentTick() public view onlyActive returns (Tick memory tick) {
// TODO document approach
// find amount of time passed and new capacity to add
// Find amount of time passed and new capacity to add
uint256 timePassed = block.timestamp - state.lastUpdate;
uint256 capacityToAdd = (state.target * timePassed) / 1 days;

Expand All @@ -274,7 +280,6 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
tick = _previousTick;
uint256 newCapacity = tick.capacity + capacityToAdd;


// Iterate over the ticks until the capacity is within the tick size
// This is the opposite of what happens in the bid function
while (newCapacity > state.tickSize) {
Expand All @@ -284,16 +289,16 @@ contract CDAuctioneer is IConvertibleDepositAuctioneer, Policy, RolesConsumer, R
// Adjust the tick price by the tick step, in the opposite direction to the bid function
tick.price = tick.price.mulDivUp(ONE_HUNDRED_PERCENT, _tickStep);

// tick price does not go below the minimum
// tick capacity is full if the min price is exceeded
// Tick price does not go below the minimum
// Tick capacity is full if the min price is exceeded
if (tick.price < state.minPrice) {
tick.price = state.minPrice;
newCapacity = state.tickSize;
break;
}
}

// decrement capacity by remainder
// Set the capacity
tick.capacity = newCapacity;

return tick;
Expand Down
269 changes: 254 additions & 15 deletions src/test/policies/ConvertibleDepositAuctioneer/bid.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,39 @@
pragma solidity 0.8.15;

import {ConvertibleDepositAuctioneerTest} from "./ConvertibleDepositAuctioneerTest.sol";
import {IConvertibleDepositAuctioneer} from "src/policies/interfaces/IConvertibleDepositAuctioneer.sol";

contract ConvertibleDepositAuctioneerBidTest is ConvertibleDepositAuctioneerTest {
// when the contract is deactivated
// [ ] it reverts
// [X] it reverts
// when the contract has not been initialized
// [ ] it reverts
// [X] it reverts
// when the caller has not approved CDEPO to spend the bid token
// [ ] it reverts
// [X] it reverts
// when the "cd_auctioneer" role is not granted to the auctioneer contract
// [ ] it reverts
// [X] it reverts
// when the bid amount converted is 0
// [ ] it reverts
// when the tick price is below the minimum price
// [ ] it does not go below the minimum price
// [X] it reverts
// when the bid is the first bid
// [X] it sets the day's deposit balance
// [X] it sets the day's converted balance
// [X] it sets the lastUpdate to the current block timestamp
// [X] it deducts the converted amount from the tick capacity
// [X] it does not update the tick price
// when the bid is the first bid of the day
// when the convertible amount of OHM will exceed the day target
// [ ] it returns the amount of OHM that can be converted at the current tick price to fill but not exceed the target
// [ ] it resets the day's deposit and converted balances
// [ ] it updates the day's deposit balance
// [ ] it updates the day's converted balance
// [ ] it sets the lastUpdate to the current block timestamp
// [X] it resets the day's deposit and converted balances
// [X] it updates the day's deposit balance
// [X] it updates the day's converted balance
// [X] it sets the lastUpdate to the current block timestamp
// when the bid is not the first bid of the day
// when the convertible amount of OHM will exceed the day target
// [ ] it returns the amount of OHM that can be converted at the current tick price to fill but not exceed the target
// [ ] it does not reset the day's deposit and converted balances
// [ ] it updates the day's deposit balance
// [ ] it updates the day's converted balance
// [ ] it sets the lastUpdate to the current block timestamp
// [X] it does not reset the day's deposit and converted balances
// [X] it updates the day's deposit balance
// [X] it updates the day's converted balance
// [X] it sets the lastUpdate to the current block timestamp
// when the bid amount converted is less than the remaining tick capacity
// when the calculated deposit amount is 0
// [ ] it completes bidding and leaves a remainder of the bid token
Expand Down Expand Up @@ -96,4 +101,238 @@ contract ConvertibleDepositAuctioneerBidTest is ConvertibleDepositAuctioneerTest
// [ ] it updates the tick capacity to the tick size minus the converted amount at the new tick price
// [ ] the tick price is unchanged
// [ ] it sets the lastUpdate to the current block timestamp

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

// Call function
auctioneer.bid(1e18);
}

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

// Call function
auctioneer.bid(1e18);
}

function test_givenSpendingNotApproved_reverts()
public
givenInitialized
givenContractActive
givenAddressHasReserveToken(recipient, 1e18)
{
// Expect revert
vm.expectRevert("TRANSFER_FROM_FAILED");

// Call function
vm.prank(recipient);
auctioneer.bid(1e18);
}

function test_givenAuctioneerRoleNotGranted_reverts()
public
givenInitialized
givenContractActive
givenAddressHasReserveToken(recipient, 1e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 1e18)
{
// Revoke the auctioneer role
rolesAdmin.revokeRole("cd_auctioneer", address(auctioneer));

// Expect revert
_expectRoleRevert("cd_auctioneer");

// Call function
vm.prank(recipient);
auctioneer.bid(1e18);
}

function test_givenBidAmountConvertedIsZero_reverts(
uint256 bidAmount_
)
public
givenInitialized
givenContractActive
givenAddressHasReserveToken(recipient, 1e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 1e18)
{
// We want a bid amount that will result in a converted amount of 0
// Given bid amount * 1e9 / 15e18 = converted amount
// When bid amount = 15e9, the converted amount = 1
uint256 bidAmount = bound(bidAmount_, 0, 15e9 - 1);

// Expect revert
vm.expectRevert(
abi.encodeWithSelector(
IConvertibleDepositAuctioneer.CDAuctioneer_InvalidParams.selector,
"converted amount"
)
);

// Call function
vm.prank(recipient);
auctioneer.bid(bidAmount);
}

function test_givenBidAmountConvertedIsAboveZero(
uint256 bidAmount_
)
public
givenInitialized
givenContractActive
givenAddressHasReserveToken(recipient, 1e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 1e18)
{
// We want a bid amount that will result in a converted amount of 0
// Given bid amount * 1e9 / 15e18 = converted amount
// When bid amount = 15e9, the converted amount = 1
uint256 bidAmount = bound(bidAmount_, 15e9, 1e18);

// Calculate the expected converted amount
uint256 expectedConvertedAmount = (bidAmount * 1e9) / 15e18;

// Check preview
(uint256 previewOhmOut, ) = auctioneer.previewBid(bidAmount);

// Assert that the preview is as expected
assertEq(previewOhmOut, expectedConvertedAmount, "preview converted amount");

// Call function
vm.prank(recipient);
uint256 ohmOut = auctioneer.bid(bidAmount);

// Assert that the converted amount is as expected
assertEq(ohmOut, expectedConvertedAmount, "converted amount");
}

function test_givenFirstBid()
public
givenInitialized
givenAddressHasReserveToken(recipient, 3e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 3e18)
{
// Expected converted amount
// 3e18 * 1e9 / 15e18 = 2e8
uint256 bidAmount = 3e18;
uint256 expectedConvertedAmount = 2e8;

// Check preview
(uint256 previewOhmOut, ) = auctioneer.previewBid(bidAmount);

// Assert that the preview is as expected
assertEq(previewOhmOut, expectedConvertedAmount, "preview converted amount");

// Call function
vm.prank(recipient);
uint256 ohmOut = auctioneer.bid(bidAmount);

// Assert that the converted amount is as expected
assertEq(ohmOut, expectedConvertedAmount, "converted amount");

// Assert the day state
assertEq(auctioneer.getDayState().deposits, bidAmount, "day deposits");
assertEq(auctioneer.getDayState().convertible, expectedConvertedAmount, "day convertible");

// Assert the state
_assertState(TARGET, TICK_SIZE, MIN_PRICE, uint48(block.timestamp));

// Assert the tick
_assertPreviousTick(TICK_SIZE - expectedConvertedAmount, MIN_PRICE);
}

function test_givenFirstBidOfDay()
public
givenInitialized
givenRecipientHasBid(120e18)
givenAddressHasReserveToken(recipient, 6e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 6e18)
{
// Warp to the next day
uint48 nextDay = uint48(block.timestamp) + 1 days;
vm.warp(nextDay);

// Get the current tick for the new day
IConvertibleDepositAuctioneer.Tick memory beforeTick = auctioneer.getCurrentTick();

// Expected converted amount
// 6e18 * 1e9 / 15e18 = 4e8
uint256 bidAmount = 6e18;
uint256 expectedConvertedAmount = 4e8;

// Check preview
(uint256 previewOhmOut, ) = auctioneer.previewBid(bidAmount);

// Assert that the preview is as expected
assertEq(previewOhmOut, expectedConvertedAmount, "preview converted amount");

// Call function
vm.prank(recipient);
uint256 ohmOut = auctioneer.bid(bidAmount);

// Assert that the converted amount is as expected
assertEq(ohmOut, expectedConvertedAmount, "converted amount");

// Assert the day state
// Not affected by the previous day's bid
assertEq(auctioneer.getDayState().deposits, bidAmount, "day deposits");
assertEq(auctioneer.getDayState().convertible, expectedConvertedAmount, "day convertible");

// Assert the state
_assertState(TARGET, TICK_SIZE, MIN_PRICE, uint48(nextDay));

// Assert the tick
_assertPreviousTick(beforeTick.capacity - expectedConvertedAmount, beforeTick.price);
}

function test_secondBidUpdatesDayState()
public
givenInitialized
givenRecipientHasBid(3e18)
givenAddressHasReserveToken(recipient, 6e18)
givenReserveTokenSpendingIsApproved(recipient, address(convertibleDepository), 6e18)
{
// Previous converted amount
// 3e18 * 1e9 / 15e18 = 2e8
uint256 previousBidAmount = 3e18;
uint256 previousConvertedAmount = 2e8;

// Expected converted amount
// 6e18 * 1e9 / 15e18 = 4e8
uint256 bidAmount = 6e18;
uint256 expectedConvertedAmount = 4e8;

// Check preview
(uint256 previewOhmOut, ) = auctioneer.previewBid(bidAmount);

// Assert that the preview is as expected
assertEq(previewOhmOut, expectedConvertedAmount, "preview converted amount");

// Call function
vm.prank(recipient);
uint256 ohmOut = auctioneer.bid(bidAmount);

// Assert that the converted amount is as expected
assertEq(ohmOut, expectedConvertedAmount, "converted amount");

// Assert the day state
// Not affected by the previous day's bid
assertEq(auctioneer.getDayState().deposits, previousBidAmount + bidAmount, "day deposits");
assertEq(
auctioneer.getDayState().convertible,
previousConvertedAmount + expectedConvertedAmount,
"day convertible"
);

// Assert the state
_assertState(TARGET, TICK_SIZE, MIN_PRICE, uint48(block.timestamp));

// Assert the tick
_assertPreviousTick(
TICK_SIZE - previousConvertedAmount - expectedConvertedAmount,
MIN_PRICE
);
}
}

0 comments on commit 5c8ec9d

Please sign in to comment.