OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

Duplicate Token Allowance Results in Unnecessary Gas Wasteg

A missing check in setAllowedSellToken() lets the owner waste gas by setting the same token status over and over again.

Description

  • The contract allows the owner to mark any token as "allowed" for selling on the order book by toggling its status via setAllowedSellToken.

  • Currently, the contract does not check whether the token is already in the desired state. As a result, repeatedly setting the same token to the same _isAllowed value leads to redundant storage writes and unnecessary event emissions.

function setAllowedSellToken(address _token, bool _isAllowed) external onlyOwner {
if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken(); // Cannot allow null or USDC itself
@> allowedSellToken[_token] = _isAllowed;
emit TokenAllowed(_token, _isAllowed);
}

Risk

Likelihood:

  • This issue is triggered when the owner calls setAllowedSellToken() with the same value that’s already set — for example, calling setAllowedSellToken(token, true) even though the token is already allowed.

Impact:

  • Each useless call wastes around 3736 gas (as confirmed in the test).

  • The contract writes to storage even though the value is already the same — this is unnecessary and expensive.

  • It pollutes the blockchain with extra logs.

Proof of Concept

This Proof of Concept demonstrates how the setAllowedSellToken() function allows duplicate entries for the same token, resulting in unnecessary gas usage. Here's how the PoC works:

  1. A dummy token address (0x123) is created to simulate a real token.

  2. The token is allowed once using setAllowedSellToken(token, true).

  3. The same function is called again with the same token and the same true status.

  4. Gas usage before and after the second call is measured using gasleft(), and the difference is logged.

function test_duplicateTokenAllowance() public {
address token = address(0x123);
vm.prank(owner);
book.setAllowedSellToken(token, true);
// Second call with same token
vm.startPrank(owner);
uint256 gasBefore = gasleft();
book.setAllowedSellToken(token, true);
uint256 gasAfter = gasleft();
vm.stopPrank();
uint256 gasUsed = gasBefore - gasAfter;
console2.log("Gas wasted due to duplicate token allowance:", gasUsed);
}

Output

Ran 1 test for test/TestOrderBook.t.sol:TestOrderBook
[PASS] test_duplicateTokenAllowance() (gas: 45888)
Logs:
Gas wasted due to duplicate token allowance: 3736

Recommended Mitigation

To avoid unnecessary gas consumption and improve function efficiency, the setAllowedSellToken() function should include a conditional check before updating the mapping.

  1. Before writing to the allowedSellToken mapping, check if the new value is different from the current one.

  2. It ensures that the function can only updates storage when an actual change is needed.

  3. Don't forget to add TokenAlreadyAllowed()custom error in contract.

function setAllowedSellToken(address _token, bool _isAllowed) external onlyOwner {
if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken(); // Cannot allow null or USDC itself
- allowedSellToken[_token] = _isAllowed;
+ if (allowedSellToken[_token] == _isAllowed) {
+ revert TokenAlreadyAllowed();
+ }
+ allowedSellToken[_token] = _isAllowed;
emit TokenAllowed(_token, _isAllowed);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.