Summary
In case transfer of token fails, claimed_amount hashmap is not updated, in which case user won't be able to get correct amount onwards.
Vulnerability Details
Please check the comments with #### ------>
@external
def claim(user: address, total_amount: uint256, proof: DynArray[bytes32, 20]) -> bool:
"""
@notice This function is used to claim the tokens
@dev Anyone can claim for any user
@param user address, the address of the user
@param total_amount uint256, the total amount of tokens
@param proof DynArray[bytes32, 20], the merkle proof
@return bool True if the claim is successful
"""
assert self.verify_proof(user, total_amount, proof), "Invalid proof"
assert block.timestamp >= self.vesting_start_time, "Claiming is not available yet"
claimable: uint256 = 0
current_amount: uint256 = self.claimed_amount[user]
vested: uint256 = self._calculate_vested_amount(total_amount)
if vested > current_amount:
claimable = vested - current_amount
assert claimable > 0, "Nothing to claim"
self.claimed_amount[user] += claimable
assert current_amount + claimable <= total_amount, "Claimed amount exceeds total amount"
log Claimed(user, claimable)
_success: bool = extcall IERC20(self.token).transfer(user, claimable)
assert _success, "Transfer failed"
return True
Impact
After failed transaction, user's will have incorrect claimed_amount, in their next transaction they will get less amount, resulting in loss of token/claimable.
Recommendations
Update the claimed_amount hashmap after the transfer
Please check the comments with #### ----->
@external
def claim(user: address, total_amount: uint256, proof: DynArray[bytes32, 20]) -> bool:
"""
@notice This function is used to claim the tokens
@dev Anyone can claim for any user
@param user address, the address of the user
@param total_amount uint256, the total amount of tokens
@param proof DynArray[bytes32, 20], the merkle proof
@return bool True if the claim is successful
"""
assert self.verify_proof(user, total_amount, proof), "Invalid proof"
assert block.timestamp >= self.vesting_start_time, "Claiming is not available yet"
claimable: uint256 = 0
current_amount: uint256 = self.claimed_amount[user]
vested: uint256 = self._calculate_vested_amount(total_amount)
if vested > current_amount:
claimable = vested - current_amount
assert claimable > 0, "Nothing to claim"
assert current_amount + claimable <= total_amount, "Claimed amount exceeds total amount"
log Claimed(user, claimable)
_success: bool = extcall IERC20(self.token).transfer(user, claimable)
assert _success, "Transfer failed"
self.claimed_amount[user] += claimable
return True