Omniscia WallFair Audit

TokenLock Manual Review Findings

TokenLock Manual Review Findings

TLK-01M: Improper Staking Design

TypeSeverityLocation
Logical FaultMediumTokenLock.sol:L73-L75

Description:

The vesting system imposed by TokenLock does not contain sane state expectations as it assigns the irreversible stake amounts prior to the actual transfer of tokens and does not validate that the tokens within the contract will ever be able to satisfy the vests.

Example:

contracts/TokenLock.sol
72// create stakes, duplicates override each other and are not checked
73for (uint256 ii = 0; ii < wallets_.length; ii += 1) {
74 _stakes[wallets_[ii]].totalTokens = amounts_[ii];
75}

Recommendation:

We advise the totalTokens assignments to be set in a dedicated function that also invokes transferFrom on the token from the caller to ensure that the contract is properly funded to make the vest payouts.

Alleviation:

The system was restructured to instead contain multiple states via which the contract progresses to a validly funded state at which users are able to withdraw their stakes. The actual funding process does make sure the contract has sufficient tokens to pay out the vesting schedules thus alleviating this exhibit. We should note that the contract currently does not gracefully handle a surplus of tokens and we advise the fund function to assess whether extra tokens exist in the contract and refund them in some secure way to the original deployer of the contract.

TLK-02M: Inexistent Sanitization of Start Time

TypeSeverityLocation
Input SanitizationMinorTokenLock.sol:L67

Description:

The startTime_ variable should represent a time in the future.

Example:

contracts/TokenLock.sol
40constructor(
41 IERC20 token_,
42 uint256 startTime_,
43 uint256 vestingPeriod_,
44 uint256 cliffPeriod_,
45 uint256 initialReleaseFraction_,
46 address[] memory wallets_,
47 uint128[] memory amounts_
48) {
49 require(wallets_.length == amounts_.length, "number of elements in lists must match");
50 // we put strong requirements for vesting parameters: this is not a generic vesting contract,
51 // we support and test for just a limited range of parameters, see below
52 require(vestingPeriod_ > 0, "vestingPeriod_ must be at least 30 days");
53 // all periods must be divisible by 30 days
54 require(vestingPeriod_ % DAYS_30_PERIOD == 0, "vestingPeriod_ must be divisible by 30 days");
55 require(cliffPeriod_ % DAYS_30_PERIOD == 0, "cliffPeriod_ must be divisible by 30 days");
56 // cliff must be shorted than total vesting period
57 require(cliffPeriod_ < vestingPeriod_, "cliffPeriod_ must be less than vestingPeriod_");
58 // decimal fraction is between 0 and 10**18
59 require(initialReleaseFraction_ <= 10**18, "initialReleaseFraction_ must be in range <0, 10**18>");
60 // cliff cannot be present if initial release is set
61 require(
62 !(initialReleaseFraction_ > 0 && cliffPeriod_ > 0),
63 "cliff period and initial release cannot be set together"
64 );
65
66 _token = token_;
67 _startTime = startTime_;
68 _vestingPeriod = vestingPeriod_;
69 _cliffPeriod = cliffPeriod_;
70 _initialReleaseFraction = initialReleaseFraction_;
71
72 // create stakes, duplicates override each other and are not checked
73 for (uint256 ii = 0; ii < wallets_.length; ii += 1) {
74 _stakes[wallets_[ii]].totalTokens = amounts_[ii];
75 }
76}

Recommendation:

We advise such a check to be introduced to the codebase to prevent it from misbehaving.

Alleviation:

The Wallfair team stated that it is indeed a valid use case to have a start time that is before the actual deployment date of the contract, thus nullifying this exhibit.

TLK-03M: Misleading Error Message

TypeSeverityLocation
Input SanitizationMinorTokenLock.sol:L52

Description:

The error message's description does not match the check performed by it.

Example:

contracts/TokenLock.sol
52require(vestingPeriod_ > 0, "vestingPeriod_ must be at least 30 days");

Recommendation:

We advise the correct conditional or description to be used here as it potentially deviates from the specification.

Alleviation:

The error message was corrected to properly reflect the check it imposes.