OrderBook

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

Hardcoded `tokenSymbol` prevent dynamic token support

Summary

The contract currently hardcodes token-to-symbol logic for a fixed set of tokens (wETH, wBTC, wSOL). While the owner is allowed to authorize additional tokens via setAllowedSellToken, the system lacks a dynamic mechanism to associate symbols with these new tokens. As a result, future tokens will not have a readable symbol in getOrderDetailsString, degrading UX and possibly affecting front-end or off-chain integrations.

Vulnerability Details

The function getOrderDetailsString(uint256 _orderId) returns order details as a string. It relies on hardcoded if/else if logic to determine the token's symbol:

if (order.tokenToSell == address(iWETH)) {
tokenSymbol = "wETH";
} else if (order.tokenToSell == address(iWBTC)) {
tokenSymbol = "wBTC";
} else if (order.tokenToSell == address(iWSOL)) {
tokenSymbol = "wSOL";
}

However, the setAllowedSellToken(...) function allows the owner to add support for arbitrary ERC-20 tokens. These newly added tokens will not have an associated symbol, causing the tokenSymbol to remain an empty string. This results in misleading or incomplete output.

Impact

  • Ambiguous or incomplete order data may mislead users and reduce trust in the UI

  • Off-chain services parsing or indexing orders may fail or misinterpret data.

  • Manual addition of new tokens to the frontend becomes error-prone and inconsistent with contract logic.

  • There is no scalable way to associate new tokens with their display names

Even though no funds are at risk, it disrupts a feature already exposed in the contract (setAllowedSellToken). The current implementation misleads developers and users by supporting dynamic tokens on-chain but hardcoding the rendering logic, which breaks UX and expected behavior.

Proof of Concept

Use the following POC inside testOrderBook.t.sol that assert that it missing the token symbol (in the order detail it is just empty)

function test_GetOrderDetailsStrings_breaks_with_dynamic_token() public {
address eve = makeAddr("eve_seller");
vm.startPrank(eve);
MockWETH customToken = new MockWETH(18);
customToken.mint(eve, 2e8);
vm.stopPrank();
vm.prank(owner);
book.setAllowedSellToken(address(customToken), true);
vm.startPrank(eve);
customToken.approve(address(book), 2e8);
uint256 eveOrderId = book.createSellOrder(address(customToken), 2e8, 180_000e6, 2 days);
vm.stopPrank();
string memory details = book.getOrderDetailsString(eveOrderId);
assertTrue(bytes(details).length > 0 && indexOf(details, "Selling: 2000000000000000000 w") == -1, "Unexpected token symbol");
console2.log("=== Order Details for Dynamic Token ===");
console2.log(details);
}
function indexOf(string memory _base, string memory _value) internal pure returns (int) {
bytes memory baseBytes = bytes(_base);
bytes memory valueBytes = bytes(_value);
if(valueBytes.length == 0 || valueBytes.length > baseBytes.length) {
return -1;
}
for (uint i = 0; i <= baseBytes.length - valueBytes.length; i++) {
bool found = true;
for (uint j = 0; j < valueBytes.length; j++)
if (baseBytes[i + j] != valueBytes[j]) {
found = false;
break;
}
if (found)
return int(i);
}
return -1;
}

Tools Used

Manual Code review

Recommendations

Refactor the contract to introduce a dynamic mapping of token addresses to their display symbols.

1/ Add a new state variable :

+ mapping(address => string) public tokenSymbols;

2/ Update setAllowedSellToken(...) to also accept a symbol:

function setAllowedSellToken(address _token, bool _isAllowed, string calldata _symbol) external onlyOwner {
if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken();
allowedSellToken[_token] = _isAllowed;
+ tokenSymbols[_token] = _symbol;
emit TokenAllowed(_token, _isAllowed);
}

3/ Modify getOrderDetailsString(...) to get the token symbol based on the mapping

- string memory tokenSymbol;
- if (order.tokenToSell == address(iWETH)) {
- tokenSymbol = "wETH";
- } else if (order.tokenToSell == address(iWBTC)) {
- tokenSymbol = "wBTC";
- } else if (order.tokenToSell == address(iWSOL)) {
- tokenSymbol = "wSOL";
- }
+string memory tokenSymbol = tokenSymbols[order.tokenToSell];
+if (bytes(tokenSymbol).length == 0) {
+ tokenSymbol = "Unknown"; // Or fallback to address
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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