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

Express lane auction contracts #214

Open
wants to merge 99 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
6875293
Added express lane auction contracts draft
yahgwai Jul 3, 2024
e1738b8
Updated express lane interface and added more tests
yahgwai Jul 18, 2024
bdae989
Addressed more todos
yahgwai Jul 18, 2024
b20f9e9
Merge branch 'develop' into express-lane-auction
yahgwai Jul 18, 2024
5e90ab3
Renamed auctioneer to auction clerk
yahgwai Jul 18, 2024
abc71fa
Updates from spec
yahgwai Jul 19, 2024
f7a0019
Balance2 usage and addressed more todos
yahgwai Jul 22, 2024
97e387c
Updated to safeTransfer
yahgwai Jul 22, 2024
9d9c199
Fixed strick equality
yahgwai Jul 22, 2024
a3bf984
Added testing comments
yahgwai Jul 22, 2024
424f0ac
Formatting
yahgwai Jul 22, 2024
46f20c3
Added slither exceptions
yahgwai Jul 23, 2024
522391c
Test updates and internal lib function
yahgwai Jul 24, 2024
521da55
Added resolvedRounds public and set latestResolvedRound internal
yahgwai Jul 24, 2024
ef2fb0b
Use access control enumerable
yahgwai Jul 24, 2024
1690451
Use functions internally, make others external
yahgwai Jul 24, 2024
f830383
Formatting
yahgwai Jul 24, 2024
60f6080
Dont reset withdrawal round after finalized withdrawal - just on deposit
yahgwai Jul 24, 2024
8251c24
Added domain comment
yahgwai Jul 24, 2024
d1ac7d3
Added domain value to express lane auction
yahgwai Jul 25, 2024
e4697d7
Added todos based on code review
yahgwai Jul 25, 2024
eb63042
Docs updates
yahgwai Jul 25, 2024
54e7aca
Beginnings of separate balance testing
yahgwai Jul 26, 2024
a2fbc1d
Finished balance tests
yahgwai Jul 29, 2024
261e1b0
Added elc round specific tests
yahgwai Jul 30, 2024
92e64ef
Docs and testing for resolved rounds
yahgwai Jul 30, 2024
89fbaa3
Cleaned todos in testing file
yahgwai Jul 31, 2024
5300676
Addressed more todos
yahgwai Aug 1, 2024
2f647c8
Formatting
yahgwai Aug 2, 2024
971ab70
Slither updates
yahgwai Aug 2, 2024
fa50fbf
Added transferrer setting first draft
yahgwai Aug 2, 2024
01c3669
Updates from slither mutate
yahgwai Aug 5, 2024
30b4dd0
Set transferrer into interface
yahgwai Aug 5, 2024
777b58e
Merge branch 'express-lane-auction-2' into express-lane-auction-3
yahgwai Aug 5, 2024
97e698d
Added alternative transferors and tests
yahgwai Aug 5, 2024
a63bdee
Comment update
yahgwai Aug 5, 2024
2def378
Removed todo
yahgwai Aug 5, 2024
9565035
Added round timing setter
yahgwai Aug 6, 2024
424d732
Remove comment
yahgwai Aug 6, 2024
ee70432
Removed unneeded admin roles
yahgwai Aug 6, 2024
a599fa1
Removed struct params usage
yahgwai Aug 6, 2024
52b983f
Merge from base
yahgwai Aug 6, 2024
74821a2
Merge from base
yahgwai Aug 6, 2024
fad469d
Delete todo
yahgwai Aug 6, 2024
2cc0bf3
Merge from base
yahgwai Aug 6, 2024
314d593
Merge from base
yahgwai Aug 6, 2024
87d578a
Added 712 signing
yahgwai Aug 8, 2024
f2e1e4f
Formatting and test update
yahgwai Aug 8, 2024
5afd437
fix: lint
gzeoneth Aug 13, 2024
795ea3e
Updated NotTransferor address to match NotExpressLaneController
yahgwai Aug 15, 2024
4301639
Formatting
yahgwai Aug 15, 2024
926b264
Added bid const hash
yahgwai Aug 15, 2024
e0fb4ed
Comment update
yahgwai Aug 15, 2024
d1cce13
Merge pull request #226 from OffchainLabs/express-lane-auction-round-…
yahgwai Aug 15, 2024
89dbece
Merge from express-lane-auction-2
yahgwai Aug 15, 2024
e4d85e1
Merge pull request #227 from OffchainLabs/express-lane-auction-712
yahgwai Aug 15, 2024
ae50531
Merge from express-lane-auction-2
yahgwai Aug 15, 2024
d4043bd
Merge pull request #224 from OffchainLabs/express-lane-auction-3
yahgwai Aug 15, 2024
b3a281b
Merge pull request #222 from OffchainLabs/express-lane-auction-2
yahgwai Aug 15, 2024
1bbd922
test: fix
gzeoneth Aug 15, 2024
d145c07
Factor out round timing interval interanl
yahgwai Aug 15, 2024
4274a31
Merge branch 'express-lane-auction' of https://github.com/OffchainLab…
yahgwai Aug 15, 2024
e54bf02
Refactored the round timing info setting to use shared internal func
yahgwai Aug 15, 2024
1ae3d10
CR updates
yahgwai Aug 15, 2024
d6fbc49
More updates from CR
yahgwai Aug 15, 2024
29f449a
Update src/express-lane-auction/ExpressLaneAuction.sol
yahgwai Aug 15, 2024
34b5da7
Merge from remote
yahgwai Aug 15, 2024
d9c8dc9
Updated shadow variable name
yahgwai Aug 15, 2024
5f288d7
Added int offset
yahgwai Aug 15, 2024
c8de1f8
Updated round timing tests
yahgwai Aug 16, 2024
70e22a7
Updated comment
yahgwai Aug 16, 2024
fb5fe04
Updated comments
yahgwai Aug 16, 2024
180262a
Merge pull request #237 from OffchainLabs/express-lane-auction-int-of…
yahgwai Aug 16, 2024
bc7a334
Updates from goran's cr
yahgwai Aug 17, 2024
fbc5565
Slither update
yahgwai Aug 17, 2024
acf77b7
Formatting
yahgwai Aug 17, 2024
0134e49
Merge from develop
yahgwai Aug 17, 2024
5766ac5
Removed set beneficiary
yahgwai Aug 19, 2024
e33c353
Updated comment
yahgwai Aug 19, 2024
9dc19d2
Public to external
yahgwai Aug 20, 2024
a41681e
Added balance at round functions
yahgwai Aug 21, 2024
35e3a46
Added specific boundary conditions for balance test as fuzzing seemed…
yahgwai Aug 21, 2024
a93c464
Merge branch 'express-lane-auction' into express-lane-auction-balance…
gzeoneth Aug 21, 2024
93272e0
Removed out of date comment
yahgwai Aug 21, 2024
13854a5
Balance updates from audit review
yahgwai Aug 22, 2024
4445b48
minor typo fixes in comments
leeederek Aug 22, 2024
94d1194
Added a burner contract in case the dao decides to burn the funds ins…
yahgwai Aug 22, 2024
94a72ab
Merge pull request #239 from leeederek/patch-1
yahgwai Aug 23, 2024
a40ba2e
Merge pull request #238 from OffchainLabs/express-lane-auction-balanc…
yahgwai Aug 27, 2024
893f40d
Burner tests
yahgwai Aug 27, 2024
e3b24b7
Added comment
yahgwai Aug 27, 2024
e4dd36a
Merge pull request #242 from OffchainLabs/express-lane-auction-burn
yahgwai Aug 27, 2024
f1f862f
Comment update
yahgwai Aug 27, 2024
84ade50
Merge pull request #243 from OffchainLabs/express-lane-auction-audit-fix
yahgwai Aug 27, 2024
a9c5cfb
Merge branch 'develop' into express-lane-auction
gzeoneth Sep 12, 2024
10c9224
Merge branch 'develop' into express-lane-auction
gzeoneth Oct 2, 2024
738c3dc
chore: format
gzeoneth Oct 24, 2024
1078d09
Revert "chore: format"
gzeoneth Oct 24, 2024
bec7d62
v2.1.1-beta.0
gzeoneth Oct 24, 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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ deployments/
scripts/config.ts
forge-cache/
out/
.env
.env
lcov.info
output_directory
2 changes: 1 addition & 1 deletion slither.db.json

