OrderBook

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

Centralization Risk via Arbitrary Token Allowlisting and Emergency Withdrawal

Root + Impact

Description

The protocol is expected to handle a fixed and trusted set of ERC20 tokens ensuring a consistent economic model and preventing misuse of token flows. However, the setAllowedToken function allows the contract owner to arbitrarily whitelist any ERC-20 token as valid "core" sell token. This creates centralization risk where the owner can whitelist a token, wait for users to create sell orders with that token, and later drain their deposits via emergencyWithdrawERC20, bypassing all protocol logic.

Risk

Likelihood: High

This will occur whenever the owner intentionally or mistakenly enables an unsupported token for trade. Risk increases in DAO-controlled or externally governed environments, where administrative access can be exploited or missued.

Impact: High

User funds locked for sell orders can be extracted by owner using emergencyWithdrawERC20, breaking user trust and leading to potential loss of user assets.

Proof of Concept

function test_addNewCoreTokenAndDrain() public {
ERC20Mock mockERC20 = new ERC20Mock();
vm.prank(owner);
book.setAllowedSellToken(address(mockERC20), true); // Arbitrary token added
deal(address(mockERC20), alice, 1000e18);
vm.startPrank(alice);
mockERC20.approve(address(book), 1000e18);
book.createSellOrder(address(mockERC20), 1000e18, 1000e6, 2 days);
vm.stopPrank();
assertEq(mockERC20.balanceOf(alice), 0);
assertEq(mockERC20.balanceOf(address(book)), 1000e18);
// Owner drains funds via emergency withdraw
vm.prank(owner);
book.emergencyWithdrawERC20(address(mockERC20), 1000e18, owner);
assertEq(mockERC20.balanceOf(alice), 0);
assertEq(mockERC20.balanceOf(address(book)), 0);
assertEq(mockERC20.balanceOf(owner), 1000e18);
}

Recommended Mitigation

Use all the immutable supported tokens in the validation of setAllowedSellToken

if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken();
+ if( _token != address(iWETH) || _token != address(iWBTC) || _token != address(iWSOL)) revert InvalidToken();
allowedSellToken[_token] = _isAllowed;
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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