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);
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
...
}
function withdrawFees(address _to) external onlyOwner {
...
iUSDC.safeTransfer(_to, totalFees);
...
}
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