Skip to content

Commit 6227e1a

Browse files
committed
use OpenZeppelin Ownable instead of custom logic, add more best practice thingies
1 parent f63c5e9 commit 6227e1a

5 files changed

Lines changed: 42 additions & 48 deletions

File tree

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "lib/forge-std"]
22
path = lib/forge-std
33
url = https://github.com/foundry-rs/forge-std
4+
[submodule "lib/openzeppelin-contracts"]
5+
path = lib/openzeppelin-contracts
6+
url = https://github.com/OpenZeppelin/openzeppelin-contracts

foundry.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ out = "out"
44
libs = ["lib"]
55
solc = "0.8.25"
66
evm_version = "cancun"
7+
remappings = [
8+
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/"
9+
]
710

811
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

lib/openzeppelin-contracts

Submodule openzeppelin-contracts added at e4f7021

src/LimitOrderBook.sol

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.25;
33

4+
import "@openzeppelin/contracts/access/Ownable.sol";
5+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
6+
47
/**
58
* @title LimitOrderBook
69
* @dev A contract for managing limit orders with off-chain execution
710
* @dev This contract is designed to be used on HyperEVM
811
* @notice Users can place limit orders which are executed by off-chain bots when price conditions are met
912
* @notice Executors can be authorized to mark orders as executed
1013
*/
11-
contract LimitOrderBook {
14+
contract LimitOrderBook is Ownable, ReentrancyGuard {
1215
/*//////////////////////////////////////////////////////////////
1316
ERRORS
1417
//////////////////////////////////////////////////////////////*/
1518

16-
error InvalidPrice();
17-
error InvalidAmount();
18-
error OrderNotFound();
19-
error NotOrderOwner();
20-
error OrderAlreadyExecuted();
21-
error OrderNotExecutable();
22-
error UnauthorizedExecutor();
23-
error NotOwner();
19+
error LimitOrderBook_InvalidPrice();
20+
error LimitOrderBook_InvalidAmount();
21+
error LimitOrderBook_OrderNotFound();
22+
error LimitOrderBook_NotOrderOwner();
23+
error LimitOrderBook_OrderAlreadyExecuted();
24+
error LimitOrderBook_OrderNotExecutable();
25+
error LimitOrderBook_UnauthorizedExecutor();
2426

2527
/*//////////////////////////////////////////////////////////////
2628
TYPES
@@ -42,7 +44,6 @@ contract LimitOrderBook {
4244

4345
// Access control for executors
4446
mapping(address => bool) public s_authorizedExecutors;
45-
address public s_owner;
4647

4748
/*//////////////////////////////////////////////////////////////
4849
EVENTS
@@ -58,27 +59,21 @@ contract LimitOrderBook {
5859
MODIFIERS
5960
//////////////////////////////////////////////////////////////*/
6061

61-
modifier onlyOwner() {
62-
if (msg.sender != s_owner) revert NotOwner();
63-
_;
64-
}
65-
6662
modifier onlyAuthorizedExecutor() {
67-
if (!s_authorizedExecutors[msg.sender]) revert UnauthorizedExecutor();
63+
if (!s_authorizedExecutors[msg.sender]) revert LimitOrderBook_UnauthorizedExecutor();
6864
_;
6965
}
7066

7167
modifier validOrder(uint256 orderId) {
72-
if (s_orders[orderId].user == address(0)) revert OrderNotFound();
68+
if (s_orders[orderId].user == address(0)) revert LimitOrderBook_OrderNotFound();
7369
_;
7470
}
7571

7672
/*//////////////////////////////////////////////////////////////
7773
CONSTRUCTOR
7874
//////////////////////////////////////////////////////////////*/
7975

80-
constructor() {
81-
s_owner = msg.sender;
76+
constructor(address initialOwner) Ownable(initialOwner) {
8277
// Initially, allow anyone to execute (can be changed later)
8378
// This supports the permissionless bot execution model
8479
s_authorizedExecutors[address(0)] = true; // Flag to indicate permissionless execution
@@ -94,10 +89,10 @@ contract LimitOrderBook {
9489
* @param amount The amount/quantity for the order
9590
* @return orderId The unique identifier for the placed order
9691
*/
97-
function placeOrder(uint256 price, uint256 amount) external returns (uint256 orderId) {
92+
function placeOrder(uint256 price, uint256 amount) external nonReentrant returns (uint256 orderId) {
9893
// Validation
99-
if (price == 0) revert InvalidPrice();
100-
if (amount == 0) revert InvalidAmount();
94+
if (price == 0) revert LimitOrderBook_InvalidPrice();
95+
if (amount == 0) revert LimitOrderBook_InvalidAmount();
10196

10297
// Create order
10398
orderId = s_nextOrderId;
@@ -113,14 +108,14 @@ contract LimitOrderBook {
113108
* @notice Cancel an existing order
114109
* @param orderId The ID of the order to cancel
115110
*/
116-
function cancelOrder(uint256 orderId) external validOrder(orderId) {
111+
function cancelOrder(uint256 orderId) external nonReentrant validOrder(orderId) {
117112
Order storage order = s_orders[orderId];
118113

119114
// Verify ownership
120-
if (order.user != msg.sender) revert NotOrderOwner();
115+
if (order.user != msg.sender) revert LimitOrderBook_NotOrderOwner();
121116

122117
// Verify not already executed
123-
if (order.executed) revert OrderAlreadyExecuted();
118+
if (order.executed) revert LimitOrderBook_OrderAlreadyExecuted();
124119

125120
// Delete the order
126121
delete s_orders[orderId];
@@ -133,16 +128,16 @@ contract LimitOrderBook {
133128
* @param orderId The ID of the order to execute
134129
* @dev This function can be called by anyone initially, but access can be restricted
135130
*/
136-
function markExecuted(uint256 orderId) external validOrder(orderId) {
131+
function markExecuted(uint256 orderId) external nonReentrant validOrder(orderId) {
137132
Order storage order = s_orders[orderId];
138133

139134
// Verify not already executed
140-
if (order.executed) revert OrderAlreadyExecuted();
135+
if (order.executed) revert LimitOrderBook_OrderAlreadyExecuted();
141136

142137
// If executor authorization is enabled, check permission
143138
// address(0) flag indicates if permissionless execution is allowed
144139
if (!s_authorizedExecutors[address(0)] && !s_authorizedExecutors[msg.sender]) {
145-
revert UnauthorizedExecutor();
140+
revert LimitOrderBook_UnauthorizedExecutor();
146141
}
147142

148143
// Mark as executed
@@ -169,14 +164,6 @@ contract LimitOrderBook {
169164
emit ExecutorRevoked(executor);
170165
}
171166

172-
/**
173-
* @notice Transfer ownership of the contract
174-
* @param newOwner The new owner address
175-
*/
176-
function transferOwnership(address newOwner) external onlyOwner {
177-
s_owner = newOwner;
178-
}
179-
180167
/**
181168
* @notice Enable or disable executor authorization requirement
182169
* @param requireAuth True to require authorization, false for permissionless execution

test/LimitOrderBook.t.sol

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contract LimitOrderBookTest is Test {
1616
event OrderCancelled(uint256 indexed orderId);
1717

1818
function setUp() public {
19-
orderBook = new LimitOrderBook();
19+
orderBook = new LimitOrderBook(address(this)); // Deploy with test contract as initial owner
2020
}
2121

2222
function test_PlaceOrder() public {
@@ -41,13 +41,13 @@ contract LimitOrderBookTest is Test {
4141

4242
function test_PlaceOrder_InvalidPrice() public {
4343
vm.prank(user1);
44-
vm.expectRevert(LimitOrderBook.InvalidPrice.selector);
44+
vm.expectRevert(LimitOrderBook.LimitOrderBook_InvalidPrice.selector);
4545
orderBook.placeOrder(0, 100);
4646
}
4747

4848
function test_PlaceOrder_InvalidAmount() public {
4949
vm.prank(user1);
50-
vm.expectRevert(LimitOrderBook.InvalidAmount.selector);
50+
vm.expectRevert(LimitOrderBook.LimitOrderBook_InvalidAmount.selector);
5151
orderBook.placeOrder(1000, 0);
5252
}
5353

@@ -64,7 +64,7 @@ contract LimitOrderBookTest is Test {
6464
orderBook.cancelOrder(orderId);
6565

6666
// Order should be deleted
67-
vm.expectRevert(LimitOrderBook.OrderNotFound.selector);
67+
vm.expectRevert(LimitOrderBook.LimitOrderBook_OrderNotFound.selector);
6868
orderBook.getOrder(orderId);
6969
}
7070

@@ -75,7 +75,7 @@ contract LimitOrderBookTest is Test {
7575

7676
// Try to cancel as user2
7777
vm.prank(user2);
78-
vm.expectRevert(LimitOrderBook.NotOrderOwner.selector);
78+
vm.expectRevert(LimitOrderBook.LimitOrderBook_NotOrderOwner.selector);
7979
orderBook.cancelOrder(orderId);
8080
}
8181

@@ -90,7 +90,7 @@ contract LimitOrderBookTest is Test {
9090

9191
// Try to cancel executed order
9292
vm.prank(user1);
93-
vm.expectRevert(LimitOrderBook.OrderAlreadyExecuted.selector);
93+
vm.expectRevert(LimitOrderBook.LimitOrderBook_OrderAlreadyExecuted.selector);
9494
orderBook.cancelOrder(orderId);
9595
}
9696

@@ -122,7 +122,7 @@ contract LimitOrderBookTest is Test {
122122

123123
// Try to execute again
124124
vm.prank(executor);
125-
vm.expectRevert(LimitOrderBook.OrderAlreadyExecuted.selector);
125+
vm.expectRevert(LimitOrderBook.LimitOrderBook_OrderAlreadyExecuted.selector);
126126
orderBook.markExecuted(orderId);
127127
}
128128

@@ -155,7 +155,7 @@ contract LimitOrderBookTest is Test {
155155
}
156156

157157
function test_AuthorizeExecutor() public {
158-
address owner = orderBook.s_owner();
158+
address owner = orderBook.owner();
159159

160160
vm.prank(owner);
161161
vm.expectEmit(true, false, false, true);
@@ -166,7 +166,7 @@ contract LimitOrderBookTest is Test {
166166
}
167167

168168
function test_RevokeExecutor() public {
169-
address owner = orderBook.s_owner();
169+
address owner = orderBook.owner();
170170

171171
// First authorize
172172
vm.prank(owner);
@@ -183,15 +183,15 @@ contract LimitOrderBookTest is Test {
183183

184184
function test_OnlyOwnerFunctions() public {
185185
vm.prank(user1);
186-
vm.expectRevert(LimitOrderBook.NotOwner.selector);
186+
vm.expectRevert(); // OpenZeppelin's Ownable will revert with OwnableUnauthorizedAccount
187187
orderBook.authorizeExecutor(executor);
188188

189189
vm.prank(user1);
190-
vm.expectRevert(LimitOrderBook.NotOwner.selector);
190+
vm.expectRevert(); // OpenZeppelin's Ownable will revert with OwnableUnauthorizedAccount
191191
orderBook.revokeExecutor(executor);
192192

193193
vm.prank(user1);
194-
vm.expectRevert(LimitOrderBook.NotOwner.selector);
194+
vm.expectRevert(); // OpenZeppelin's Ownable will revert with OwnableUnauthorizedAccount
195195
orderBook.transferOwnership(user2);
196196
}
197197

0 commit comments

Comments
 (0)