Vyper Vested Claims

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

[L-2] Failure on time-bound tests might come from deployment time and warp time method

Summary

A known issue has been reported in the VestedAirdrop contract: Time-bound tests fails for an issue in Titanoboa. The issue is being tracked here and here. But it seems that the tests are rightfully failing.

Vulnerability Details

Tests are failing because of the random second difference between EVM block timestamps and datetime timestamps in seconds. Also the method used to warp time might not be adapted to the test environment. The combination of these two issues might explain the failure of time-bound tests. But normally these issues are not expected to occur in production.

Impact

Time-bound tests fails on these tests:

  • TestVestingSystem::test_claim

  • TestVestingSystem::test_claim_all

Wrong claim amount are returned and the assert fails.

Proof of Concept:

  • Printing the timestamp while deploying the contract to check the second difference:

current_time = int(boa.env.evm.patch.timestamp)
print("EVM time:", current_time)
current_time = int(datetime.now().timestamp())
print("Datetime time:", current_time)
"""
WORKING:
EVM time: 1740258736
Datetime time: 1740258736
.EVM time: 1740258736
Datetime time: 1740258736
.EVM time: 1740258736
NOT WORKING:
Datetime time: 1740258791
FEVM time: 1740258792
Datetime time: 1740258791
FEVM time: 1740258792
"""
  • Checking the claimable amount against the expected amount (example with 30 days):

# def test_claimable_amount(self):
# after 30 days
warp(time_now + thirty_days())
claimable = self.airdrop.claimable_amount(self.user1, self.amount)
print("claimable:", claimable)
print(
"expected:",
(self.amount * 31 // 100)
+ (linear_vesting * thirty_days()) // ninety_days(),
)
assert (
claimable
== (self.amount * 31 // 100)
+ (linear_vesting * thirty_days()) // ninety_days()
)
"""
claimable: 54000008873456790123
expected: 54000000000000000000
OR
claimable: 22999991126543209877
expected: 23000000000000000000
"""

Recommendations

  • Deploy the contract with boa env timestamp rather than datetime.

def deploy() -> VyperContract:
current_time = int(boa.env.evm.patch.timestamp)
  • Use boa.env.time_travel instead of custom warping to take into account the block number too.

boa.env.time_travel(THIRTY_DAYS)

I propose a reworked test file with all the original tests but with the recommended mitigation on my fork:
https://github.com/s3bc40/2025-02-vyper-vested-claims/blob/92c54dd15738435b4db2245655fbef0c8a0fa0b6/tests/test_audit.py

Updates

Appeal created

bube Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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