Skip to content

Commit

Permalink
Added int offset
Browse files Browse the repository at this point in the history
  • Loading branch information
yahgwai committed Aug 15, 2024
1 parent d9c8dc9 commit 5f288d7
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 101 deletions.
3 changes: 2 additions & 1 deletion src/express-lane-auction/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ error NotTransferor(uint64 round, address expectedTransferor, address msgSender)
error InvalidNewRound(uint64 currentRound, uint64 newRound);
error InvalidNewStart(uint64 currentStart, uint64 newStart);
error RoundTooLong(uint64 roundDurationSeconds);
error ZeroAuctionClosingSeconds();
error ZeroAuctionClosingSeconds();
error NegativeOffset();
4 changes: 4 additions & 0 deletions src/express-lane-auction/ExpressLaneAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ contract ExpressLaneAuction is
reservePrice = args._minReservePrice;
emit SetReservePrice(0, args._minReservePrice);

// the initial timestamp cannot be negative
if (args._roundTimingInfo.offsetTimestamp < 0) {
revert NegativeOffset();
}
setRoundTimingInfoInternal(args._roundTimingInfo);

// roles without a custom role admin set will have this as the admin
Expand Down
4 changes: 2 additions & 2 deletions src/express-lane-auction/IExpressLaneAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg
/// @param reserveSubmissionSeconds The new reserve submission seconds
event SetRoundTimingInfo(
uint64 currentRound,
uint64 offsetTimestamp,
int64 offsetTimestamp,
uint64 roundDurationSeconds,
uint64 auctionClosingSeconds,
uint64 reserveSubmissionSeconds
Expand Down Expand Up @@ -223,7 +223,7 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg
external
view
returns (
uint64 offsetTimestamp,
int64 offsetTimestamp,
uint64 roundDurationSeconds,
uint64 auctionClosingSeconds,
uint64 reserveSubmissionSeconds
Expand Down
44 changes: 28 additions & 16 deletions src/express-lane-auction/RoundTimingInfo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ pragma solidity ^0.8.0;
/// after which the auctioneer can submit the two highest bids to the auction contract to resolve the auction
struct RoundTimingInfo {
/// @notice The timestamp when round 0 starts
uint64 offsetTimestamp;
/// @notice The total duration (in seconds) of the round
/// We allow this to be negative so that later when setting new round timing info
/// we can use offsets very far in the past. This combined with the maxium that we allow
/// on round duration ensures that we can always set a new round duration within the possible range
int64 offsetTimestamp;
/// @notice The total duration (in seconds) of the round. Always less than 86400 and greater than 0
uint64 roundDurationSeconds;
/// @notice The number of seconds before the end of the round that the auction round closes
/// @notice The number of seconds before the end of the round that the auction round closes. Cannot be 0
uint64 auctionClosingSeconds;
/// @notice A reserve setter account has the rights to set a reserve for a round,
/// however they cannot do this within a reserve blackout period.
Expand All @@ -19,32 +22,40 @@ struct RoundTimingInfo {
}

library RoundTimingInfoLib {
/// @dev Using signed offset involves a lot of casting when comparing the to the block timestamp
/// so we provide a helper method here
function blockTimestampBeforeOffset(int64 offsetTimestamp) private view returns(bool) {
return int64(uint64(block.timestamp)) < offsetTimestamp;
}

/// @dev Using signed offset involves a lot of casting when comparing the to the block timestamp
/// so we provide a helper method here
/// Notice! this helper method should not be used before checking that the offset is less than the timestamp
function unsignedSinceTimestamp(int64 offsetTimestamp) private view returns (uint64) {
return uint64(int64(uint64(block.timestamp)) - offsetTimestamp);
}

/// @notice The current round, given the current timestamp, the offset and the round duration
function currentRound(RoundTimingInfo memory info) internal view returns (uint64) {
if (block.timestamp < info.offsetTimestamp) {
if (blockTimestampBeforeOffset(info.offsetTimestamp)) {
return 0;
}

return (uint64(block.timestamp) - info.offsetTimestamp) / info.roundDurationSeconds;
return (unsignedSinceTimestamp(info.offsetTimestamp)) / info.roundDurationSeconds;
}

/// @notice Has the current auction round closed
function isAuctionRoundClosed(RoundTimingInfo memory info) internal view returns (bool) {
if (block.timestamp < info.offsetTimestamp) {
if (blockTimestampBeforeOffset(info.offsetTimestamp)) {
return false;
}

uint64 timeInRound = timeIntoRound(info);
uint64 timeSinceOffset = unsignedSinceTimestamp(info.offsetTimestamp);
uint64 timeInRound = timeSinceOffset % info.roundDurationSeconds;
// round closes at AuctionClosedSeconds before the end of the round
return timeInRound >= info.roundDurationSeconds - info.auctionClosingSeconds;
}

/// @notice How far (in seconds) are we throught the current round. Can be 0 at the start of the current round
function timeIntoRound(RoundTimingInfo memory info) internal view returns (uint64) {
uint64 timeSinceOffset = (uint64(block.timestamp) - info.offsetTimestamp);
return timeSinceOffset % info.roundDurationSeconds;
}

/// @notice The reserve cannot be set during the blackout period
/// This period runs from ReserveSubmissionSeconds before the auction closes and ends when the round resolves, or when the round ends.
/// @param info Round timing info
Expand All @@ -54,7 +65,7 @@ library RoundTimingInfoLib {
view
returns (bool)
{
if (block.timestamp < info.offsetTimestamp) {
if (blockTimestampBeforeOffset(info.offsetTimestamp)) {
// no rounds have started, can't be in blackout
return false;
}
Expand All @@ -70,7 +81,8 @@ library RoundTimingInfoLib {
// the round in question hasnt been resolved
// therefore if we're within ReserveSubmissionSeconds of the auction close then we're in blackout
// otherwise we're not
uint64 timeInRound = timeIntoRound(info);
uint64 timeSinceOffset = unsignedSinceTimestamp(info.offsetTimestamp);
uint64 timeInRound = timeSinceOffset % info.roundDurationSeconds;
return
timeInRound >=
(info.roundDurationSeconds -
Expand All @@ -88,7 +100,7 @@ library RoundTimingInfoLib {
pure
returns (uint64, uint64)
{
uint64 roundStart = info.offsetTimestamp + info.roundDurationSeconds * round;
uint64 roundStart = uint64(info.offsetTimestamp + int64(info.roundDurationSeconds * round));
uint64 roundEnd = roundStart + info.roundDurationSeconds - 1;
return (roundStart, roundEnd);
}
Expand Down
39 changes: 22 additions & 17 deletions test/foundry/ExpressLaneAuction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ contract ExpressLaneAuctionTest is Test {
);
event SetRoundTimingInfo(
uint64 currentRound,
uint64 offsetTimestamp,
int64 offsetTimestamp,
uint64 roundDurationSeconds,
uint64 auctionClosingSeconds,
uint64 reserveSubmissionSeconds
);

uint64 roundDuration = 60; // 1 min
uint64 offsetTimestamp = 3234000;
int64 offsetTimestamp = 3234000;

struct TestBidder {
uint256 privKey;
Expand Down Expand Up @@ -128,7 +128,7 @@ contract ExpressLaneAuctionTest is Test {

// move to round test round
(, uint64 roundDurationSeconds, , ) = auction.roundTimingInfo();
vm.warp(offsetTimestamp + roundDurationSeconds * testRound);
vm.warp(uint64(offsetTimestamp) + roundDurationSeconds * testRound);

return (token, IExpressLaneAuction(auction));
}
Expand Down Expand Up @@ -179,6 +179,11 @@ contract ExpressLaneAuctionTest is Test {
// expect div by 0 or not less than auction closing - either way revert
vm.expectRevert();
auction.initialize(rdArgs2);

InitArgs memory rdArgs3 = createArgs(address(token));
rdArgs3._roundTimingInfo.offsetTimestamp = -1;
vm.expectRevert(abi.encodeWithSelector(NegativeOffset.selector));
auction.initialize(rdArgs3);
}

function testInit() public {
Expand Down Expand Up @@ -217,7 +222,7 @@ contract ExpressLaneAuctionTest is Test {
);
auction.initialize(args);
(
uint64 offsetTimestampA,
int64 offsetTimestampA,
uint64 roundDurationSeconds,
uint64 auctionClosingSeconds,
uint64 reserveSubmissionSeconds
Expand Down Expand Up @@ -393,9 +398,9 @@ contract ExpressLaneAuctionTest is Test {
vm.warp(1);
assertEq(auction.currentRound(), 0);

(uint64 offsetTimestampA, uint64 roundDurationSeconds, , ) = auction.roundTimingInfo();
(int64 offsetTimestampA, uint64 roundDurationSeconds, , ) = auction.roundTimingInfo();

vm.warp(offsetTimestampA - 1);
vm.warp(uint64(offsetTimestampA) - 1);
assertEq(auction.currentRound(), 0);

for (uint256 i = 0; i < testRound; i++) {
Expand Down Expand Up @@ -1385,12 +1390,12 @@ contract ExpressLaneAuctionTest is Test {
ResolveSetup memory rs = deployDepositAndBids();
// start of the test round
(
uint64 offsetTimestampA,
int64 offsetTimestampA,
uint64 roundDurationSeconds,
uint64 auctionClosingSeconds,
uint64 reserveSubmissionSeconds
) = rs.auction.roundTimingInfo();
vm.warp(offsetTimestampA + roundDurationSeconds * testRound);
vm.warp(uint64(offsetTimestampA) + roundDurationSeconds * testRound);
vm.stopPrank();

assertEq(rs.auction.reservePrice(), minReservePrice, "before reserve price");
Expand Down Expand Up @@ -1430,7 +1435,7 @@ contract ExpressLaneAuctionTest is Test {

// during blackout
vm.warp(
offsetTimestamp +
uint64(offsetTimestamp) +
roundDurationSeconds *
(testRound + 1) -
auctionClosingSeconds -
Expand All @@ -1441,7 +1446,7 @@ contract ExpressLaneAuctionTest is Test {
vm.expectRevert(abi.encodeWithSelector(ReserveBlackout.selector));
rs.auction.setReservePrice(minReservePrice);

vm.warp(offsetTimestamp + roundDurationSeconds * (testRound + 1) - auctionClosingSeconds);
vm.warp(uint64(offsetTimestamp) + roundDurationSeconds * (testRound + 1) - auctionClosingSeconds);

vm.prank(reservePriceSetter);
vm.expectRevert(abi.encodeWithSelector(ReserveBlackout.selector));
Expand Down Expand Up @@ -1772,13 +1777,13 @@ contract ExpressLaneAuctionTest is Test {
auction.setRoundTimingInfo(newInfo);

// set to round 23
vm.warp(offsetTimestamp + roundDuration * 23);
vm.warp(uint64(offsetTimestamp) + roundDuration * 23);
// now use an offset that would put us on round 24
vm.prank(roundTimingSetter);
vm.expectRevert(abi.encodeWithSelector(InvalidNewRound.selector, 23, 24));
auction.setRoundTimingInfo(
RoundTimingInfo({
offsetTimestamp: offsetTimestamp - roundDuration,
offsetTimestamp: offsetTimestamp - int64(roundDuration),
roundDurationSeconds: roundDuration,
auctionClosingSeconds: 10,
reserveSubmissionSeconds: 20
Expand All @@ -1790,8 +1795,8 @@ contract ExpressLaneAuctionTest is Test {
vm.expectRevert(
abi.encodeWithSelector(
InvalidNewStart.selector,
offsetTimestamp + roundDuration * 24,
offsetTimestamp + (roundDuration - 1) * 24
uint64(offsetTimestamp) + roundDuration * 24,
uint64(offsetTimestamp) + (roundDuration - 1) * 24
)
);
auction.setRoundTimingInfo(
Expand All @@ -1805,7 +1810,7 @@ contract ExpressLaneAuctionTest is Test {

uint64 longDuration = 86401;
(uint64 start, ) = auction.roundTimestamps(auction.currentRound() + 1);
uint64 newOffset = start - longDuration * 24;
int64 newOffset = int64(start - longDuration * 24);

vm.prank(roundTimingSetter);
vm.expectRevert(abi.encodeWithSelector(RoundTooLong.selector, longDuration));
Expand Down Expand Up @@ -1842,7 +1847,7 @@ contract ExpressLaneAuctionTest is Test {

uint64 cNewDuration = (roundDuration * 7) / 3;
(uint64 cStart, ) = auction.roundTimestamps(auction.currentRound() + 1);
uint64 cNewOffset = cStart - cNewDuration * (auction.currentRound() + 1);
int64 cNewOffset = int64(cStart - cNewDuration * (auction.currentRound() + 1));

vm.expectEmit(true, true, true, true);
emit SetRoundTimingInfo(auction.currentRound(), cNewOffset, cNewDuration, 13, 12);
Expand All @@ -1855,7 +1860,7 @@ contract ExpressLaneAuctionTest is Test {
reserveSubmissionSeconds: 12
})
);
(uint64 offsetAfter, uint64 durationAfter, uint64 acAfter, uint64 rsAfter) = auction
(int64 offsetAfter, uint64 durationAfter, uint64 acAfter, uint64 rsAfter) = auction
.roundTimingInfo();
assertEq(offsetAfter, cNewOffset);
assertEq(durationAfter, cNewDuration);
Expand Down
Loading

0 comments on commit 5f288d7

Please sign in to comment.