From 5d0c1ee00e7b25fefd9f2cf3277c0eb37e3a0734 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Thu, 19 Sep 2024 17:21:21 -0400 Subject: [PATCH 1/4] feat: add builder system transactions --- lib/solmate | 1 + src/BuilderSystemTransactions.sol | 73 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 160000 lib/solmate create mode 100644 src/BuilderSystemTransactions.sol diff --git a/lib/solmate b/lib/solmate new file mode 160000 index 0000000..97bdb20 --- /dev/null +++ b/lib/solmate @@ -0,0 +1 @@ +Subproject commit 97bdb2003b70382996a79a406813f76417b1cf90 diff --git a/src/BuilderSystemTransactions.sol b/src/BuilderSystemTransactions.sol new file mode 100644 index 0000000..3d68f09 --- /dev/null +++ b/src/BuilderSystemTransactions.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Owned} from "solmate/auth/Owned.sol"; + +/// @title Builder System Transactions +contract BuilderSystemTransactions is Owned(msg.sender) { + error Unauthorized(); + event BuilderAdded(address indexed builder); + event BuilderRemoved(address indexed builder); + event FlashblockIndexSet(uint8 flashblockIndex); + + mapping(address => bool) public builders; + uint8 public flashblockIndex; + + modifier onlyBuilder() { + if (!builders[msg.sender]) { + revert Unauthorized(); + } + _; + } + + constructor(address[] memory initialBuilders) { + for (uint256 i = 0; i < initialBuilders.length; i++) { + builders[initialBuilders[i]] = true; + } + } + + /// @notice Sets the flashblock index + /// @dev Only a builder can set the flashblock index + /// @param _flashblockIndex The new flashblock index + function setFlashblockIndex(uint8 _flashblockIndex) external onlyBuilder { + flashblockIndex = _flashblockIndex; + emit FlashblockIndexSet(_flashblockIndex); + } + + /// @notice Gets the current flashblock index + /// @return flashblockIndex The current flashblock index + function getFlashblockIndex() external view returns (uint8) { + return uint8(flashblockIndex); + } + + /// @notice Gets the "Unichain Block" using the current block number and flashblock index + /// @dev The Unichain Block that uniquely identifies the current block and flashblock + /// The top bit is flipped to indicate it is a Unichain Block + /// The block number is shifted by 8 bits to the left + /// The flashblock index is inserted in the last 8 bits + /// @return unichainBlock The Unichain Block + function getUnichainBlock() external view returns (uint256 unichainBlock) { + // flip the top bit to indicate it is a unichain block + unichainBlock |= 1 << 255; + // insert the block number shifted by 8 bits + unichainBlock |= block.number << 8; + // insert the flashblock index and return + return unichainBlock |= flashblockIndex; + } + + /// @notice Adds a builder to the list of builders + /// @dev Only the owner can add a builder + /// @param builder The address of the builder to add + function addBuilder(address builder) external onlyOwner { + builders[builder] = true; + emit BuilderAdded(builder); + } + + /// @notice Removes a builder from the list of builders + /// @dev Only the owner can add a builder + /// @param builder The address of the builder to remove + function removeBuilder(address builder) external onlyOwner { + builders[builder] = false; + emit BuilderRemoved(builder); + } +} From 9f902564b96fde1da74ed8595a119a67ecba9460 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Wed, 20 Nov 2024 15:53:15 -0500 Subject: [PATCH 2/4] fix: increment and add tests this commit updates the flashblock logic to increment rather than set arbitrarily --- src/BuilderSystemTransactions.sol | 43 +++++++------ test/BuilderSystemTransactions.t.sol | 90 ++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 test/BuilderSystemTransactions.t.sol diff --git a/src/BuilderSystemTransactions.sol b/src/BuilderSystemTransactions.sol index 3d68f09..029c233 100644 --- a/src/BuilderSystemTransactions.sol +++ b/src/BuilderSystemTransactions.sol @@ -1,17 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Owned} from "solmate/auth/Owned.sol"; +import {Owned} from 'solmate/auth/Owned.sol'; /// @title Builder System Transactions contract BuilderSystemTransactions is Owned(msg.sender) { error Unauthorized(); + error InvalidFlashblockIndex(); + event BuilderAdded(address indexed builder); event BuilderRemoved(address indexed builder); event FlashblockIndexSet(uint8 flashblockIndex); mapping(address => bool) public builders; - uint8 public flashblockIndex; + uint8 public lastFlashblockIndex; + uint256 public lastBlockNumber; modifier onlyBuilder() { if (!builders[msg.sender]) { @@ -26,33 +29,27 @@ contract BuilderSystemTransactions is Owned(msg.sender) { } } - /// @notice Sets the flashblock index + /// @notice Increments the flashblock index /// @dev Only a builder can set the flashblock index - /// @param _flashblockIndex The new flashblock index - function setFlashblockIndex(uint8 _flashblockIndex) external onlyBuilder { - flashblockIndex = _flashblockIndex; - emit FlashblockIndexSet(_flashblockIndex); + /// @dev The flashblock index is reset when a new block is reached + function incrementFlashblockIndex() external onlyBuilder { + uint8 _lastFlashblockIndex; + if (block.number == lastBlockNumber) { + // same block, increment flashblock index + _lastFlashblockIndex = lastFlashblockIndex + 1; + } else { + // new block, reset flashblock index and update block number + lastBlockNumber = block.number; + } + + lastFlashblockIndex = _lastFlashblockIndex; + emit FlashblockIndexSet(_lastFlashblockIndex); } /// @notice Gets the current flashblock index /// @return flashblockIndex The current flashblock index function getFlashblockIndex() external view returns (uint8) { - return uint8(flashblockIndex); - } - - /// @notice Gets the "Unichain Block" using the current block number and flashblock index - /// @dev The Unichain Block that uniquely identifies the current block and flashblock - /// The top bit is flipped to indicate it is a Unichain Block - /// The block number is shifted by 8 bits to the left - /// The flashblock index is inserted in the last 8 bits - /// @return unichainBlock The Unichain Block - function getUnichainBlock() external view returns (uint256 unichainBlock) { - // flip the top bit to indicate it is a unichain block - unichainBlock |= 1 << 255; - // insert the block number shifted by 8 bits - unichainBlock |= block.number << 8; - // insert the flashblock index and return - return unichainBlock |= flashblockIndex; + return uint8(lastFlashblockIndex); } /// @notice Adds a builder to the list of builders diff --git a/test/BuilderSystemTransactions.t.sol b/test/BuilderSystemTransactions.t.sol new file mode 100644 index 0000000..2516815 --- /dev/null +++ b/test/BuilderSystemTransactions.t.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {BuilderSystemTransactions} from '../src/BuilderSystemTransactions.sol'; +import 'forge-std/Test.sol'; + +contract BuilderSystemTransactionsTest is Test { + BuilderSystemTransactions public systemTransactions; + address public owner = makeAddr('owner'); + address public builder1 = makeAddr('builder1'); + address public builder2 = makeAddr('builder2'); + address public nonBuilder = makeAddr('nonBuilder'); + + function setUp() public { + // Deploy contract and set up initial builders + address[] memory initialBuilders = new address[](1); + initialBuilders[0] = builder1; + + vm.prank(owner); + systemTransactions = new BuilderSystemTransactions(initialBuilders); + } + + function testAddBuilder() public { + vm.startPrank(owner); + systemTransactions.addBuilder(builder2); + assertTrue(systemTransactions.builders(builder2)); + vm.stopPrank(); + } + + function testRemoveBuilder() public { + vm.startPrank(owner); + systemTransactions.addBuilder(builder2); + systemTransactions.removeBuilder(builder2); + assertFalse(systemTransactions.builders(builder2)); + vm.stopPrank(); + } + + function testIncrementFlashblockIndexSameBlock() public { + vm.startPrank(builder1); + + // Increment flashblock index for the first time + systemTransactions.incrementFlashblockIndex(); + assertEq(systemTransactions.getFlashblockIndex(), 0); + + // Increment again within the same block + systemTransactions.incrementFlashblockIndex(); + assertEq(systemTransactions.getFlashblockIndex(), 1); + + vm.stopPrank(); + } + + function testIncrementFlashblockIndexNewBlock() public { + vm.startPrank(builder1); + + // Increment flashblock index for the first time + systemTransactions.incrementFlashblockIndex(); + assertEq(systemTransactions.getFlashblockIndex(), 0); + + // Roll to a new block + vm.roll(block.number + 1); + + // Increment in a new block should reset the flashblock index + systemTransactions.incrementFlashblockIndex(); + assertEq(systemTransactions.getFlashblockIndex(), 0); + + vm.stopPrank(); + } + + function testUnauthorizedAccessToIncrementFlashblockIndex() public { + vm.prank(nonBuilder); + vm.expectRevert(BuilderSystemTransactions.Unauthorized.selector); + systemTransactions.incrementFlashblockIndex(); + } + + function testUnauthorizedAccessToAddBuilder() public { + vm.prank(nonBuilder); + vm.expectRevert('UNAUTHORIZED'); + systemTransactions.addBuilder(builder2); + } + + function testUnauthorizedAccessToRemoveBuilder() public { + vm.startPrank(owner); + systemTransactions.addBuilder(builder2); + vm.stopPrank(); + + vm.prank(nonBuilder); + vm.expectRevert('UNAUTHORIZED'); + systemTransactions.removeBuilder(builder2); + } +} From 39717068fc3884ec94dbde3ed434ff90b59e1a17 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Wed, 20 Nov 2024 15:54:01 -0500 Subject: [PATCH 3/4] fix: remove unused error --- src/BuilderSystemTransactions.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BuilderSystemTransactions.sol b/src/BuilderSystemTransactions.sol index 029c233..2cbf020 100644 --- a/src/BuilderSystemTransactions.sol +++ b/src/BuilderSystemTransactions.sol @@ -6,7 +6,6 @@ import {Owned} from 'solmate/auth/Owned.sol'; /// @title Builder System Transactions contract BuilderSystemTransactions is Owned(msg.sender) { error Unauthorized(); - error InvalidFlashblockIndex(); event BuilderAdded(address indexed builder); event BuilderRemoved(address indexed builder); From 9a0bbce9ae769d1466c66739767404d3231491e3 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Wed, 20 Nov 2024 15:55:41 -0500 Subject: [PATCH 4/4] fix: remove unnecessary cast --- src/BuilderSystemTransactions.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BuilderSystemTransactions.sol b/src/BuilderSystemTransactions.sol index 2cbf020..9e44bf8 100644 --- a/src/BuilderSystemTransactions.sol +++ b/src/BuilderSystemTransactions.sol @@ -48,7 +48,7 @@ contract BuilderSystemTransactions is Owned(msg.sender) { /// @notice Gets the current flashblock index /// @return flashblockIndex The current flashblock index function getFlashblockIndex() external view returns (uint8) { - return uint8(lastFlashblockIndex); + return lastFlashblockIndex; } /// @notice Adds a builder to the list of builders