20,000 USDC
View results
Submission Details
Severity: medium

Users cannot accumulate staking reward

Summary

Staking reward does not accumulate for users if no additional WETH(staking rewards is deposited into Staking.sol).

Vulnerability Details

balance and index variables are updated after first deposit() is called. Any subsequent deposit() calls will not update balance or index, unless more WETH is sent to the contract. claimable mapping is calculated based on these 2 state variables.

Add following code to test folder:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Staking} from "../src/Staking.sol";
import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol";
contract testStaking is Test {
ERC20Mock public weth;
ERC20Mock public stakingToken;
Staking public staking;
uint256 startAt;
uint256 public wethAmount = 10e18;
address public user1 = makeAddr("user1");
address public user2 = makeAddr("user2");
address public user3 = makeAddr("user3");
function setUp() public {
weth = new ERC20Mock();
stakingToken = new ERC20Mock();
staking= new Staking(address(stakingToken), address(weth));
//SetUp
weth.mint(address(staking), wethAmount);
stakingToken.mint(user1, 100e18);
stakingToken.mint(user2, 100e18);
stakingToken.mint(user3, 100e18);
//User1 deposits 100 TKN
vm.startPrank(user1);
stakingToken.approve(address(staking), 100e18);
staking.deposit(100e18);
startAt = block.timestamp;
vm.stopPrank();
}
function testStakingAttack()public{
//User2 deposits 100 TKN
vm.startPrank(user2);
stakingToken.approve(address(staking), 100e18);
staking.deposit(100e18);
vm.stopPrank();
//User3 deposits 100 TKN
vm.startPrank(user3);
stakingToken.approve(address(staking), 100e18);
staking.deposit(100e18);
vm.stopPrank();
//move time forward 10 days
vm.warp(startAt + 10 days);
//User1 tries to claim the accumulated rewards
vm.startPrank(user1);
uint256 rewardsAccumulatedInMapping = staking.claimable(user1);
console.log("user1 rewards accumulated based on internal accounting");
console.logUint(rewardsAccumulatedInMapping);
staking.claim();
console.log("user1 WETH balance after claim");
console.logUint(weth.balanceOf(user1));
vm.stopPrank();
}
}
console.log:
user1 rewards accumulated based on internal accounting
0
user1 WETH balance after claim
0

Impact

Users lose out on staking rewards

Tools Used

foundry/manual review

Recommendations

I am not sure what the best mitigation approach is here. This contract does not provide how staking reward is calculated. There are also other glaring issues with this contract that need to be addressed.

Support

FAQs

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