There's an issue in StabilityBranch.fulfillSwap resulting in a portion of the total expected amount out being lost while splitting the user funds and the protocol rewards.
The fees in fulfillSwap will be distributed as ctx.protocolReward for the protocolFeeRecipients and ctx.amountOut for the user.
However, ctx.protocolReward summed with ctx.amountOut will be not be equal to ctx.amountOutBeforeFeesX18.
The issue occurs because of the following calculations:
As we can see, the amountOut is the amountOutBeforeFees subtracted with the sum of baseFee and swapFee, while the protocolReward is the baseFee summed with the multiplication of the swapFee with the totalFeeRecipientsShare.
This will result in some value being lost between amountOutBeforeFees and the values effectively being distributed via token transfers: ctx.protocolReward and ctx.amountOut.
We can verify this with the following snippet computations below.
Please note swapFee and baseFee are resulting in very small values in the existing tests, e.g. it's resulting in 0 or 1 wei, therefore we'll use some fee values greather than 1 wei to demonstrate this accumulation error and to highlight the value being lost. We are assuming that baseFee and swapFee should have non negligible values in some cases, otherwise there wouldn't be a reason for these fees to be defined.
We will also be using the values with 18 decimals as uint256 instead of ud60 for ease of demonstration.
With these values, the protocolFeeRecipients will receive 37e18 and the user will receive 900e18, but 63e18 from amountOutBeforeFees would be lost since 1000e18 - 900e18 - 37e18 = 63e18.
This will cause expected values not being distributed while fulfilling a swap from usd to a collateral from the vault.
Although there's a slippage protection, the impact is considerable as it results in assets not being delivered correctly to the user. The bigger the fees the bigger the discrepancy can be.
Likelihood is high as it can happen at every swap being that gets fulfilled - assuming swapFee and baseFee have substantial values greater than 1 wei.
Manual review.
Calculate the protocolReward first and then calculate the amountOut as amountOutBeforeFees decremented by the protocolReward.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.