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

WETH sent to the staking contract while there is no tokens staked will be lost forever

Summary

WETH fees sent to the staking contract, if there are no tokens staked, will be lost forever.

Vulnerability Details

Consider the following POC:

import "forge-std/Test.sol";
import "../src/Staking.sol";

import {ERC20, WETH} from "solady/src/tokens/WETH.sol";

contract TERC20 is ERC20 {
    function name() public pure override returns (string memory) {
        return "Test ERC20";
    }
    function symbol() public pure override returns (string memory) {
        return "TERC20";
    }
    function mint(address _to, uint256 _amount) public {
        _mint(_to, _amount);
    }
}

contract StakingTest is Test {

    Staking public staking;

    WETH public wethToken;
    TERC20 public stakingToken;

    address public staker1 = address(0x1);
    address public owner = address(0x3);

    function setUp() public {
        stakingToken = new TERC20();
        wethToken = new WETH();
        vm.deal(owner, 1000 ether);
        vm.startPrank(owner);
        staking = new Staking(address(stakingToken), address(wethToken));
        stakingToken.mint(owner, 100 ether);
        wethToken.deposit{value: 50 ether}();
    }

    function test_rewardsNoTokenStaked() public {
        vm.startPrank(owner);
        stakingToken.transfer(staker1, 20 ether);
        //deposit 10 weth with no token staked.
        wethToken.transfer(address(staking), 10 ether);

        // users stake the tokens
        vm.startPrank(staker1);
        stakingToken.approve(address(staking), 20 ether);
        staking.deposit(20 ether);

        // claim the weth rewards
        vm.startPrank(staker1);
        staking.claim();

        assertEq(wethToken.balanceOf(address(staking)) / 1 ether, 10);
        assertEq(wethToken.balanceOf(address(staker1)) / 1 ether, 0);

        // the first 10 weth are forever stuck on the contract
        console.log("staking contract weth balance", wethToken.balanceOf(address(staking)) / 1 ether);
        console.log("staker 1 eth balance", wethToken.balanceOf(staker1)/ 1 ether);
    }

Impact

Permanent loss of WETH in this specific circumstance.

Tools Used

Foundry

Recommendations

Make sure there is always a small amount of token staked on the staking contract.

Support

FAQs

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