20,000 USDC
View results
Submission Details
Severity: high
Valid

Missing approve() in Fees.sol renders the contract unusable

Summary

The Fees.sol contract handles any deposits to the contract of any token to be converted to WETH which will be used for rewards on the Staking.sol contract. During the sellProfits() function call the contract attempts to swap any balance of the passed token as parameter to WETH through Uniswap Router and forwards the proceeds to the Staking.sol contract.

Vulnerability Details

The problem lies in the lack of approve() function before the actual attempt to swap tokens using the Uniswap Router which would cause any call to the sellProfits() function to fail.

Below you can find a simple PoC which uses a fork of Ethereum Mainnet to test the functionality.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Fees.sol";
import "../src/Staking.sol";
import {WETH} from "solady/src/tokens/WETH.sol";
import {IERC20} from "../src/interfaces/IERC20.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
contract FeesTester is Test {
IERC20 _usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
WETH _weth;
ERC20 _tkn;
Staking _staking;
Fees _fees;
address _user = vm.addr(0x01);
address _nexo = 0xFfec0067F5a79CFf07527f63D83dD5462cCf8BA4;
function setUp() public {
_tkn = new ERC20("TokenTest", "TKN");
_staking = new Staking(address(_tkn),address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
_weth = WETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
_fees = new Fees(address(_weth),address(_staking));
}
function test_uniRouterApproval() external {
vm.startPrank(_nexo);
_usdc.transfer(_user, 3000e6);
vm.stopPrank();
vm.startPrank(_user);
_usdc.transfer(address(_fees), 1000e6);
_fees.sellProfits(address(_usdc));
vm.stopPrank();
}
}

The test can be ran with the following command:

forge test --match-contract FeesTester --fork-url [infura_rpc_link] -vvv`

The test run will fail with error SBF which indicates the lack of approval which can be expected also in the logs on the terminal.

Impact

Any funds send to the Fees.sol contract will be locked there forever.

Tools Used

Manual Review / Foundry

Recommendations

Add the following line of code before the actual swap happens within the sellProfits() function:

IERC20(_profits).approve(address(swapRouter), amount);

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.