Omniscia BlazeSwap Audit

BlazeSwapBasePair Manual Review Findings

BlazeSwapBasePair Manual Review Findings

BSB-01M: Inexplicable Usage of Unchecked Mathematical Operations

Description:

The linked unchecked code block is ill-advised as the reserve multiplications can in certain edge cases overflow and should be prevented from doing so, as is the case in the original Uniswap implementation.

Impact:

An overflow in the referenced calculation can cause abnormal fee amounts that would in turn negatively impact all fee recipients the BlazeSwap pair supports.

Example:

contracts/core/BlazeSwapBasePair.sol
168unchecked {
169 if (feeOn) kLast = uint256(reserve0) * reserve1; // reserve0 and reserve1 are up-to-date
170}

Recommendation:

We advise the unchecked specifier to be omitted from the referenced calculation to ensure it is performed safely.

Alleviation:

The BlazeSwap team evaluated this exhibit and assessed that an overflow in the referenced case cannot occur given that the maximum result of two uint112 numbers multiplied between them would fit within a uint224 which is a type lower in accuracy than the uint256 that the result is currently stored in. As a result, we consider this exhibit as nullified.

BSB-02M: Incorrect K System Fee Assumptions

Description:

The _recordSwapFees function contains incorrect assumptions as to the Uniswap K-based fee system as it performs a subtraction of two values that have been multiplied between them without any safety checks.

Impact:

An underflow that should otherwise be ignored can currently lead to swaps failing incorrectly. Fees accrued in the pendingFeeTotal variable may also be ultimately unclaimable depending on the design behind the fee share system.

Example:

contracts/core/BlazeSwapBasePair.sol
199function _recordSwapFees(
200 uint112 oldReserve0,
201 uint112 oldReserve1,
202 bool splitFee
203) private {
204 if (kLast != 0) {
205 (uint112 newReserve0, uint112 newReserve1, ) = getReserves();
206 uint256 feeShare = uint256(newReserve0) * newReserve1 - uint256(oldReserve0) * oldReserve1;
207 pendingFeeTotal += feeShare;
208 if (splitFee) {
209 (address splitFeeRecipient, uint256 splitFeeBips) = IBlazeSwapBaseManager(manager).getTradingFeeSplit(
210 msg.sender
211 );
212 if (splitFeeRecipient != address(0) && splitFeeBips > 0) {
213 uint256 splitFeeShare = (feeShare * splitFeeBips) / 100_00;
214 if (feeShare > 0) {
215 uint256 oldFeeShare = pendingFeeShare[splitFeeRecipient];
216 if (oldFeeShare == 0) {
217 pendingFeeAccount.push(splitFeeRecipient);
218 }
219 pendingFeeShare[splitFeeRecipient] += splitFeeShare;
220 }
221 }
222 }
223 }
224}

Recommendation:

We advise each multiplication sum to be stored to a dedicated variable and the function to continue execution only when uint256(newReserve0) * newReserve1 is greater-than (>) the value of uint256(oldReserve0) * oldReserve1 as otherwise it will cause an underflow to halt the transaction contrary to the _mintFee implementation. Additionally, the pendingFeeTotal calculated is not based on the square root of the values and instead uses a dry sum delta which may not be ultimately satisfied by the fee tracked by _mintFee. As a result, we advise the specialized swap fee system to be re-evaluated, expanded in documentation and potentially re-written as it currently appears dis-joint with the actual fees generated by the BlazeSwap model.

Alleviation:

The BlazeSwap team evaluated this exhibit and deemed the current calculations to be correct as the constant product formula of Uniswap V2 that BlazeSwap is derived from guarantees that the new reserve product will be greater than the old reserve product. Additionally, the fee tracked here is the delta in the product rather than the squared root end-fee as those calculations are meant to be carried out in the _mintFee function by design. As a result of BlazeSwap's assessment, we consider this exhibit nullified.