Closed c4-bot-8 closed 1 month ago
In this report i showed how this problem will cause long term leakage of value:
minBuyAmount
rounded down (the amount taken to protocol from auction finalization as revenue for example)the comments says that we scale the amounts down if it exceeds GNOSIS_MAX_TOKENS
while preserving the ratio (to preserve the slippage percentage set at the trading contract):
now due to different rounding direction this will cause the ratio to increase (slippage will increase) exceeding max slippage set at trade initiation
this will cause leakage of value on the long run on every trade that has any amount exceeding GNOSIS_MAX_TOKENS
(which is not rare since 21 decimal tokens are in scope)
There is no loss. minBuyAmount is used to calculate worstCasePrice, not the actual trade price. The actual trade price is determined by the auction results.
worstCasePrice = shiftl_toFix(req.minBuyAmount, 9).divu(req.sellAmount, FLOOR);
File: TradeLib.sol
118: function prepareTradeToCoverDeficit(
119: TradeInfo memory trade,
120: uint192 minTradeVolume,
121: uint192 maxTradeSlippage
122: ) internal view returns (bool notDust, TradeRequest memory req) {
123: assert(
124: trade.prices.sellLow != 0 &&
125: trade.prices.sellLow != FIX_MAX &&
126: trade.prices.buyHigh != 0 &&
127: trade.prices.buyHigh != FIX_MAX
128: );
129:
130: // Don't buy dust.
131: trade.buyAmount = fixMax(
132: trade.buyAmount,
133: minTradeSize(minTradeVolume, trade.prices.buyHigh)
134: );
135:
136: // {sellTok} = {buyTok} * {UoA/buyTok} / {UoA/sellTok}
137: uint192 exactSellAmount = trade.buyAmount.mulDiv(
138: trade.prices.buyHigh,
139: trade.prices.sellLow,
140: CEIL
141: );
142: // exactSellAmount: Amount to sell to buy `deficitAmount` if there's no slippage
143:
144: // slippedSellAmount: Amount needed to sell to buy `deficitAmount`, counting slippage
145: uint192 slippedSellAmount = exactSellAmount.div(FIX_ONE.minus(maxTradeSlippage), CEIL);
146:
147: trade.sellAmount = fixMin(slippedSellAmount, trade.sellAmount); // {sellTok} <<<<<<<<@ HERE
148: return prepareTradeSell(trade, minTradeVolume, maxTradeSlippage); <<<<<<<<@ HERE
149: }
150:
line 147, 148, we determine minBuyAmount by calling prepareTradeSell
with slipped sell amount (the amount that we are okey to lose at max)
now when we do the bad rounding direction, we exceed that slippage
worstCasePrice = shiftl_toFix(req.minBuyAmount, 9).divu(req.sellAmount, FLOOR);
in wors case price we div by sellAmount (that we previously rounded up) and we use minBuyAmount
that we previously rounded down
making worstCasePrice
further lower
worstCasePrice is only used in determining whether there should report violation or not, we can say that a lower worstCasePrice will be more lenient on violation checking, but only a 1 wei dust effect.
if (clearingPrice.lt(worstCasePrice)) broker.reportViolation();
Lines of code
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/Broker.sol#L251-L256
Vulnerability details
Impact
Leakage of Value in the long term, applying more slippage than intended
GNOSIS_MAX_TOKENS
Proof of Concept
The problem arises in
Broker::newBatchAuction
in Line 253 we check if any of the trading amounts are bigger than
GNOSIS_MAX_TOKENS
to lower the tokens amounts while preserving the Ratio of both (which is not true)(CEIL)
thesellAmount
the tokens that the protocol will provide to the contract, while in the same time in Line 255 we round down(FLOOR)
minBuyAmount
Tokens which will be sent to the protocol as a minimum after the trade settledmaxTradeSlippage
Potentially leaking value in the long run every time
GNOSIS_MAX_TOKENS
is exceededAssessed type
Math