Large diffs are not rendered by default.

172 changes: 172 additions & 0 deletions src/express-lane-auction/Balance.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./Errors.sol";

/// @notice Account balance and the round at which it can be withdrawn
/// Balances are withdrawn as part of a two step process - intiation and finalization
/// This is so that a bidder can't withdraw their balance after making a bid
/// Instead, if they initiate their withdrawal in round r, they must wait until the beginning of
/// round r+2 before they can withdraw the balance. In the mean time their balance can be used to
/// resolve an auction if it is within a valid bid, however the auctioneer may choose to
/// reject bids from accounts with an initiated balance withdrawal
/// Once a withdrawal has been initiated no more balance can be deposited until
/// after the withdrawal has been finalized
struct Balance {
/// @notice The amount of balance in the account
uint256 balance;
/// @notice The round at which all of the balance can be withdrawn
/// Is set to uint64.max when no withdrawal has been intiated
uint64 withdrawalRound;
}

/// @notice Balance mutation and view functionality. This is in it's own library so that we can
// reason about and test how the different ways balance mutations interact with each other
library BalanceLib {
/// @notice Has this balance initiated a withdrawal that has yet to be finalized
function hasInitiatedWithdrawal(Balance storage bal) internal view returns (bool) {
// since rounds are multiples of seconds they cannot reach uint64.max
return bal.withdrawalRound != type(uint64).max;
}

/// @notice Reset an initiated withdrawal. If a balance has been initiated for withdrawal
/// this function cancels it.
/// Should be called the first time a balance struct is created
function resetInitiatedWithdrawal(Balance storage bal) internal {
if (bal.withdrawalRound != type(uint64).max) {
bal.withdrawalRound = type(uint64).max;
}
}

/// @notice Check whether the full balance is withdrawable at a specified round
/// @param bal The balance to check
/// @param round The round to check withdrawal in
function isWithdrawnAtRound(Balance storage bal, uint64 round) internal view returns (bool) {
return round >= bal.withdrawalRound;
}

/// @notice The available balance at the supplied round. 0 if a withdrawal has been initiated and has
/// past the withdrawal round.
/// @param bal The balance to query
/// @param round The round to check the balance in
function balanceAtRound(Balance storage bal, uint64 round) internal view returns (uint256) {
return isWithdrawnAtRound(bal, round) ? 0 : bal.balance;
}

/// @notice The withdrawable balance at the supplied round. If a withdrawal has been initiated, the
/// supplied round is past the withdrawal round and has yet to be finalized, then the balance
/// of this account is returned. Otherwise 0.
/// @param bal The balance to query
/// @param round The round to check the withdrawable balance in
function withdrawableBalanceAtRound(Balance storage bal, uint64 round)
internal
view
returns (uint256)
{
return isWithdrawnAtRound(bal, round) ? bal.balance : 0;
}

/// @notice Increase a balance by a specified amount
/// Cannot be called if a withdrawal has been initiated
/// @param bal The balance info
/// @param amount The amount to increase the balance by
function increase(Balance storage bal, uint256 amount) internal {
// no point increasing if no amount is being supplied
if (amount == 0) {
revert ZeroAmount();
}

// withdrawal round can only be zero if a balance has never been used
// since nowhere can a withdrawal round be set to zero
// we need to initialize a new balance by setting the withdrawal round to max
if (bal.withdrawalRound == 0) {
resetInitiatedWithdrawal(bal);
}

if (hasInitiatedWithdrawal(bal)) {
revert WithdrawalInProgress();
}
gzeoneth marked this conversation as resolved.
Show resolved Hide resolved

bal.balance += amount;
}

/// @notice Reduce a balance immediately. The balance must already be greater than the amount
/// and if there a withdrawal has been initiated for this balance it must be occuring in
/// a round after the supplied round
/// @param bal The balance to reduce
/// @param amount The amount to reduce by
/// @param round The round to check withdrawals against. A withdrawal after this round will be ignored
/// and the balance reduced anyway, withdrawals before or on this round will be respected
/// and the reduce will revert
function reduce(
Balance storage bal,
uint256 amount,
uint64 round
) internal {
if (balanceAtRound(bal, round) < amount) {
revert InsufficientBalance(amount, balanceAtRound(bal, round));
gzeoneth marked this conversation as resolved.
Show resolved Hide resolved
}

// is there a withdrawal in progress
gzeoneth marked this conversation as resolved.
Show resolved Hide resolved
bal.balance -= amount;

// if there is currently a pending withdrawal but the balance has been reduce to 0
// then we should cancel the pending withdrawal as there's no longer anything to withdraw
if (bal.balance == 0) {
resetInitiatedWithdrawal(bal);
}
}

/// @notice Initiate a withdrawal. A withdrawal is a reduction of the full amount.
/// Withdrawal is a two step process initialization and finalization. Finalization is only
/// possible two rounds after the supplied round parameter. This is
/// so that balance cannot be reduced unexpectedly without notice. An external
/// observer can see that a withdrawal has been initiated, and will therefore
/// be able to take it into account and not rely on the balance being there.
/// In the case of the auction contract this allows the bidders to withdraw their
/// balance, but an auctioneer will know not to accept there bids in the mean time
/// @param bal The balance to iniate a reduction on
/// @param round The round that the initiation is occuring within. Withdrawal can then be finalized
/// two rounds after this supplied round.
function initiateWithdrawal(Balance storage bal, uint64 round) internal {
if (bal.balance == 0) {
revert ZeroAmount();
}

if (hasInitiatedWithdrawal(bal)) {
revert WithdrawalInProgress();
}

// We dont make it round + 1 in case the iniation were to occur right at the
// end of a round. Doing round + 2 ensure observer always have at least one full round
// to become aware of the future balance change.
bal.withdrawalRound = round + 2;
}

/// @notice Finalize an already initialized withdrawal. Reduces the balance to 0.
/// Can only be called two round after the withdrawal was initiated.
/// @param bal The balance to finalize
/// @param round The round to check whether withdrawal is valid in. Usually the current round.
function finalizeWithdrawal(Balance storage bal, uint64 round) internal returns (uint256) {
uint256 withdrawableBalance = withdrawableBalanceAtRound(bal, round);
if (withdrawableBalance == 0) {
revert NothingToWithdraw();
}

// CHRIS: TODO: double storage pull again
resetInitiatedWithdrawal(bal);
bal.balance = 0;
return withdrawableBalance;
}
}

