###### tags: `Preliminary Report` Ubeswap limit order Audit -- Preliminary Issue Analysis === > Copyright © 2022 by Verilog. All rights reserved. > Mar 20, 2022 > by **Verilog Audit** This is the preliminary audit report to summarize the **most urgent and critical issues** found by the Verilog Audit. We will list the major concerns and findings here. Once the issues below are all resolved, we can proceed to a lower-level security and improvement analysis. --- ## Table of Content [TOC] --- ## Priviliged Roles ## Findings & Improvement Suggestions #### <html></html> <style> .info { background-color:mediumseagreen; font-size: 12px; color: white; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .minor { background-color: #698999; font-size: 12px; color: white; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .medium { background-color: #FFCA0F; color: #121212; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .major{ background-color: #FF6B4A; color: white; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .critical{ background-color: #FF0000; color: white; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style> <span class='info'>Informational</span><span class='minor'>Minor</span><span class='medium'>Medium</span><span class='major'>Major</span><span class='critical'>Critical</span> | | Total | Acknowledged | Resolved | | ------------- | ----- | ------------ | -------- | | Critical | 0 | 0 | 0 | | Major | 1 | 1 | 1 | | Medium | 1 | 1 | 1 | | Minor | 1 | 1 | 1 | | Informational | 3 | 3 | 0 | <script type="text/javascript"> var i = document.getElementsByClassName('critical'); document.getElementById('critical').innerHTML; </script> ### Critical none ;) ### Major 1. Unlimited `broadcastOrder()` function calls at `OrderBookWithFee.sol` <span class='major'>Major</span> **Description**: Users can call `broadcastOrder()` function for unlimited times given they provide correct `_order` and `_signature`. Non-users can also observe exisiting `_order` and `_signature`, and then perform mimicry attack. **Recommendation**: Adding a mapping to check whether this order has been created or not. **Result**: Discussed with project team, a mapping to protect re-using order will be considered. ### Medium 1. Reentrancy Risk at `OrderBookWithFee.sol` function `broadcastOrder()`. <span class='medium'>medium</span> **Description**: `address _notificationTarget` parameter can be passed by users. ```solidity= /// @notice Logs an order/signature pair. Also executes the order placement fee. /// @param _order The order to broadcast /// @param _signature A valid signature of _order /// @param _notificationTarget A notification target. Disable by providing the 0 address function broadcastOrder( LimitOrderProtocol.Order memory _order, bytes calldata _signature, address _notificationTarget ) public { if (feeRecipient != address(0) && fee > 0) { uint256 feeAmount = _order.makingAmount.mul(fee).div( PCT_DENOMINATOR ); if (feeAmount > 0) { IERC20(_order.makerAsset).safeTransferFrom( msg.sender, feeRecipient, feeAmount ); } if (_notificationTarget != address(0)) { IOrderNotificationReceiver(_notificationTarget) .notifyOrderBroadcasted(_order, msg.sender); } } _broadcastOrder(_order, _signature); } ``` Thus, users can pass an attacker contract address such as below to get benefit from the rewards. ```solidity= contract Attacker is IOrderNotificationReceiver{ //malicious version function notifyOrderBroadcasted( LimitOrderProtocol.Order memory _order, address _rewardRecipient ) public { broadcastOrder(order, sig, address(distributor)); // pass ligit reward distributor's address // to earn profit from reward broadcastOrder(order, sig, address(this)); // pass attacker's contract address // to trigger a new reenter stack. } } ``` ``` **Call stack presentation:** broadcastOrder(Attacker) └─ Attacker.notifyOrderBroadcasted() ├─ broadcastOrder(Distributor) (Line 7) │ └─Distributor.notifyOrderBroadcasted() <= earn reward └─ broadcastOrder(Attacker) (Line 10) └─ Attacker.notifyOrderBroadcasted() ├─ broadcastOrder(Distributor) (Line 7) │ └─Distributor.notifyOrderBroadcasted() <= earn reward └─ broadcastOrder(Attacker) (Line 10) ... loop ``` **Recommendation**: Protect `broadcastOrder()` with `nonReentrant` modifier. **Result**: Discussed with project team, nonReentrancy protection will be added, and they have no intention of giving out higher reward than fee. ### Minor 1. Missing reentrancy protection at `OrderMixin._fillOrderTo()`. <span class='minor'>minor</span> **Description**: missing reentrancy protection at `OrderMixin._fillOrderTo()`. The function `_fillOrderTo()` transfers asset from maker to target first. Then have a function call with data passed by `msg.sender`. It calls the function `notifyFillOrder` of the address decoded from the data, which gives oppotunities to the reentrancy actions. ```solidity // Maker => Taker _makeCall( order.makerAsset, abi.encodePacked( IERC20.transferFrom.selector, uint256(uint160(order.maker)), uint256(uint160(target)), orderAmounts.makingAmount, order.makerAssetData ) ); // Handle external extraInteraction if (extraInteraction.length >= 20) { // proceed only if interaction length is enough to store address (address interactionTarget, bytes memory interactionData) = extraInteraction.decodeTargetAndCalldata(); InteractiveNotificationReceiver(interactionTarget).notifyFillOrder( msg.sender, order.makerAsset, order.takerAsset, orderAmounts.makingAmount, orderAmounts.takingAmount, interactionData ); } // Taker => Maker _makeCall( order.takerAsset, abi.encodePacked( IERC20.transferFrom.selector, uint256(uint160(msg.sender)), uint256(uint160(order.receiver == address(0) ? order.maker : order.receiver)), orderAmounts.takingAmount, order.takerAssetData ) ); ``` **Recommendation**: transfer `msg.sender`'s token to maker first or add a nonReentrant modifier to protect this function. **Result**: Acknowledged without action due to low risk ### Informational 1. `OrderBookWithFee.sol` does not include functionalites in `OrderMixin.sol` and `OrderRFQMixin.sol`. <span class='info'>Informational</span> **Description**: The broadcast and fee payment mechanism is stand-alone and does not include `OrderFill()` functions. In this case, users can bypass the fee payment and use order book functions solely. **Recommendation**: Discussion needed **Result**: Discussed with project team, and this design is made intentionally. 2. Do not transfer reward if the contract has insufficient token amount. <span class='info'>Informational</span> **Description**: In `OrderBookRewardDistributor.sol L83`, `notifyOrderBroadcasted()` function does not transfer reward if the contract has insufficient token amount. **Recommendation**: Transfer all the remaining tokens as reward. **Result**: Acknowledged without action 3. Informational</span> Duplicate code. <span class='info'>Informational</span> **Description**: In `Permitable.sol`, the `_permit()` and `_permitMemory()` functions are duplicates. `Permit memory` is never used. **Recommendation**: Replace `_permitMemory()` with `_permit()`. `Permit calldata` is more gas-efficient. **Result**: Acknowledged without action due to low risk