Vyper Vested Claims

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

Emitting events before checking if transfer

In both functions: rescue_tokens and claim the events are emitted before before verifying the transfer is successful.
If the transfer fails for some reason, this can lead to recording an event for transfer without successful transfer.

Vulnerability Details

When rescue_tokens is called - event is emitted every time, even if transfer fails.

@external
def rescue_tokens(to: address, amount: uint256):
[...]
self.onlyOwner()
log TokensRescued(to, amount) # The event here is emited before checking the transfer success
_success: bool = extcall IERC20(self.token).transfer(to, amount)
assert _success, "Transfer failed"

When claim is called - event is emitted every time, even if transfer fails.

@external
def claim(user: address, total_amount: uint256, proof: DynArray[bytes32, 20]) -> bool:
[...]
log Claimed(user, claimable) # The event here is emited before checking the transfer success
# Transfer the claimable amount to the user - Interactions
_success: bool = extcall IERC20(self.token).transfer(user, claimable)
assert _success, "Transfer failed"
return True

Impact

Checking events can lead to misunderstanding which transfer was truly successful for both functions.

for example:
if we have 5 transfer for 20 tokens each and 1 fails we will still have
5 emitted events for total of 100 tokens
instead of
4 emitted events for total of 80 tokens.

This will cause differences between actual funds and funds that are recorder as rescued/claimed.

Tools Used

Manual review

Recommendations

Moving emitting events after verifying the transfer is succcessful

@external
def rescue_tokens(to: address, amount: uint256):
[...]
self.onlyOwner()
- log TokensRescued(to, amount)
_success: bool = extcall IERC20(self.token).transfer(to, amount)
assert _success, "Transfer failed"
+ log TokensRescued(to, amount)
@external
def claim(user: address, total_amount: uint256, proof: DynArray[bytes32, 20]) -> bool:
[...]
- 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"
+ log Claimed(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.