// CHRIS: TODO: balance testing and todos:
// CHRIS: TODO: add a doc about the difference between bidding for round and bidding in round
// CHRIS: TODO: list gurantee of the balance lib
// 1. withdrawal round is 0 only if the balance has never been initialized, otherwise it is 2 or more
// 2. both withdrawable balance and available balance cannot be 0
// CHRIS: TODO: one thing to test is what these functions do before round 0, also what about unitialised balances - should we test that?
// CHRIS: TODO: it should be possible to call any of these in any order - particularly the updating functions. How can we test for this
// we would need to first define an inconsistent state and then go from there
// CHRIS: TODO: we wanna make sure we're not in a state where we can get trapped, either with funds in there, or with a zero balance or something - those are inconsistent states
// another inconsistent state is when the balance in there doesnt match what we expect from external reduces etc
75 changes: 75 additions & 0 deletions src/express-lane-auction/ELCRound.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./Errors.sol";

/// @notice When an auction round is resolved a new express lane controller is chosen for that round
/// An elc round stores that selected express lane controller against the round number
struct ELCRound {
address expressLaneController;
uint64 round;
}

// CHRIS: TODO: consider all usages of the these during initialization
// CHRIS: TODO: Invariant: not possible for the rounds in latest rounds to have the same value
// CHRIS: TODO: what values do these functions have during init?
// CHRIS: TODO: Invariant: lastAuctionRound.round should never be > round if called during resolve auction except during initialization

