Summary
In the MultiFlowPump._capRates()
function, the return values from calcReservesAtRatioSwap()
are ignored, leading to incorrect cappedReserves
values being returned.
Vulnerability Details
Consider the function _capRates()
from the MultiFlowPump
contract.
function _capRates(
uint256[] memory lastReserves,
uint256[] memory reserves,
uint256 capExponent,
CapReservesParameters memory crp,
IMultiFlowPumpWellFunction mfpWf,
bytes memory data
) internal view returns (uint256[] memory cappedReserves) {
cappedReserves = reserves;
(uint256 i, uint256 j) = lastReserves[0] > lastReserves[1] ? (0, 1) : (1, 0);
CapRatesVariables memory crv;
crv.rLast = mfpWf.calcRate(lastReserves, i, j, data);
crv.r = mfpWf.calcRate(cappedReserves, i, j, data);
if (crv.r > crv.rLast) {
bytes16 tempExp = ABDKMathQuad.ONE.add(crp.maxRateChanges[i][j]).powu(capExponent);
crv.rLimit = tempExp.cmp(MAX_CONVERT_TO_128x128) != -1
? crv.rLimit = type(uint256).max
: crv.rLast.mulDivOrMax(tempExp.to128x128().toUint256(), CAP_PRECISION2);
if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) > crv.rLimit) {
calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
}
} else if (crv.r < crv.rLast) {
crv.rLimit = crv.rLast.mulDiv(
ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(crp.maxRateChanges[j][i])).powu(capExponent).to128x128()
.toUint256(),
CAP_PRECISION2
);
if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) < crv.rLimit) {
calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
}
}
}
At both occurrences where calcReservesAtRatioSwap()
is called (refer to (2) and (3) in the provided code snippet), the returned values are ignored. Consequently, the cappedReserves
variable remains unchanged from its initialization using reserves
at the beginning of the function (refer to (1)). As a result, the function returns uncapped values instead of the intended capped ones. This discrepancy is highlighted when comparing _capRates()
with the _capLpTokenSupply()
function, where return values are appropriately assigned to the cappedReserves
variable.
Since _capRates()
is utilized by _capReserves()
, which in turn is utilized by readCappedReserves()
and update()
within the MultiFlowPump
contract, the incorrect cappedReserves
values could significantly impact the output of this oracle.
Impact
MultiFlowPump._capRates()
returns incorrect cappedReserves
values when cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) > crv.rLimit
or cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) < crv.rLimit
, uncapped ratio values will be employed in further computations, (readCappedReserves()
and update()
), leading to wrong results.
Tools Used
Manual Review
Recommendations
Do not ignore the returned values and instead set cappedReserves
to the return values of the function calcReservesAtRatioSwap()
. See code below:
@@ -273,7 +273,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu
? crv.rLimit = type(uint256).max
: crv.rLast.mulDivOrMax(tempExp.to128x128().toUint256(), CAP_PRECISION2);
if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) > crv.rLimit) {
- calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
+ cappedReserves = calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
}
// If the ratio decreased, check that it didn't decrease below the max.
} else if (crv.r < crv.rLast) {
@@ -283,7 +283,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu
CAP_PRECISION2
);
if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) < crv.rLimit) {
- calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
+ cappedReserves = calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
}
}
}