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

Users cannot claim their staking rewards because of the limited amount of tokens in the 'StakingVault' contract.

Summary

In case of big staking amounts of tokens for a long period users will not be able to claim their rewards due to limited 'loveToken.allowance[address(stakingVault)][address(stakingContract)]' that in this case equals the 'loveToken.balanceOf(address(stakingVault))'

Vulnerability Details

In case of big staking rewards users may not be able to claim them because of limited amount 'loveToken.allowance[address(stakingVault)][address(stakingContract)]' causing an 'underflow error' in a 'transferFrom' function.

Impact

Users will not be able to claim their rewards and the contract to work as promised.

POC (The higher the number of 'soulmates' the lower the time needed to drain 'loveToken.allowance[address(stakingVault)][address(stakingContract)]' ).

contract BaseTest is Test {
event Soulmatearereunited(
address indexed soulmate1,
address indexed soulmate2
);
event balance (address indexed, uint256);
Soulmate public soulmateContract;
LoveToken public loveToken;
Staking public stakingContract;
Airdrop public airdropContract;
Vault public airdropVault;
Vault public stakingVault;
address deployer = makeAddr("deployer");
address soulmate1 = makeAddr("soulmate1");
address soulmate2 = makeAddr("soulmate2");
address soulmate3;
address soulmate4;
address soulmate5;
address soulmate6;
address soulmate7;
address soulmate8;
address soulmate9;
address soulmate10;
uint256 start;
function setUp() public {
vm.startPrank(deployer);
airdropVault = new Vault();
stakingVault = new Vault();
soulmateContract = new Soulmate();
loveToken = new LoveToken(
ISoulmate(address(soulmateContract)),
address(airdropVault),
address(stakingVault)
);
stakingContract = new Staking(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(stakingVault))
);
airdropContract = new Airdrop(
ILoveToken(address(loveToken)),
ISoulmate(address(soulmateContract)),
IVault(address(airdropVault))
);
airdropVault.initVault(
ILoveToken(address(loveToken)),
address(airdropContract)
);
stakingVault.initVault(
ILoveToken(address(loveToken)),
address(stakingContract)
);
soulmate1 = address(1);
soulmate2 = address(2);
soulmate3 = address(3);
soulmate4 = address(4);
soulmate5 = address(5);
soulmate6 = address(6);
soulmate7 = address(7);
soulmate8 = address(8);
soulmate9 = address(9);
soulmate10 = address(10);
start = block.timestamp;
// init
vm.stopPrank();
}
function test_staking() public {
// Mint Soulmatetoken
vm.prank(soulmate1);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate2);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate3);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate4);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate5);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate6);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate7);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate8);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate9);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate10);
soulmateContract.mintSoulmateToken();
vm.warp(start+100 weeks);
//Claim Airdrop
vm.prank(soulmate1);
airdropContract.claim();
vm.prank(soulmate2);
airdropContract.claim();
vm.prank(soulmate3);
airdropContract.claim();
vm.prank(soulmate4);
airdropContract.claim();
vm.prank(soulmate5);
airdropContract.claim();
vm.prank(soulmate6);
airdropContract.claim();
vm.prank(soulmate7);
airdropContract.claim();
vm.prank(soulmate8);
airdropContract.claim();
vm.prank(soulmate9);
airdropContract.claim();
vm.prank(soulmate10);
airdropContract.claim();
// Token concentration
vm.startPrank(soulmate1);
uint256 balancesoul1= loveToken.balanceOf(soulmate1);
uint256 balancesoul2= loveToken.balanceOf(soulmate2);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate1));
assertTrue(loveToken.balanceOf(soulmate2)== (balancesoul1+balancesoul2));
vm.stopPrank();
vm.startPrank(soulmate3);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate3));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate4);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate4));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate5);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate5));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate6);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate6));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate7);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate7));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate8);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate8));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate9);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate9));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.startPrank(soulmate10);
loveToken.transfer(soulmate2, loveToken.balanceOf(soulmate10));
emit balance(soulmate2 ,loveToken.balanceOf(soulmate2));
vm.stopPrank();
// Deposit Staking round 1
vm.startPrank(soulmate2);
loveToken.approve(address(stakingContract), loveToken.balanceOf(soulmate2));
stakingContract.deposit(loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.warp(start+200 weeks);
// Claim reward and withdraw round 1
vm.startPrank(soulmate2);
stakingContract.claimRewards();
stakingContract.withdraw(stakingContract.userStakes(soulmate2));
emit balance (soulmate2, loveToken.balanceOf(soulmate2));
vm.stopPrank();
// Deposit Staking round 2
vm.startPrank(soulmate2);
loveToken.approve(address(stakingContract), loveToken.balanceOf(soulmate2));
stakingContract.deposit(loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.warp(start+300 weeks);
// Claim reward round 2
vm.startPrank(soulmate2);
stakingContract.claimRewards();
stakingContract.withdraw(stakingContract.userStakes(soulmate2));
emit balance (soulmate2, loveToken.balanceOf(soulmate2));
vm.stopPrank();
// Deposit round3
vm.startPrank(soulmate2);
loveToken.approve(address(stakingContract), loveToken.balanceOf(soulmate2));
stakingContract.deposit(loveToken.balanceOf(soulmate2));
vm.stopPrank();
vm.warp(start+400 weeks);
// Claim reward round 3
vm.startPrank(soulmate2);
emit balance (address(stakingVault), loveToken.balanceOf(address(stakingVault)));
stakingContract.claimRewards();
stakingContract.withdraw(stakingContract.userStakes(soulmate2));
emit balance (soulmate2, loveToken.balanceOf(soulmate2));
vm.stopPrank();
}
}
├─ [0] VM::warp(241920001 [2.419e8])
│ └─ ← ()
├─ [0] VM::startPrank(0x0000000000000000000000000000000000000002)
│ └─ ← ()
├─ [586] LoveToken::balanceOf(Vault: [0x1240FA2A84dd9157a0e76B5Cfe98B1d52268B264]) [staticcall]
│ └─ ← 357900000000000000000000000 [3.579e26]
├─ emit balance(: Vault: [0x1240FA2A84dd9157a0e76B5Cfe98B1d52268B264], : 357900000000000000000000000 [3.579e26])
├─ [3395] 0x9101223D33eEaeA94045BB2920F00BA0F7A475Bc::claimRewards()
│ ├─ [585] Soulmate::ownerToId(0x0000000000000000000000000000000000000002)
│ │ └─ ← 0
│ ├─ [824] LoveToken::transferFrom(Vault: [0x1240FA2A84dd9157a0e76B5Cfe98B1d52268B264], 0x0000000000000000000000000000000000000002, 14210700000000000000000000000 [1.421e28])
│ │ └─ ← "Arithmetic over/underflow"
│ └─ ← "Arithmetic over/underflow"
└─ ← "Arithmetic over/underflow"

Tools Used

Foundry

Recommendations

Amount and staking time of the deposits should be limited according to the 'loveToken.allowance[address(stakingVault)][address(stakingContract)] and loveToken.balanceOf(address(stakingVault))' in order to avoid the insolvency risk.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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