Platform: Code4rena
Start Date: 03/07/2023
Pot Size: $40,000 USDC
Total HM: 14
Participants: 74
Period: 7 days
Judge: alcueca
Total Solo HM: 9
Id: 259
League: ETH
Rank: 20/74
Findings: 1
Award: $135.79
🌟 Selected for report: 0
🚀 Solo Findings: 0
135.7856 USDC - $135.79
https://github.com/code-423n4/2023-07-basin/blob/main/src/Well.sol#L352-L377
According to comments in Well
contract, _updatePumps
function "Fetches the current token reserves of the Well and updates the Pumps. Typically called before an operation that modifies the Well's reserves."
In functions like swap, add/remove liquidity _updatePumps
is called, but in shift
function pumps are not updated even though the function works like a swap function, where reserves are changed.
As a result - pumps are not updated when they are expected to.
Function Well._updatePumps
function _updatePumps(uint256 _numberOfTokens) internal returns (uint256[] memory reserves) { reserves = _getReserves(_numberOfTokens); if (numberOfPumps() == 0) { return reserves; } // gas optimization: avoid looping if there is only one pump if (numberOfPumps() == 1) { Call memory _pump = firstPump(); // Don't revert if the update call fails. try IPump(_pump.target).update(reserves, _pump.data) {} catch { // ignore reversion. If an external shutoff mechanism is added to a Pump, it could be called here. } } else { Call[] memory _pumps = pumps(); for (uint256 i; i < _pumps.length; ++i) { // Don't revert if the update call fails. try IPump(_pumps[i].target).update(reserves, _pumps[i].data) {} catch { // ignore reversion. If an external shutoff mechanism is added to a Pump, it could be called here. } } } }
is called in:
This is correct according to the comments in the contract.
However, in shift
(which functions like a swapFrom
and swapTo
function) _updatePumps
function is not called even though the reserves are updated
Manual Review
Add _updatePumps
function to shift
function.
function shift( IERC20 tokenOut, uint256 minAmountOut, address recipient ) external nonReentrant returns (uint256 amountOut) { IERC20[] memory _tokens = tokens(); _updatePumps(_tokens.length); uint256[] memory reserves = new uint256[](_tokens.length); // Use the balances of the pool instead of the stored reserves. // If there is a change in token balances relative to the currently // stored reserves, the extra tokens can be shifted into `tokenOut`. for (uint256 i; i < _tokens.length; ++i) { reserves[i] = _tokens[i].balanceOf(address(this)); } uint256 j = _getJ(_tokens, tokenOut); amountOut = reserves[j] - _calcReserve(wellFunction(), reserves, j, totalSupply()); if (amountOut >= minAmountOut) { tokenOut.safeTransfer(recipient, amountOut); reserves[j] -= amountOut; _setReserves(_tokens, reserves); emit Shift(reserves, tokenOut, amountOut, recipient); } else { revert SlippageOut(amountOut, minAmountOut); } }
Context
#0 - c4-pre-sort
2023-07-11T13:35:54Z
141345 marked the issue as duplicate of #278
#1 - c4-pre-sort
2023-07-11T14:00:04Z
141345 marked the issue as duplicate of #136
#2 - c4-judge
2023-08-03T07:58:08Z
alcueca marked the issue as partial-25
#3 - c4-judge
2023-08-03T07:58:17Z
alcueca changed the severity to 3 (High Risk)
#4 - alcueca
2023-08-03T07:59:15Z
No mention of sync()
, not an accurate estimation of impact.