From fa50fbf280fba6d06672ef85491c9d2dc6e1368a Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Fri, 2 Aug 2024 18:12:29 +0100 Subject: [PATCH 1/6] Added transferrer setting first draft --- .../ExpressLaneAuction.sol | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/express-lane-auction/ExpressLaneAuction.sol b/src/express-lane-auction/ExpressLaneAuction.sol index f0cc16ef..253200ec 100644 --- a/src/express-lane-auction/ExpressLaneAuction.sol +++ b/src/express-lane-auction/ExpressLaneAuction.sol @@ -480,6 +480,28 @@ contract ExpressLaneAuction is ); } + // struct Trasferrer { + // address addr; + // uint64 fixedUntilRound; + // // add a bool for auto reset? + // bool resetAfterTransfer; + // } + // mapping(address => Trasferrer) transferrers; + // // CHRIS: TODO: docs and tests + // function setTransferrer(Transferrer transferrer) public { + // // if a transferrer has been set + // Transferrer currentTransferrer = transferrers[msg.sender]; + // if(currentTransferrer.addr != addr(0) && currentTransferrer.fixedUntilRound > roundTimingInfo.currentRound()){ + // // CHRIS: TODO: + // // revert + // } + + // transferrers[msg.sender] = transferrer; + + // // CHRIS: TODO: events + // } + + /// @inheritdoc IExpressLaneAuction function transferExpressLaneController(uint64 round, address newExpressLaneController) external @@ -495,13 +517,22 @@ contract ExpressLaneAuction is ELCRound storage resolvedRound = latestResolvedRounds.resolvedRound(round); address resolvedELC = resolvedRound.expressLaneController; + // CHRIS: TODO: + // address transferrer = transferrers[resolvedELC].addr; + // if(transferrer != address(0)) { + // if(transferrer != msg.sender) { + // revert("hi"); + // } + // } else if (resolvedELC != msg.sender) { revert NotExpressLaneController(round, resolvedELC, msg.sender); } - + resolvedRound.expressLaneController = newExpressLaneController; (uint64 start, uint64 end) = info.roundTimestamps(round); + // CHRIS: TODO: add the transferrer here? + // CHRIS: TODO: if reset after transfer then 0 out the transferrer emit SetExpressLaneController( round, resolvedELC, From 30b4dd069c65674447a9437911be01ca6c6cf452 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 5 Aug 2024 16:39:18 +0100 Subject: [PATCH 2/6] Set transferrer into interface --- src/express-lane-auction/Errors.sol | 1 + .../ExpressLaneAuction.sol | 39 +++++++++---------- .../IExpressLaneAuction.sol | 24 ++++++++++++ test/foundry/ExpressLaneAuction.t.sol | 2 +- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/express-lane-auction/Errors.sol b/src/express-lane-auction/Errors.sol index 913ded7e..5bf0bdb3 100644 --- a/src/express-lane-auction/Errors.sol +++ b/src/express-lane-auction/Errors.sol @@ -20,3 +20,4 @@ error ReserveBlackout(); error RoundTooOld(uint256 round, uint256 currentRound); error RoundNotResolved(uint256 round); error NotExpressLaneController(uint64 round, address controller, address sender); +error FixedTransferrer(uint64 fixedUntilRound); diff --git a/src/express-lane-auction/ExpressLaneAuction.sol b/src/express-lane-auction/ExpressLaneAuction.sol index 253200ec..547a9178 100644 --- a/src/express-lane-auction/ExpressLaneAuction.sol +++ b/src/express-lane-auction/ExpressLaneAuction.sol @@ -9,7 +9,7 @@ import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import {DelegateCallAware} from "../libraries/DelegateCallAware.sol"; -import {IExpressLaneAuction, Bid, InitArgs} from "./IExpressLaneAuction.sol"; +import {IExpressLaneAuction, Bid, InitArgs, Transferrer} from "./IExpressLaneAuction.sol"; import {ELCRound, LatestELCRoundsLib} from "./ELCRound.sol"; import {RoundTimingInfo, RoundTimingInfoLib} from "./RoundTimingInfo.sol"; @@ -133,6 +133,9 @@ contract ExpressLaneAuction is /// @inheritdoc IExpressLaneAuction uint256 public beneficiaryBalance; + /// @inheritdoc IExpressLaneAuction + mapping(address => Transferrer) public transferrerOf; + /// @inheritdoc IExpressLaneAuction function initialize(InitArgs memory args) public initializer onlyDelegated { if (address(args._biddingToken) == address(0)) { @@ -480,27 +483,21 @@ contract ExpressLaneAuction is ); } - // struct Trasferrer { - // address addr; - // uint64 fixedUntilRound; - // // add a bool for auto reset? - // bool resetAfterTransfer; - // } - // mapping(address => Trasferrer) transferrers; - // // CHRIS: TODO: docs and tests - // function setTransferrer(Transferrer transferrer) public { - // // if a transferrer has been set - // Transferrer currentTransferrer = transferrers[msg.sender]; - // if(currentTransferrer.addr != addr(0) && currentTransferrer.fixedUntilRound > roundTimingInfo.currentRound()){ - // // CHRIS: TODO: - // // revert - // } - - // transferrers[msg.sender] = transferrer; - - // // CHRIS: TODO: events - // } + /// @notice Sets a transferrer for an express lane controller + /// The transferrer is an address that will have the right to transfer express lane controller rights + /// on behalf an express lane controller. + /// @param transferrer The transferrer to set + function setTransferrer(Transferrer calldata transferrer) external { + // if a transferrer has already been set, it may be fixed until a future round + Transferrer storage currentTransferrer = transferrerOf[msg.sender]; + if(currentTransferrer.addr != address(0) && currentTransferrer.fixedUntilRound > roundTimingInfo.currentRound()){ + revert FixedTransferrer(currentTransferrer.fixedUntilRound); + } + transferrerOf[msg.sender] = transferrer; + + emit SetTransferrer(msg.sender, transferrer.addr, transferrer.fixedUntilRound); + } /// @inheritdoc IExpressLaneAuction function transferExpressLaneController(uint64 round, address newExpressLaneController) diff --git a/src/express-lane-auction/IExpressLaneAuction.sol b/src/express-lane-auction/IExpressLaneAuction.sol index be5272ef..b25a3939 100644 --- a/src/express-lane-auction/IExpressLaneAuction.sol +++ b/src/express-lane-auction/IExpressLaneAuction.sol @@ -25,6 +25,18 @@ struct Bid { bytes signature; } +/// @notice Sets a transferrer for an express lane controller +/// The transferrer is an address that will have the right to transfer express lane controller rights +/// on behalf an express lane controller. +struct Transferrer { + /// @notice The address of the transferrer + address addr; + /// @notice The express lane controller can choose to fix the transferrer until a future round number + /// This gives them ability to guarantee to other parties that they will not change transferrer during an ongoing round + /// The express lane controller can ignore this feature by setting this value to 0. + uint64 fixedUntilRound; +} + /// @notice The arguments used to initialize an express lane auction struct InitArgs { /// @param _auctioneer The address who can resolve auctions @@ -112,6 +124,12 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg uint64 endTimestamp ); + /// @notice A new transferrer has been set for + /// @param expressLaneController The express lane controller that has a transferrer + /// @param transferrer The transferrer chosen + /// @param fixedUntilRound The round until which this transferrer is fixed for this controller + event SetTransferrer(address indexed expressLaneController, address indexed transferrer, uint64 fixedUntilRound); + /// @notice The minimum reserve price was set /// @param oldPrice The previous minimum reserve price /// @param newPrice The new minimum reserve price @@ -175,6 +193,12 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg /// This is a gas optimisation to avoid making a transfer every time an auction is resolved function beneficiaryBalance() external returns (uint256); + /// @notice Express lane controllers can optionally set a transferrer address that has the rights + /// to transfer their controller rights. This function returns the transferrer if one has been set + /// Returns the transferrer for the supplied controller, and the round until which this + /// transferrer is fixed if set. + function transferrerOf(address expressLaneController) external returns (address addr, uint64 fixedUntil); + /// @notice Initialize the auction /// @param args Initialization parameters function initialize(InitArgs memory args) external; diff --git a/test/foundry/ExpressLaneAuction.t.sol b/test/foundry/ExpressLaneAuction.t.sol index 224ba4b1..7d285af3 100644 --- a/test/foundry/ExpressLaneAuction.t.sol +++ b/test/foundry/ExpressLaneAuction.t.sol @@ -645,7 +645,7 @@ contract ExpressLaneAuctionTest is Test { } function testGetBidBytes() public { - (MockERC20 erc20, IExpressLaneAuction auction) = deployAndDeposit(); + (, IExpressLaneAuction auction) = deployAndDeposit(); uint64 biddingForRound = auction.currentRound() + 1; bytes memory b0 = auction.getBidBytes( biddingForRound, From 97e698d3695ae64837cc0abae42ccd54a06be87f Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 5 Aug 2024 18:05:18 +0100 Subject: [PATCH 3/6] Added alternative transferors and tests --- src/express-lane-auction/Errors.sol | 3 +- .../ExpressLaneAuction.sol | 50 ++++----- .../IExpressLaneAuction.sol | 50 ++++++--- test/foundry/ExpressLaneAuction.t.sol | 103 +++++++++++++++++- 4 files changed, 158 insertions(+), 48 deletions(-) diff --git a/src/express-lane-auction/Errors.sol b/src/express-lane-auction/Errors.sol index 5bf0bdb3..a5dccc02 100644 --- a/src/express-lane-auction/Errors.sol +++ b/src/express-lane-auction/Errors.sol @@ -20,4 +20,5 @@ error ReserveBlackout(); error RoundTooOld(uint256 round, uint256 currentRound); error RoundNotResolved(uint256 round); error NotExpressLaneController(uint64 round, address controller, address sender); -error FixedTransferrer(uint64 fixedUntilRound); +error FixedTransferor(uint64 fixedUntilRound); +error NotTransferor(address expectedTransferor, address msgSender); diff --git a/src/express-lane-auction/ExpressLaneAuction.sol b/src/express-lane-auction/ExpressLaneAuction.sol index 9ed11767..8343cda0 100644 --- a/src/express-lane-auction/ExpressLaneAuction.sol +++ b/src/express-lane-auction/ExpressLaneAuction.sol @@ -9,7 +9,7 @@ import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import {DelegateCallAware} from "../libraries/DelegateCallAware.sol"; -import {IExpressLaneAuction, Bid, InitArgs, Transferrer} from "./IExpressLaneAuction.sol"; +import {IExpressLaneAuction, Bid, InitArgs, Transferor} from "./IExpressLaneAuction.sol"; import {ELCRound, LatestELCRoundsLib} from "./ELCRound.sol"; import {RoundTimingInfo, RoundTimingInfoLib} from "./RoundTimingInfo.sol"; @@ -61,7 +61,7 @@ import {RoundTimingInfo, RoundTimingInfoLib} from "./RoundTimingInfo.sol"; // 4. during round 2 // * same as above, but can finalize the withdrawal -// CHRIS: TODO: add ability to set the transferrer of controller rights +// CHRIS: TODO: add ability to set the transferor of controller rights // CHRIS: TODO: rewrite the spec to have offchain and onchain components // CHRIS: TODO: describe the different actors in the system @@ -134,7 +134,7 @@ contract ExpressLaneAuction is uint256 public beneficiaryBalance; /// @inheritdoc IExpressLaneAuction - mapping(address => Transferrer) public transferrerOf; + mapping(address => Transferor) public transferorOf; /// @inheritdoc IExpressLaneAuction function initialize(InitArgs memory args) public initializer onlyDelegated { @@ -344,6 +344,7 @@ contract ExpressLaneAuction is biddingForRound, address(0), firstPriceBid.expressLaneController, + address(0), roundStart, roundEnd ); @@ -483,20 +484,20 @@ contract ExpressLaneAuction is ); } - /// @notice Sets a transferrer for an express lane controller - /// The transferrer is an address that will have the right to transfer express lane controller rights - /// on behalf an express lane controller. - /// @param transferrer The transferrer to set - function setTransferrer(Transferrer calldata transferrer) external { - // if a transferrer has already been set, it may be fixed until a future round - Transferrer storage currentTransferrer = transferrerOf[msg.sender]; - if(currentTransferrer.addr != address(0) && currentTransferrer.fixedUntilRound > roundTimingInfo.currentRound()){ - revert FixedTransferrer(currentTransferrer.fixedUntilRound); + /// @inheritdoc IExpressLaneAuction + function setTransferor(Transferor calldata transferor) external { + // if a transferor has already been set, it may be fixed until a future round + Transferor storage currentTransferor = transferorOf[msg.sender]; + if ( + currentTransferor.addr != address(0) && + currentTransferor.fixedUntilRound > roundTimingInfo.currentRound() + ) { + revert FixedTransferor(currentTransferor.fixedUntilRound); } - transferrerOf[msg.sender] = transferrer; + transferorOf[msg.sender] = transferor; - emit SetTransferrer(msg.sender, transferrer.addr, transferrer.fixedUntilRound); + emit SetTransferor(msg.sender, transferor.addr, transferor.fixedUntilRound); } /// @inheritdoc IExpressLaneAuction @@ -514,26 +515,25 @@ contract ExpressLaneAuction is ELCRound storage resolvedRound = latestResolvedRounds.resolvedRound(round); address resolvedELC = resolvedRound.expressLaneController; - // CHRIS: TODO: - // address transferrer = transferrers[resolvedELC].addr; - // if(transferrer != address(0)) { - // if(transferrer != msg.sender) { - // revert("hi"); - // } - // } else - if (resolvedELC != msg.sender) { + address transferor = transferorOf[resolvedELC].addr; + // can only be the transferor if one has been set + // otherwise we default to the express lane controller to do the transfer + if (transferor != address(0)) { + if (transferor != msg.sender) { + revert NotTransferor(transferor, msg.sender); + } + } else if (resolvedELC != msg.sender) { revert NotExpressLaneController(round, resolvedELC, msg.sender); } - + resolvedRound.expressLaneController = newExpressLaneController; (uint64 start, uint64 end) = info.roundTimestamps(round); - // CHRIS: TODO: add the transferrer here? - // CHRIS: TODO: if reset after transfer then 0 out the transferrer emit SetExpressLaneController( round, resolvedELC, newExpressLaneController, + transferor != address(0) ? transferor : resolvedELC, start < uint64(block.timestamp) ? uint64(block.timestamp) : start, end ); diff --git a/src/express-lane-auction/IExpressLaneAuction.sol b/src/express-lane-auction/IExpressLaneAuction.sol index b25a3939..da26f5d2 100644 --- a/src/express-lane-auction/IExpressLaneAuction.sol +++ b/src/express-lane-auction/IExpressLaneAuction.sol @@ -25,14 +25,14 @@ struct Bid { bytes signature; } -/// @notice Sets a transferrer for an express lane controller -/// The transferrer is an address that will have the right to transfer express lane controller rights +/// @notice Sets a transferor for an express lane controller +/// The transferor is an address that will have the right to transfer express lane controller rights /// on behalf an express lane controller. -struct Transferrer { - /// @notice The address of the transferrer +struct Transferor { + /// @notice The address of the transferor address addr; - /// @notice The express lane controller can choose to fix the transferrer until a future round number - /// This gives them ability to guarantee to other parties that they will not change transferrer during an ongoing round + /// @notice The express lane controller can choose to fix the transferor until a future round number + /// This gives them ability to guarantee to other parties that they will not change transferor during an ongoing round /// The express lane controller can ignore this feature by setting this value to 0. uint64 fixedUntilRound; } @@ -114,21 +114,27 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg /// @param round The round which the express lane controller will control /// @param previousExpressLaneController The previous express lane controller /// @param newExpressLaneController The new express lane controller + /// @param transferor The address that transferored the controller rights. The transferor if set, otherwise the express lane controller /// @param startTimestamp The timestamp at which the new express lane controller takes over /// @param endTimestamp The timestamp at which the new express lane controller will cease to have control event SetExpressLaneController( uint64 round, - address previousExpressLaneController, - address newExpressLaneController, + address indexed previousExpressLaneController, + address indexed newExpressLaneController, + address indexed transferor, uint64 startTimestamp, uint64 endTimestamp ); - /// @notice A new transferrer has been set for - /// @param expressLaneController The express lane controller that has a transferrer - /// @param transferrer The transferrer chosen - /// @param fixedUntilRound The round until which this transferrer is fixed for this controller - event SetTransferrer(address indexed expressLaneController, address indexed transferrer, uint64 fixedUntilRound); + /// @notice A new transferor has been set for + /// @param expressLaneController The express lane controller that has a transferor + /// @param transferor The transferor chosen + /// @param fixedUntilRound The round until which this transferor is fixed for this controller + event SetTransferor( + address indexed expressLaneController, + address indexed transferor, + uint64 fixedUntilRound + ); /// @notice The minimum reserve price was set /// @param oldPrice The previous minimum reserve price @@ -193,11 +199,13 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg /// This is a gas optimisation to avoid making a transfer every time an auction is resolved function beneficiaryBalance() external returns (uint256); - /// @notice Express lane controllers can optionally set a transferrer address that has the rights - /// to transfer their controller rights. This function returns the transferrer if one has been set - /// Returns the transferrer for the supplied controller, and the round until which this - /// transferrer is fixed if set. - function transferrerOf(address expressLaneController) external returns (address addr, uint64 fixedUntil); + /// @notice Express lane controllers can optionally set a transferor address that has the rights + /// to transfer their controller rights. This function returns the transferor if one has been set + /// Returns the transferor for the supplied controller, and the round until which this + /// transferor is fixed if set. + function transferorOf(address expressLaneController) + external + returns (address addr, uint64 fixedUntil); /// @notice Initialize the auction /// @param args Initialization parameters @@ -330,6 +338,12 @@ interface IExpressLaneAuction is IAccessControlEnumerableUpgradeable, IERC165Upg function resolveMultiBidAuction(Bid calldata firstPriceBid, Bid calldata secondPriceBid) external; + /// @notice Sets a transferor for an express lane controller + /// The transferor is an address that will have the right to transfer express lane controller rights + /// on behalf an express lane controller. + /// @param transferor The transferor to set + function setTransferor(Transferor calldata transferor) external; + /// @notice Express lane controllers are allowed to transfer their express lane rights for the current or future /// round to another address. They may use this for reselling their rights after purchasing them /// @param round The round to transfer rights for diff --git a/test/foundry/ExpressLaneAuction.t.sol b/test/foundry/ExpressLaneAuction.t.sol index 7d285af3..e9a8b0e9 100644 --- a/test/foundry/ExpressLaneAuction.t.sol +++ b/test/foundry/ExpressLaneAuction.t.sol @@ -40,12 +40,18 @@ contract ExpressLaneAuctionTest is Test { event SetMinReservePrice(uint256 oldPrice, uint256 newPrice); event SetExpressLaneController( uint64 round, - address from, - address to, + address indexed from, + address indexed to, + address indexed transferor, uint64 startTimestamp, uint64 endTimestamp ); event SetBeneficiary(address oldBeneficiary, address newBeneficiary); + event SetTransferor( + address indexed expressLaneController, + address indexed transferor, + uint64 fixedUntilRound + ); uint64 roundDuration = 60; // 1 min @@ -1195,6 +1201,7 @@ contract ExpressLaneAuctionTest is Test { biddingForRound, address(0), bidders[1].elc, + address(0), uint64(block.timestamp + auctionClosingSeconds), uint64(block.timestamp + auctionClosingSeconds + roundDurationSeconds - 1) ); @@ -1306,6 +1313,7 @@ contract ExpressLaneAuctionTest is Test { biddingForRound, address(0), bidders[3].elc, + address(0), uint64(block.timestamp + auctionClosingSeconds), uint64(block.timestamp + auctionClosingSeconds + roundDurationSeconds - 1) ); @@ -1450,6 +1458,7 @@ contract ExpressLaneAuctionTest is Test { biddingForRound, address(0), bidders[1].elc, + address(0), uint64(block.timestamp + auctionClosingSeconds), uint64(block.timestamp + auctionClosingSeconds + roundDurationSeconds - 1) ); @@ -1661,7 +1670,14 @@ contract ExpressLaneAuctionTest is Test { (uint64 start, uint64 end) = rs.auction.roundTimestamps(testRound + 1); vm.prank(bidders[1].elc); vm.expectEmit(true, true, true, true); - emit SetExpressLaneController(testRound + 1, bidders[1].elc, bidders[0].elc, start, end); + emit SetExpressLaneController( + testRound + 1, + bidders[1].elc, + bidders[0].elc, + bidders[1].elc, + start, + end + ); rs.auction.transferExpressLaneController(testRound + 1, bidders[0].elc); (, uint64 roundDurationSeconds, , ) = rs.auction.roundTimingInfo(); @@ -1680,6 +1696,7 @@ contract ExpressLaneAuctionTest is Test { testRound + 1, bidders[0].elc, bidders[1].elc, + bidders[0].elc, uint64(block.timestamp), end ); @@ -1715,6 +1732,7 @@ contract ExpressLaneAuctionTest is Test { testRound + 1, bidders[1].elc, bidders[0].elc, + bidders[1].elc, uint64(block.timestamp), end ); @@ -1736,8 +1754,85 @@ contract ExpressLaneAuctionTest is Test { end = end + roundDuration; vm.prank(bidders[3].elc); vm.expectEmit(true, true, true, true); - emit SetExpressLaneController(testRound + 2, bidders[3].elc, bidders[2].elc, start, end); + emit SetExpressLaneController( + testRound + 2, + bidders[3].elc, + bidders[2].elc, + bidders[3].elc, + start, + end + ); rs.auction.transferExpressLaneController(testRound + 2, bidders[2].elc); + + // set a transferor and have them transfer + vm.prank(bidders[2].elc); + rs.auction.setTransferor(Transferor(bidders[2].addr, 1000)); + + vm.prank(bidders[3].elc); + vm.expectRevert( + abi.encodeWithSelector(NotTransferor.selector, bidders[2].addr, bidders[3].elc) + ); + rs.auction.transferExpressLaneController(testRound + 2, bidders[2].elc); + + // change next now + vm.prank(bidders[2].addr); + vm.expectEmit(true, true, true, true); + emit SetExpressLaneController( + testRound + 2, + bidders[2].elc, + bidders[3].elc, + bidders[2].addr, + start, + end + ); + rs.auction.transferExpressLaneController(testRound + 2, bidders[3].elc); + } + + function testSetTransferor() public { + (, IExpressLaneAuction auction) = deploy(); + + address elc = vm.addr(1559); + address transferor = vm.addr(1560); + address transferor2 = vm.addr(1561); + uint64 fixedUntilRound = 137; + address actualTransferor; + uint64 actualFixedUntil; + (actualTransferor, actualFixedUntil) = auction.transferorOf(elc); + assertEq(actualTransferor, address(0)); + assertEq(actualFixedUntil, 0); + + vm.prank(elc); + vm.expectEmit(true, true, true, true); + emit SetTransferor(elc, transferor, 0); + auction.setTransferor(Transferor({addr: transferor, fixedUntilRound: 0})); + (actualTransferor, actualFixedUntil) = auction.transferorOf(elc); + assertEq(actualTransferor, transferor); + assertEq(actualFixedUntil, 0); + + vm.prank(elc); + vm.expectEmit(true, true, true, true); + emit SetTransferor(elc, transferor2, fixedUntilRound); + auction.setTransferor(Transferor({addr: transferor2, fixedUntilRound: fixedUntilRound})); + (actualTransferor, actualFixedUntil) = auction.transferorOf(elc); + assertEq(actualTransferor, transferor2); + assertEq(actualFixedUntil, fixedUntilRound); + + vm.prank(elc); + vm.expectRevert(abi.encodeWithSelector(FixedTransferor.selector, fixedUntilRound)); + auction.setTransferor(Transferor({addr: transferor, fixedUntilRound: fixedUntilRound + 1})); + + while (auction.currentRound() < fixedUntilRound) { + vm.warp(block.timestamp + roundDuration); + } + + assertEq(auction.currentRound(), fixedUntilRound); + vm.prank(elc); + vm.expectEmit(true, true, true, true); + emit SetTransferor(elc, transferor, fixedUntilRound + 1); + auction.setTransferor(Transferor({addr: transferor, fixedUntilRound: fixedUntilRound + 1})); + (actualTransferor, actualFixedUntil) = auction.transferorOf(elc); + assertEq(actualTransferor, transferor); + assertEq(actualFixedUntil, fixedUntilRound + 1); } function testSetBeneficiary() public { From a63bdee7f20656a189d1f57cadb063ffec316c8e Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Mon, 5 Aug 2024 18:10:27 +0100 Subject: [PATCH 4/6] Comment update --- src/express-lane-auction/ExpressLaneAuction.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/express-lane-auction/ExpressLaneAuction.sol b/src/express-lane-auction/ExpressLaneAuction.sol index 8343cda0..e4e2edca 100644 --- a/src/express-lane-auction/ExpressLaneAuction.sol +++ b/src/express-lane-auction/ExpressLaneAuction.sol @@ -61,8 +61,6 @@ import {RoundTimingInfo, RoundTimingInfoLib} from "./RoundTimingInfo.sol"; // 4. during round 2 // * same as above, but can finalize the withdrawal -// CHRIS: TODO: add ability to set the transferor of controller rights - // CHRIS: TODO: rewrite the spec to have offchain and onchain components // CHRIS: TODO: describe the different actors in the system // CHRIS: TODO: examine all the different actors in the system, how can they affect other parties From 795ea3e659d3a65e14bd7526e3e302b0ef4e5531 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Thu, 15 Aug 2024 12:35:55 +0100 Subject: [PATCH 5/6] Updated NotTransferor address to match NotExpressLaneController --- src/express-lane-auction/Errors.sol | 2 +- src/express-lane-auction/ExpressLaneAuction.sol | 2 +- test/foundry/ExpressLaneAuction.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/express-lane-auction/Errors.sol b/src/express-lane-auction/Errors.sol index a5dccc02..4034ac09 100644 --- a/src/express-lane-auction/Errors.sol +++ b/src/express-lane-auction/Errors.sol @@ -21,4 +21,4 @@ error RoundTooOld(uint256 round, uint256 currentRound); error RoundNotResolved(uint256 round); error NotExpressLaneController(uint64 round, address controller, address sender); error FixedTransferor(uint64 fixedUntilRound); -error NotTransferor(address expectedTransferor, address msgSender); +error NotTransferor(uint64 round, address expectedTransferor, address msgSender); diff --git a/src/express-lane-auction/ExpressLaneAuction.sol b/src/express-lane-auction/ExpressLaneAuction.sol index c0eed06b..277d7466 100644 --- a/src/express-lane-auction/ExpressLaneAuction.sol +++ b/src/express-lane-auction/ExpressLaneAuction.sol @@ -469,7 +469,7 @@ contract ExpressLaneAuction is // otherwise we default to the express lane controller to do the transfer if (transferor != address(0)) { if (transferor != msg.sender) { - revert NotTransferor(transferor, msg.sender); + revert NotTransferor(round, transferor, msg.sender); } } else if (resolvedELC != msg.sender) { revert NotExpressLaneController(round, resolvedELC, msg.sender); diff --git a/test/foundry/ExpressLaneAuction.t.sol b/test/foundry/ExpressLaneAuction.t.sol index f7a75bc7..3c41a0c2 100644 --- a/test/foundry/ExpressLaneAuction.t.sol +++ b/test/foundry/ExpressLaneAuction.t.sol @@ -1750,7 +1750,7 @@ contract ExpressLaneAuctionTest is Test { vm.prank(bidders[3].elc); vm.expectRevert( - abi.encodeWithSelector(NotTransferor.selector, bidders[2].addr, bidders[3].elc) + abi.encodeWithSelector(testRound + 2, NotTransferor.selector, bidders[2].addr, bidders[3].elc) ); rs.auction.transferExpressLaneController(testRound + 2, bidders[2].elc); From 4301639be3289e87a60a948c12f688bd08a14223 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Thu, 15 Aug 2024 12:39:21 +0100 Subject: [PATCH 6/6] Formatting --- test/foundry/ExpressLaneAuction.t.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/foundry/ExpressLaneAuction.t.sol b/test/foundry/ExpressLaneAuction.t.sol index 3c41a0c2..1c954309 100644 --- a/test/foundry/ExpressLaneAuction.t.sol +++ b/test/foundry/ExpressLaneAuction.t.sol @@ -1750,7 +1750,12 @@ contract ExpressLaneAuctionTest is Test { vm.prank(bidders[3].elc); vm.expectRevert( - abi.encodeWithSelector(testRound + 2, NotTransferor.selector, bidders[2].addr, bidders[3].elc) + abi.encodeWithSelector( + testRound + 2, + NotTransferor.selector, + bidders[2].addr, + bidders[3].elc + ) ); rs.auction.transferExpressLaneController(testRound + 2, bidders[2].elc);