Omniscia Nori Audit

ScheduleUtils Code Style Findings

ScheduleUtils Code Style Findings

SUS-01C: Inefficient Cliff Total Evaluation

Description:

The total cliff amount evaluation occurs on each cliff addition and contains an expensive iteration of all cliff periods and their amounts.

Example:

contracts/ScheduleUtils.sol
51/**
52 * @dev Adds a cliff defined by *time* and *amount* to *schedule*
53 *
54 * *time* must be >= any existing cliff, >= schedule.startTime and <= schedule.endTime
55 * *amount* must be <= (schedule.totalAmount - total of existing cliffs)
56 */
57function addCliff(
58 Schedule storage schedule,
59 uint256 time,
60 uint256 amount
61) internal {
62 if (schedule.cliffCount == 0) {
63 require(time >= schedule.startTime, "Cliff before schedule start");
64 } else {
65 require(
66 time >= schedule.cliffs[schedule.cliffCount - 1].time,
67 "Cliffs not chronological"
68 );
69 }
70 require(time <= schedule.endTime, "Cliffs cannot end after schedule");
71 require(
72 allCliffAmounts(schedule) + amount <= schedule.totalAmount,
73 "cliff amounts exceed total"
74 );
75 schedule.cliffs[schedule.cliffCount].time = time;
76 schedule.cliffs[schedule.cliffCount].amount = amount;
77 schedule.cliffCount += 1;
78}
79
80/**
81 * @dev The sum of all cliff amounts in *schedule*
82 */
83function allCliffAmounts(Schedule storage schedule)
84 internal
85 view
86 returns (uint256)
87{
88 uint256 cliffAmounts;
89 for (uint256 i = 0; i < schedule.cliffCount; i++) {
90 cliffAmounts += schedule.cliffs[i].amount;
91 }
92 return cliffAmounts;
93}

Recommendation:

We advise the total to be tracked whenever a cliff is introduced instead, greatly optimizing the efficiency of the system.

Alleviation:

The total cliff amount is now correctly tracked in every addCliff operation rendering the calculation of it redundant.

SUS-02C: Inefficient Loop Limit Evaluations

Description:

The linked loop limit evaluations read a value from storage on each iteration.

Example:

contracts/ScheduleUtils.sol
98function cliffAmountsAvailable(Schedule storage schedule, uint256 atTime)
99 internal
100 view
101 returns (uint256)
102{
103 uint256 available = 0;
104 for (uint256 i = 0; i < schedule.cliffCount; i++) {

Recommendation:

We advise their limit to instead be cached to a local variable that is consequently utilized greatly optimizing their gas cost.

Alleviation:

The first loop was omitted as part of another exhibit and the second loop was optimized as advised thereby alleviating this exhibit.

SUS-03C: Inefficient Usage of Signed Integers

Description:

The linked statements make use of the int256 type to allow the subtraction to occur even when it goes to negative values in which case the value of 0 is yielded for linear amount available.

Example:

contracts/ScheduleUtils.sol
135int256 rampTimeElapsed = int256(atTime) - int256(rampStartTime);
136return
137 rampTimeElapsed <= 0
138 ? 0
139 : (rampTotalAmount * uint256(rampTimeElapsed)) / rampTotalTime;

Recommendation:

We advise an if check to be utilized instead that checks whether atTime is greater-than (>) the value of rampStartTime in which case the value of zero is returned, reducing the complexity and gas cost of the statements.

Alleviation:

The code was updated to utilize the existing ternary operator instead with a different conditional optimizing the codebase.

SUS-04C: Inefficient mapping Lookups

Description:

The linked statements contain duplicate mapping entry lookups which could instead be cached to a local variable (i.e. for mapping(uint256 => Struct) foo the statements foo[0].a = 0; foo[0].b = 1; can be optimized to Struct storage bar = foo[0]; bar.a = 0; bar.b = 1;).

Example:

contracts/ScheduleUtils.sol
75schedule.cliffs[schedule.cliffCount].time = time;
76schedule.cliffs[schedule.cliffCount].amount = amount;

Recommendation:

We advise optimizations to be applied as outlined in the description.

Alleviation:

The mapping lookups were only optimized for the first instance thereby partially alleviating this exhibit.

SUS-05C: Redundant Duplicate Value Reads

Description:

The linked statements read schedule.cliffCount from storage multiple times redundantly.

Example:

contracts/ScheduleUtils.sol
57function addCliff(
58 Schedule storage schedule,
59 uint256 time,
60 uint256 amount
61) internal {
62 if (schedule.cliffCount == 0) {
63 require(time >= schedule.startTime, "Cliff before schedule start");
64 } else {
65 require(
66 time >= schedule.cliffs[schedule.cliffCount - 1].time,
67 "Cliffs not chronological"
68 );
69 }
70 require(time <= schedule.endTime, "Cliffs cannot end after schedule");
71 require(
72 allCliffAmounts(schedule) + amount <= schedule.totalAmount,
73 "cliff amounts exceed total"
74 );
75 schedule.cliffs[schedule.cliffCount].time = time;
76 schedule.cliffs[schedule.cliffCount].amount = amount;
77 schedule.cliffCount += 1;
78}

Recommendation:

We advise its value to be cached to a local variable optimizing the code's execution cost.

Alleviation:

The code was optimized in all instances except the first if conditional thereby partially alleviating this exhibit.