Platform: Code4rena
Start Date: 18/05/2023
Pot Size: $24,500 USDC
Total HM: 3
Participants: 72
Period: 4 days
Judge: LSDan
Id: 237
League: ETH
Rank: 16/72
Findings: 1
Award: $321.72
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: minhquanym
Also found by: 0xStalin, BLACK-PANDA-REACH, Madalad, T1MOH, Udsen, adriro, max10afternoon, rbserver, sces60107
321.7244 USDC - $321.72
didPay()
function is payable and receives ETH. Then during uniswapV3Callback deposits ETH into WETH and sends it into UniswapV3Pool for projectToken
. But excess ETH is not returned to caller of JBXBuybackDelegate and can be used by other users.
didPay()
is payable:
function didPay(JBDidPayData calldata _data) external payable override {
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override { // Check if this is really a callback if (msg.sender != address(pool)) revert JuiceBuyback_Unauthorized(); // Unpack the data (uint256 _minimumAmountReceived) = abi.decode(data, (uint256)); // Assign 0 and 1 accordingly uint256 _amountReceived = uint256(-(_projectTokenIsZero ? amount0Delta : amount1Delta)); uint256 _amountToSend = uint256(_projectTokenIsZero ? amount1Delta : amount0Delta); // Revert if slippage is too high if (_amountReceived < _minimumAmountReceived) revert JuiceBuyback_MaximumSlippage(); // Wrap and transfer the weth to the pool weth.deposit{value: _amountToSend}(); weth.transfer(address(pool), _amountToSend); }
Manual Review
Add argument excess receiver
to data
argument passed to pool.swap()
. And send excess:
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override { // Unpack the data (uint256 _minimumAmountReceived, address excessReceiver) = abi.decode(data, (uint256, address)); ... // Wrap and transfer the weth to the pool weth.deposit{value: _amountToSend}(); weth.transfer(address(pool), _amountToSend); // SOLUTION payable(excessReceiver).call{value: address(this).balance}(""); }
It may require to refactor logic of terminal contract interacting with JBXBuybackDelegate
ETH-Transfer
#0 - c4-pre-sort
2023-05-24T16:11:11Z
dmvt marked the issue as primary issue
#1 - drgorillamd
2023-05-30T15:01:02Z
this might be valid but really not sure -> the quote comes from frontend by actually staticcalling the quoter (ie as Uni v3 SDK is doing), then the price used is max or min tick (accordingly) -> iic, this means it's unbounded and all the eth are used anyway (and users are protected by passing the quote which takes it into account, "X eth returns Y token" with y being what's left), ie no "partial fill"
#2 - c4-sponsor
2023-06-01T07:11:31Z
drgorillamd requested judge review
#3 - c4-judge
2023-06-02T14:42:52Z
dmvt marked issue #162 as primary and marked this issue as a duplicate of 162
#4 - dmvt
2023-06-02T14:45:11Z
There are several scenarios where this can occur, one of which is talked about in #162.
#5 - c4-judge
2023-06-02T14:45:23Z
dmvt marked the issue as satisfactory