DeFiHardhat
12,000 USDC
View results
Submission Details
Severity: medium
Valid

Ignoring the Well Function logic for a ratio of reserves calculation

Summary

The MultiFlowPump._capRates function calculates a ratio of cappedReserves with its own formula instead of using the Well Function formula implementation received as parameter. This breaks the main idea of the composability support and can cause incorrect calculation for other well function implementations.

Vulnerability Details

According to the documentation Basin allows for anyone to compose new and existing (1) Well Functions (i.e. exchange functions), (2) Pumps (i.e., network native oracles) and (3) Well Implementations (i.e., exchange implementations) to create a Well (i.e., a customized liquidity pool). and A Well Function defines an invariant relationship between a Well’s Reserves and the supply of Well LP tokens. Though the MultiFlowPump contract should use Well Functions for mentioned relationships everywhere there are two places where it does not.
When the _capRates function caps the change in ratio of reserves an in place calculation of a ratio of cappedReserves is used in if statements instead of using mfpWf.calcRate.

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;
// Part 1: Cap Rates
// Use the larger reserve as the numerator for the ratio to maximize precision
(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 the ratio increased, check that it didn't increase above the max.
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);
}
// If the ratio decreased, check that it didn't decrease below the max.
} 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);
}
}
}

Though the ConstantProduct2.calcRate function calculates ratio in the same manner this can be incorrect for other Well Functions since Basin supports Well Functions that contain arbitrary logic.

Impact

Unintended behavior, potential incorrect calculation for other Well Function implementations.

Tools used

Manual Review

Recommendations

Consider using Well Function for a ratio of cappedReserves calculation or using crv.r cached variable if it is appropriate:

CapRatesVariables memory crv;
crv.rLast = mfpWf.calcRate(lastReserves, i, j, data);
crv.r = mfpWf.calcRate(cappedReserves, i, j, data);
// If the ratio increased, check that it didn't increase above the max.
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) {
+ if (crv.r > crv.rLimit) {
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) {
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) {
+ if (crv.r < crv.rLimit) {
calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data);
}
}
Updates

Lead Judging Commences

giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Well function ratio

pontifex Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Well function ratio

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.