The StabilityBranch
contract has a flaw in its slippage protection mechanism. The initiateSwap
function performs slippage checks against a pre-fee amount, while the actual amount users receive is post-fee. This discrepancy allows swaps to be initiated that are guaranteed to fail during fulfillment, leading to temporarily locked funds until refund is initiated.
The vulnerability stems from an inconsistency in how amounts are compared against the user's minAmountOut
parameter between the initiation and fulfillment phases of a swap.
During Initiation (initiateSwap
):
The code calculates expectedAssetOut
using getAmountOfAssetOut()
This calculation only takes into account the premiumDiscountFactor
but does NOT include fees
It then compares this pre-fee amount against the user's minAmountOut
If expectedAssetOut >= minAmountOut
, the swap is initiated
During Fulfillment (fulfillSwap
):
It calculates amountOutBeforeFeesX18
using the same getAmountOfAssetOut()
Then calculates baseFeeX18
and swapFeeX18
Subtracts these fees to get the final amountOut
Compares this post-fee amount against minAmountOut
The issue is that:
During initiation, it compares: expectedAssetOut (pre-fees) >= minAmountOut
During fulfillment, it compares: amountOut (post-fees) >= minAmountOut
This means a swap could be initiated even though the final amount received after fees would be less than minAmountOut
.
Therefore, the check during initiation is incorrect because it uses the pre-fee amount to compare against the user's minAmountOut
. This is a flaw because the user's minAmountOut
is not properly enforced. The user could set a minAmountOut
that's higher than what they will actually receive, leading to the swap being initiated but later failing during fulfillment. Because during fulfillment, the code does check the post-fee amount against minAmountOut
, which would revert if it's insufficient. So the user's transaction during initiateSwap
would pass, but the fulfillSwap
would fail. This leaves the user's funds locked until they can refund, but they have to wait for the deadline.
Example Scenario:
The transaction would fail because:
User expects at least 49.9 ETH (0.2% slippage from 50 ETH)
But after fees they get 49.8495 ETH
49.8495 ETH < 49.9 ETH, so fulfillSwap reverts
Code does not properly enforce slippage checks during initiateSwap
which can cause the swap to fail during fulfilSwap
.
Manual Review
Modify the initiateSwap
function to perform slippage checks against the post-fee amount:
This change ensures that:
Slippage checks are consistent between initiation and fulfillment
Users can accurately set their minimum output expectations
Swaps that would fail during fulfillment are rejected during initiation
User funds are not unnecessarily locked
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.