OrderBook

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

OrderBook Funds Can Be Frozen Due to USDC Blacklisting

Root + Impact

Description

The OrderBook contract uses Circle's official USDC for pricing, settlements and fees. While this behaves like a standard ERC-20 in most interactions, Circle retails centralized control over the token contract. This includes the ability to blacklist addresses, effectibely preventing them from sending or receiving USDC. If the OrderBook contract is blacklisted by Circle, all USDC-based operations will fail, including order purchases and fee withdrawals.

This is critical centralization-based DoS risk.

function buyOrder(uint256 _orderId) public {
...
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee); // @> Will revert if contract is blacklisted
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives); // @> Will revert if order.seller is blacklisted
...
}
function withdrawFees(address _to) external onlyOwner {
...
iUSDC.safeTransfer(_to, totalFees); // @> Will revert if contract is blacklisted
...
}

Risk

Likelihood: Low

Assuming the OrderBook contract is compliant the likelihood is set to low, however, USDC's blacklisting function is already implemented and used. Any trigger can cause OrderBook contract to be blacklisted without warning.

Impact: High

All new buy orders will fail because the contract cannot receive USDC from buyers. Fees cannot be withdrawn, locking protocol earnings indefinitely.

Proof of Concept

Below a test that simulates the blacklisting behavior is provided

contract MockBlacklistedUSDC is ERC20 {
uint8 tokenDecimals;
constructor(uint8 _tokenDecimals) ERC20("USD Coin", "USDC") {
tokenDecimals = _tokenDecimals;
}
function mint(address to, uint256 value) public {
uint256 updateDecimals = uint256(tokenDecimals);
_mint(to, (value * 10 ** updateDecimals));
}
function transfer(address, uint256) public override returns (bool) {
revert("Address blacklisted");
}
function transferFrom(address, address, uint256) public override returns (bool) {
revert("Address blacklisted");
}
}
function test_buyOrderFailsIfBlacklistedUSDC() public {
MockBlacklistedUSDC mockBlacklistedUsdc = new MockBlacklistedUSDC(6);
OrderBook orderBook =
new OrderBook(address(weth), address(wbtc), address(wsol), address(mockBlacklistedUsdc), owner);
vm.startPrank(alice);
wbtc.approve(address(orderBook), 2e8);
uint256 aliceId = orderBook.createSellOrder(address(wbtc), 2e8, 180_000e6, 2 days);
vm.stopPrank();
mockBlacklistedUsdc.mint(dan, 200_000);
vm.startPrank(dan);
mockBlacklistedUsdc.approve(address(orderBook), 200_000);
vm.expectRevert("Address blacklisted");
orderBook.buyOrder(aliceId);
vm.stopPrank();
}

Recommended Mitigation

Consider holding funds in escrow-like systems that can be withdrawn even in blacklisting scenarios

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.