Summary
The Staking::withdraw
function accepts any withdraw amount
as input. If the user tries to withdraw an amount greater than the deposit the function goes and then fails. The user wastes money in a transaction that reverts and doesn't receive their LoveToken.
Vulnerability Details
function withdraw(uint256 amount) public {
@> userStakes[msg.sender] -= amount;
loveToken.transfer(msg.sender, amount);
emit Withdrew(msg.sender, amount);
}
Impact
Although the function works as expected (it fails if the user tries to withdraw an amount greater than the deposit), the user wastes money in a transaction that in the end reverts without receiving anything.
The test below shows this:
function testWithdrawAGreaterAmount() public {
_mintOneTokenForBothSoulmates();
vm.startPrank(soulmate1);
vm.warp(block.timestamp + 14 days);
airdropContract.claim();
uint256 solmate1Amount = loveToken.balanceOf(soulmate1);
console2.log("soulmate1 loveToken amount after airdrop claim", solmate1Amount);
loveToken.approve(address(stakingContract), solmate1Amount);
stakingContract.deposit(solmate1Amount);
console2.log("soulmate1 loveToken after deposit", loveToken.balanceOf(soulmate1));
vm.expectRevert();
stakingContract.withdraw(solmate1Amount + 1);
console2.log("soulmate1 loveToken after withdraw", loveToken.balanceOf(soulmate1));
vm.stopPrank();
}
function _mintOneTokenForBothSoulmates() internal {
vm.prank(soulmate1);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate2);
soulmateContract.mintSoulmateToken();
}
Running 1 test for test/unit/StakingTest.t.sol:StakingTest
[PASS] testWithdrawAGreaterAmount() (gas: 335495)
Logs:
soulmate1 loveToken amount after airdrop claim 14000000000000000000
soulmate1 loveToken after deposit 0
soulmate1 loveToken after withdraw 0
Tools Used
Manual review
Recommendations
Add a requirement statement that checks the withdrawal amount. The withdraw amount must be less / equal then the deposited amount.
function withdraw(uint256 amount) public {
+ require(amount <= userStakes[msg.sender] && amount > 0, "Wrong amount");
userStakes[msg.sender] -= amount;
loveToken.transfer(msg.sender, amount);
emit Withdrew(msg.sender, amount);
}