Vyper Vested Claims

First Flight #34
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: high
Invalid

Claimed amount of user's not updated incase of failed transaction

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
"""
# Checks
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)
# Calculate how much the user can claim now
if vested > current_amount:
claimable = vested - current_amount
assert claimable > 0, "Nothing to claim"
# Update the claimed amount - Effects
##### -----> here claimed_amount is updated
self.claimed_amount[user] += claimable
# invariant: claimed amount should always be less than or equal to amount (better safe then sorry)
assert current_amount + claimable <= total_amount, "Claimed amount exceeds total amount"
log Claimed(user, claimable)
# Transfer the claimable amount to the user - Interactions
##### ------> transaction fails here, then claimed_amount is not recovered
_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
"""
# Checks
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)
# Calculate how much the user can claim now
if vested > current_amount:
claimable = vested - current_amount
assert claimable > 0, "Nothing to claim"
# Update the claimed amount - Effects
##### -----> remove from here
##### -----> self.claimed_amount[user] += claimable
# invariant: claimed amount should always be less than or equal to amount (better safe then sorry)
assert current_amount + claimable <= total_amount, "Claimed amount exceeds total amount"
log Claimed(user, claimable)
# Transfer the claimable amount to the user - Interactions
_success: bool = extcall IERC20(self.token).transfer(user, claimable)
assert _success, "Transfer failed"
##### ------> add here
self.claimed_amount[user] += claimable
return True
Updates

Appeal created

bube Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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