/// @notice Latest resolved express lane controller auction rounds
// Only the two latest resolved rounds are stored
library LatestELCRoundsLib {
/// @notice The last resolved express lane controller round, and its index in the array
/// @param rounds The stored resolved rounds
/// @return The last resolved elc round
/// @return The index of that last resolved round within the supplied array
function latestELCRound(ELCRound[2] storage rounds)
public
view
returns (ELCRound storage, uint8)
{
ELCRound storage latestRound = rounds[0];
uint8 index = 0;
if (latestRound.round < rounds[1].round) {
latestRound = rounds[1];
index = 1;
}
return (latestRound, index);
yahgwai marked this conversation as resolved.
Show resolved Hide resolved
}

/// @notice Finds the elc round that matches the supplied round. Reverts if no matching round found.
/// @param latestResolvedRounds The resolved elc rounds
/// @param round The round number to find a resolved round for
function resolvedRound(ELCRound[2] storage latestResolvedRounds, uint64 round)
internal
view
returns (ELCRound storage)
{
if (latestResolvedRounds[0].round == round) {
return latestResolvedRounds[0];
} else if (latestResolvedRounds[1].round == round) {
return latestResolvedRounds[1];
} else {
revert RoundNotResolved(round);
gzeoneth marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// @notice Set a resolved round into the array, overwriting the oldest resolved round
/// in the array.
/// @param latestResolvedRounds The resolved rounds aray
/// @param round The round to resolve
/// @param expressLaneController The new express lane controller for that round
function setResolvedRound(
ELCRound[2] storage latestResolvedRounds,
uint64 round,
address expressLaneController
) internal {
(ELCRound storage lastRoundResolved, uint8 index) = latestELCRound(latestResolvedRounds);
if (lastRoundResolved.round >= round) {
revert RoundAlreadyResolved(round);
}

// dont replace the newest round, use the oldest slot
uint8 oldestRoundIndex = index ^ 1;
latestResolvedRounds[oldestRoundIndex] = ELCRound(expressLaneController, round);
}
}
23 changes: 23 additions & 0 deletions src/express-lane-auction/Errors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

// CHRIS: TODO: docs and see if all of these are actually used

error InsufficientBalance(uint256 amountRequested, uint256 balance);
error InsufficientBalanceAcc(address acount, uint256 amountRequested, uint256 balance);
error RoundDurationTooShort();
error NothingToWithdraw();
error ZeroAmount();
error ZeroBiddingToken();
error WithdrawalInProgress();
error RoundAlreadyResolved(uint64 round);
error SameBidder();
error BidsWrongOrder();
error TieBidsWrongOrder();
error AuctionNotClosed();
error ReservePriceTooLow(uint256 reservePrice, uint256 minReservePrice);
error ReservePriceNotMet(uint256 bidAmount, uint256 reservePrice);
error ReserveBlackout();
error RoundTooOld(uint256 round, uint256 currentRound);
error RoundNotResolved(uint256 round);
error NotExpressLaneController(uint64 round, address controller, address sender);
Loading
Loading