Skip to content

feat: add builder system transactions #5

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions lib/solmate
Submodule solmate added at 97bdb2
69 changes: 69 additions & 0 deletions src/BuilderSystemTransactions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 lastFlashblockIndex;
uint256 public lastBlockNumber;

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 Increments the flashblock index
/// @dev Only a builder can set the flashblock index
/// @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 lastFlashblockIndex;
}

/// @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);
}
}
90 changes: 90 additions & 0 deletions test/BuilderSystemTransactions.t.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading