Platform: Code4rena
Start Date: 01/08/2023
Pot Size: $91,500 USDC
Total HM: 14
Participants: 80
Period: 6 days
Judge: gzeon
Total Solo HM: 6
Id: 269
League: ETH
Rank: 17/80
Findings: 2
Award: $640.79
🌟 Selected for report: 0
🚀 Solo Findings: 0
625.436 USDC - $625.44
swapTokensForExactETH
is capable of receiving tokens and then converting them into the specified amount of ETH to return to the user. However, after the exchange, the remaining tokens are not returned to the user, resulting in a loss of funds for the user. It simply clears the approval but does not return the remaining ogInAsset
tokens after the exchange to the user.
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) payable external returns (uint[] memory amounts) { require(path.length == 2, "Direct swap only"); require(path[1] == ROUTER.WETH9(), "Invalid path"); ERC20 ogInAsset = ERC20(path[0]); ogInAsset.safeTransferFrom(msg.sender, address(this), amountInMax); ogInAsset.safeApprove(address(ROUTER), amountInMax); amounts = new uint[](2); amounts[0] = ROUTER.exactOutputSingle(ISwapRouter.ExactOutputSingleParams(path[0], path[1], feeTier, address(this), deadline, amountOut, amountInMax, 0)); amounts[1] = amountOut; // => we should return ogInAsset to user ogInAsset.safeApprove(address(ROUTER), 0); IWETH9 weth = IWETH9(ROUTER.WETH9()); acceptPayable = true; weth.withdraw(amountOut); acceptPayable = false; payable(msg.sender).call{value: amountOut}(""); emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); }
Consider the following scenario:
swapTokensForExactETH(1, 10000, [address(USDT), address(WETH)], address(0), 0)
V3Proxy
contract and cannot be retrieved.VSCode
After exchanging WETH, return the remaining ogInAsset
tokens to the msg.sender
.
diff --git a/contracts/helper/V3Proxy.sol b/contracts/helper/V3Proxy.sol index 45ab57e..190b5cb 100644 --- a/contracts/helper/V3Proxy.sol +++ b/contracts/helper/V3Proxy.sol @@ -166,6 +166,7 @@ contract V3Proxy is ReentrancyGuard, Ownable { amounts = new uint[](2); amounts[0] = ROUTER.exactOutputSingle(ISwapRouter.ExactOutputSingleParams(path[0], path[1], feeTier, address(this), deadline, amountOut, amountInMax, 0)); amounts[1] = amountOut; + ogInAsset.safeTransfer(msg.sender, ogInAsset.balanceOf(address(this))); ogInAsset.safeApprove(address(ROUTER), 0); IWETH9 weth = IWETH9(ROUTER.WETH9()); acceptPayable = true;
Other
#0 - c4-pre-sort
2023-08-09T08:17:00Z
141345 marked the issue as duplicate of #64
#1 - c4-judge
2023-08-20T17:31:46Z
gzeon-c4 marked the issue as satisfactory
🌟 Selected for report: Team_FliBit
Also found by: 0x70C9, 3docSec, 8olidity, DavidGiladi, Krace, LokiThe5th, Rolezn, Sathish9098, UniversalCrypto, banpaleo5, catellatech, digitizeworx, fatherOfBlocks, hpsb, j4ld1na, josephdara, kutugu, niser93, nonseodion, oakcobalt, osmanozdemir1, pep7siup, ravikiranweb3, said, sivanesh_808
15.3494 USDC - $15.35
GeVault::modifyTick
can modify the elements of ticks
without the need to maintain the order of tick. If the owner wrongly modified the tick and destroy the order of ticks
, the normal function of contract TokenisableRange
will be compromised.
As shown in the code, the owner could modify the elements of ticks
without any restrictions.
/// @notice Modify ticker /// @param tr New tick address /// @param index Tick to modify function modifyTick(address tr, uint index) public onlyOwner { (ERC20 t0,) = TokenisableRange(tr).TOKEN0(); (ERC20 t1,) = TokenisableRange(tr).TOKEN1(); require(t0 == token0 && t1 == token1, "GEV: Invalid TR"); ticks[index] = TokenisableRange(tr); emit ModifyTick(tr, index); }
VSCode
Add some checks to ensure that the modified Tick can meet the sequencing requirements.
diff --git a/contracts/GeVault.sol b/contracts/GeVault.sol index 4c9dae3..f58a60f 100644 --- a/contracts/GeVault.sol +++ b/contracts/GeVault.sol @@ -165,10 +165,28 @@ contract GeVault is ERC20, Ownable, ReentrancyGuard { /// @param tr New tick address /// @param index Tick to modify function modifyTick(address tr, uint index) public onlyOwner { - (ERC20 t0,) = TokenisableRange(tr).TOKEN0(); - (ERC20 t1,) = TokenisableRange(tr).TOKEN1(); + TokenisableRange t = TokenisableRange(tr); + (ERC20 t0,) = t.TOKEN0(); + (ERC20 t1,) = t.TOKEN1(); require(t0 == token0 && t1 == token1, "GEV: Invalid TR"); - ticks[index] = TokenisableRange(tr); + // left + if (index - 1 >= 0){ + if (baseTokenIsToken0) + require( t.lowerTick() > ticks[index - 1].upperTick(), "GEV: Modify Tick Overlap"); + else + require( t.upperTick() < ticks[index - 1].lowerTick(), "GEV: Modify Tick Overlap"); + } + // right + if (index < ticks.length - 1){ + if (baseTokenIsToken0) + require( t.upperTick() < ticks[index + 1].lowerTick(), "GEV: Modify Tick Overlap"); + else + require( t.lowerTick() > ticks[index + 1].upperTick(), "GEV: Modify Tick Overlap"); + } + ticks[index] = t; + //(ERC20 t0,) = TokenisableRange(tr).TOKEN0(); + //(ERC20 t1,) = TokenisableRange(tr).TOKEN1(); + //ticks[index] = TokenisableRange(tr); emit ModifyTick(tr, index); }
DoS
#0 - c4-pre-sort
2023-08-09T09:53:12Z
141345 marked the issue as duplicate of #125
#1 - c4-judge
2023-08-20T16:09:18Z
gzeon-c4 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-08-20T17:40:31Z
gzeon-c4 marked the issue as grade-b