Summary
MultiFlowPump
would store the wrong pump reserves if update()
is called twice in the same transaction.
Vulnerability Details
updates()
doesn't update the pump reserves if they have been updated already in the same transaction.
function update(uint256[] calldata reserves, bytes calldata data) external {
(bytes16 alpha, uint256 capInterval, CapReservesParameters memory crp) =
abi.decode(data, (bytes16, uint256, CapReservesParameters));
uint256 numberOfReserves = reserves.length;
PumpState memory pumpState;
bytes32 slot = _getSlotForAddress(msg.sender);
(, pumpState.lastTimestamp, pumpState.lastReserves) = slot.readLastReserves();
if (pumpState.lastTimestamp == 0) {
_init(slot, uint40(block.timestamp), reserves);
return;
}
bytes16 alphaN;
bytes16 deltaTimestampBytes;
uint256 capExponent;
{
uint256 deltaTimestamp = _getDeltaTimestamp(pumpState.lastTimestamp);
if (deltaTimestamp == 0) return;
alphaN = alpha.powu(deltaTimestamp);
deltaTimestampBytes = deltaTimestamp.fromUInt();
capExponent = calcCapExponent(deltaTimestamp, capInterval);
}
...
}
So if update()
is called again with the changed Well reserves, it will just skip the changes and the pump reserves will store the old data.
uint256 _reserve;
for (uint256 i; i < numberOfReserves; ++i) {
_reserve = reserves[i];
pumpState.lastReserves[i] =
_capReserve(pumpState.lastReserves[i], (_reserve > 0 ? _reserve : 1).fromUIntToLog2(), capExponent);
pumpState.emaReserves[i] =
pumpState.lastReserves[i].mul((ABDKMathQuad.ONE.sub(alphaN))).add(pumpState.emaReserves[i].mul(alphaN));
pumpState.cumulativeReserves[i] =
pumpState.cumulativeReserves[i].add(pumpState.lastReserves[i].mul(deltaTimestampBytes));
}
So, in the next transaction, lastReserves
will be calculated with the wrong saved reserve and the current reserve.
Impact
The pump reserves might be tracked wrongly in some cases.
Tools Used
Manual Review
Recommendations
The update()
function should track the pump reserves with the last Well's reserves every time.