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: 9/80
Findings: 3
Award: $1,143.55
π Selected for report: 1
π Solo Findings: 0
π Selected for report: josephdara
Also found by: 3docSec
1115.318 USDC - $1,115.32
The functions above can be used to swap tokens, however the swaps are not sent to the provided address. Instead they are sent to the msg.sender. This could cause issues if the user has been blacklisted on a token. Or if the user has a compromised signature/allowance of the target token and they attempt to swap to the token, the user looses all value even though they provided an destination adress
//@audit-H does not send tokens to the required address function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts) { require(path.length == 2, "Direct swap only"); ERC20 ogInAsset = ERC20(path[0]); ogInAsset.safeTransferFrom(msg.sender, address(this), amountIn); ogInAsset.safeApprove(address(ROUTER), amountIn); amounts = new uint[](2); amounts[0] = amountIn; //@audit-issue it should be the to address not msg.sender amounts[1] = ROUTER.exactInputSingle(ISwapRouter.ExactInputSingleParams(path[0], path[1], feeTier, msg.sender, deadline, amountIn, amountOutMin, 0)); ogInAsset.safeApprove(address(ROUTER), 0); emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); }
Here is one of the many functions with this issue, As we can see after the swap is completed, tokens are sent back to the msg.sender from the router not to the to
address
Manual Review. Uniswap Router: https://github.com/Uniswap/v3-periphery/blob/main/contracts/SwapRouter.sol
The uniswap router supports inputting of a destination address. Hence the router should be called with the to address not the msg.sender.
Else remove the address to
from the parameter list
Uniswap
#0 - c4-pre-sort
2023-08-10T05:29:14Z
141345 marked the issue as primary issue
#1 - c4-sponsor
2023-08-15T01:18:31Z
Keref marked the issue as sponsor acknowledged
#2 - Keref
2023-08-15T01:19:32Z
Cannot remove to
from param list as the stated goal is to be compatible with uniswap v2 interface
#3 - c4-sponsor
2023-08-15T01:19:33Z
Keref marked the issue as sponsor confirmed
#4 - c4-sponsor
2023-08-17T08:44:47Z
Keref marked the issue as disagree with severity
#5 - Keref
2023-08-17T08:48:31Z
Although it looks like a severe issue, in our case this was actually on purpose as this is a compatibility module used for OPM only, where it's always msg.sender == to
, and to avoid potential spam under GE banner (ppl swapping spam tokens to famous addresses).
So it's not a high risk issue, maybe low only
#6 - c4-judge
2023-08-19T15:40:54Z
gzeon-c4 marked the issue as satisfactory
#7 - c4-judge
2023-08-20T17:27:39Z
gzeon-c4 marked the issue as selected for report
#8 - c4-judge
2023-08-20T17:29:21Z
gzeon-c4 changed the severity to 2 (Med Risk)
#9 - gzeon-c4
2023-08-20T17:29:41Z
recommend to restrict caller to OPM
#10 - Keref
2023-08-21T03:24:41Z
We could have several (out of this audit scope) contracts that need to swap, restricting to msg.sender is enough
#11 - gzeoneth
2023-08-21T03:26:41Z
right, so may be checking msg.sender == to ?
#12 - Keref
2023-08-21T03:39:10Z
Accepted, see PR#10
π Selected for report: dd0x7e8
Also found by: Bughunter101, Fulum, Kaysoft, MatricksDeCoder, SanketKogekar, Sathish9098, T1MOH, Udsen, debo, fatherOfBlocks, grearlake, hpsb, j4ld1na, josephdara, parsely, pep7siup, piyushshukla, ravikiranweb3, shirochan
12.8772 USDC - $12.88
Ether swaps conducted with the V3proxy would be lost to another user forever. This is because of 2 major issues
When swapping for ETH with the function below:
function swapExactTokensForETH(uint amountIn, uint amountOutMin, 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), amountIn); ogInAsset.safeApprove(address(ROUTER), amountIn); amounts = new uint[](2); amounts[0] = amountIn; amounts[1] = ROUTER.exactInputSingle(ISwapRouter.ExactInputSingleParams(path[0], path[1], feeTier, address(this), deadline, amountIn, amountOutMin, 0)); ogInAsset.safeApprove(address(ROUTER), 0); IWETH9 weth = IWETH9(ROUTER.WETH9()); acceptPayable = true; weth.withdraw(amounts[1]); acceptPayable = false; payable(msg.sender).call{value: amounts[1]}(""); emit Swap(msg.sender, path[0], path[1], amounts[0], amounts[1]); }
WETH is returned from the uniswap and sent to the contract. The ether value is sent to the contract from WETH, and the the value is sent to the user via the call() method. However the calls can be made from a contract as the msg.sender in this case. This causes loss of funds in two scenarios
address to
, it still sends to the direct msg.sender.address to
, it still sends to the direct msg.sender.Manual Review
Check the bool returned after the call() for success, to prevent loss of funds Also send the ETH to the required destination
call/delegatecall
#0 - c4-pre-sort
2023-08-09T08:19:46Z
141345 marked the issue as duplicate of #481
#1 - c4-pre-sort
2023-08-09T09:26:09Z
141345 marked the issue as duplicate of #83
#2 - c4-judge
2023-08-20T17:11:10Z
gzeon-c4 changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-08-20T17:11:32Z
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
function safeIncreaseAllowance is used to increase allowances of tokens for the protocol. However, some tokens do not allowances for address if the current allowance is not set to zero first. Hence increasing the allowance would be impossible. It is better to use the safe Approve method which approves zero first. Although it will take a while, the initial allowance can be exhausted, hence it would be impossible to continue using the affected tokens in the protocol.
Here is the safeIncreaseAllowance being used by the protocol. It makes a call directly to the approve function
function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); }
The implementation
/// @notice Check and set allowance /// @param token Token address /// @param spender Spender address /// @param amount Minimum allowance needed function checkSetAllowance(address token, address spender, uint amount) internal { if ( ERC20(token).allowance(address(this), spender) < amount ) ERC20(token).safeIncreaseAllowance(spender, type(uint256).max); }
Manual Review
Use SsfeApprove to first approve zero then approve the required amount
ERC20
#0 - c4-pre-sort
2023-08-10T06:54:01Z
141345 marked the issue as primary issue
#1 - c4-pre-sort
2023-08-10T06:55:29Z
141345 marked the issue as low quality report
#2 - 141345
2023-08-10T06:55:33Z
bot race known issue NC-34
#3 - c4-judge
2023-08-19T15:32:34Z
gzeon-c4 marked the issue as unsatisfactory: Out of scope
#4 - c4-judge
2023-08-20T15:07:01Z
gzeon-c4 marked the issue as duplicate of #137
#5 - gzeon-c4
2023-08-20T15:08:24Z
Not NC-34 Not exact duplicate of 137 but similar, both are low risk anyway since not exploitable
#6 - c4-judge
2023-08-20T15:09:19Z
gzeon-c4 changed the severity to QA (Quality Assurance)
#7 - c4-judge
2023-08-20T17:43:20Z
gzeon-c4 marked the issue as grade-b