Omniscia Moby Audit

Utils Code Style Findings

Utils Code Style Findings

USL-01C: Inefficient Extraction of Option Entry

Description:

The referenced functions are significantly inefficient in extracting a single member from the 256-bit option as they will parse the full option before yielding only one of its members.

Example:

contracts/Utils.sol
230function getUnderlyingAssetIndexByOptionTokenId(uint256 optionTokenId) internal pure returns (uint16) {
231 (uint16 underlyingAssetIndex, , , , , , , ) = parseOptionTokenId(optionTokenId);
232 return underlyingAssetIndex;
233}
234
235function getExpiryByOptionTokenId(uint256 optionTokenId) internal pure returns (uint40) {
236 (, uint40 expiry, , , , , , ) = parseOptionTokenId(optionTokenId);
237 return expiry;
238}
239
240function getStrategyByOptionTokenId(uint256 optionTokenId) internal pure returns (Strategy) {
241 (, , Strategy strategy, , , , , ) = parseOptionTokenId(optionTokenId);
242 return strategy;
243}
244
245function getSourceVaultIndexByOptionTokenId(uint256 optionTokenId) internal pure returns (uint8) {
246 (, , , , , , , uint8 sourceVaultIndex) = parseOptionTokenId(optionTokenId);
247 return sourceVaultIndex;
248}

Recommendation:

We advise the atomic statements in the Utils::parseOptionTokenId function to be relocated to each function, and the Utils::parseOptionTokenId function to invoke the get-prefixed functions for acquiring the respective data entry from the option ID.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The recommended optimization has been applied, reducing the gas cost of each referenced function.

USL-02C: Inefficient Iterator Types

Description:

The EVM is built to operate on 256-bit data slots and is inefficient when dealing with any data type less than that.

The referenced for loops utilize a uint8 iterator unnecessarily.

Example:

contracts/Utils.sol
111for (uint8 i = 0; i < strikePrices.length; i++) {
112 if (strikePrices[i] != 0) _length++;
113}

Recommendation:

We advise their iterator to be set as uint256, optimizing each loop's increment cost.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The referenced iterators have been properly upcast to 256 bits, optimizing their usage cost.

USL-03C: Inefficient Loop Iteration

TypeSeverityLocation
Gas OptimizationUtils.sol:L97

Description:

The upper-most for loop in the Utils::determineStrategy function will redundantly operate until strikePrices.length - 1 when its inner loop will not perform a single iteration at that value as j < 0 is impossible.

Example:

contracts/Utils.sol
96// Sort the strikePrices array and adjust isBuys accordingly
97for (uint i = 0; i < strikePrices.length; i++) {
98 for (uint j = 0; j < strikePrices.length - i - 1; j++) {
99 bool shouldSwap = (strikePrices[j] > strikePrices[j + 1] && strikePrices[j + 1] != 0) || (strikePrices[j] == 0 && strikePrices[j + 1] != 0);
100 if (shouldSwap) {
101 // Swap strikePrices
102 (strikePrices[j], strikePrices[j + 1]) = (strikePrices[j + 1], strikePrices[j]);
103 // Swap isBuys and isCalls
104 (isBuys[j], isBuys[j + 1]) = (isBuys[j + 1], isBuys[j]);
105 (isCalls[j], isCalls[j + 1]) = (isCalls[j + 1], isCalls[j]);
106 }
107 }
108}

Recommendation:

We advise the loop to iterate until strikePrices.length - 2 (i.e. i < strikePrices.length - 1), optimizing the code by one iteration's increment.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The code was updated by adjusting the iteration to loop until strikePrices.length - 1 instead, achieving an identical gas reduction and thus rendering this exhibit addressed.

USL-04C: Inefficient Validation of Strike Prices

TypeSeverityLocation
Gas OptimizationUtils.sol:L112

Description:

The loop that precedes the referenced statement's one will order strike prices in a strictly ascending manner while retaining all zero-value entries at the end of the array.

Example:

contracts/Utils.sol
110uint8 _length = 0;
111for (uint8 i = 0; i < strikePrices.length; i++) {
112 if (strikePrices[i] != 0) _length++;
113}

Recommendation:

We advise an else statement to be introduced to the referenced if check that issues a break statement terminating the loop early efficiently.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The loop will properly terminate early via a break statement as advised, optimizing its gas cost.

USL-05C: Loop Iterator Optimizations

Description:

The linked for loops increment / decrement their iterator "safely" due to Solidity's built - in safe arithmetics (post-0.8.X).

Example:

contracts/Utils.sol
97for (uint i = 0; i < strikePrices.length; i++) {

Recommendation:

We advise the increment / decrement operations to be performed in an unchecked code block as the last statement within each for loop to optimize their execution cost.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The referenced loop iterator increment statements have been relocated at the end of each respective for loop's body and have been unwrapped in an unchecked code block, optimizing their gas cost.

USL-06C: Optimization of Condition Evaluation

Description:

The referenced functions are meant to evaluate in which category the input Strategy enum falls in, and can be optimized in multiple ways.

Example:

contracts/Utils.sol
250function isBuy(Strategy strategy) internal pure returns (bool) {
251 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
252 return strategy == Strategy.BuyCall || strategy == Strategy.BuyPut || strategy == Strategy.BuyCallSpread || strategy == Strategy.BuyPutSpread;
253}
254
255function isSell(Strategy strategy) internal pure returns (bool) {
256 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
257 return strategy == Strategy.SellCall || strategy == Strategy.SellPut || strategy == Strategy.SellCallSpread || strategy == Strategy.SellPutSpread;
258}
259
260function isCall(Strategy strategy) internal pure returns (bool) {
261 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
262 return strategy == Strategy.BuyCall || strategy == Strategy.SellCall || strategy == Strategy.BuyCallSpread || strategy == Strategy.SellCallSpread;
263}
264
265function isPut(Strategy strategy) internal pure returns (bool) {
266 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
267 return strategy == Strategy.BuyPut || strategy == Strategy.SellPut || strategy == Strategy.BuyPutSpread || strategy == Strategy.SellPutSpread;
268}
269
270function isNaked(Strategy strategy) internal pure returns (bool) {
271 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
272 return strategy == Strategy.BuyCall || strategy == Strategy.SellCall || strategy == Strategy.BuyPut || strategy == Strategy.SellPut;
273}
274
275function isSpread(Strategy strategy) internal pure returns (bool) {
276 require(strategy != Strategy.NotSupported, "Utils: Invalid strategy");
277 return strategy == Strategy.BuyCallSpread || strategy == Strategy.SellCallSpread || strategy == Strategy.BuyPutSpread || strategy == Strategy.SellPutSpread;
278}

Recommendation:

We advise the buy / sell distinction to remain as-is in the structure, evaluating whether the enum is odd or even.

In relation to call / put and naked / spread distinction, one of the two checks can be performed by relocating the call / naked enum states at the bottom half of the enum and the put / spread enum states at the upper half, permitting a single comparison to be utilized to deduce the option's type.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The Moby team evaluated this exhibit and opted to retain the current mechanism in place as they consider it to have higher legibility.

USL-07C: Redundant Bitwise Clear Operations

Description:

The referenced statements will perform a bitwise AND (&) to clear the upper bits of the value being downcast, however, this is unnecessary as Solidity will already clear those bits automatically.

Example:

contracts/Utils.sol
173underlyingAssetIndex = uint16((optionTokenId >> 240) & 0xFFFF); // 16 bits
174expiry = uint40((optionTokenId >> 200) & 0xFFFFFFFFFF); // 40 bits

Recommendation:

We advise upper-bit clears to be removed from the referenced instances, optimizing the code's gas cost.

To note, the upper-bit clear operation for the Strategy enum should remain as enum declarations perform an explicit bound check.

Alleviation (b02fae335f62cc1f5f4236fb4d982ad16a32bd26):

The Moby team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase

USL-08C: Redundant Reservation of Memory

TypeSeverityLocation
Gas OptimizationUtils.sol:L99

Description:

The Utils::determineStrategy function will reserve a bool in memory that will be utilized in an if statement that immediately follows it.

Example:

contracts/Utils.sol
98for (uint j = 0; j < strikePrices.length - i - 1; j++) {
99 bool shouldSwap = (strikePrices[j] > strikePrices[j + 1] && strikePrices[j + 1] != 0) || (strikePrices[j] == 0 && strikePrices[j + 1] != 0);
100 if (shouldSwap) {
101 // Swap strikePrices
102 (strikePrices[j], strikePrices[j + 1]) = (strikePrices[j + 1], strikePrices[j]);
103 // Swap isBuys and isCalls
104 (isBuys[j], isBuys[j + 1]) = (isBuys[j + 1], isBuys[j]);
105 (isCalls[j], isCalls[j + 1]) = (isCalls[j + 1], isCalls[j]);
106 }
107}

Recommendation:

We advise the memory reservation to be omitted, and legibility to be introduced via comments as the compiler is not able to optimize these instances.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The shouldSwap local declaration has been removed and the conditional it was assigned to has been placed directly on the if block it was previously used in, optimizing the code's gas cost.

USL-09C: Simplification of Strategy Conditions

Description:

The referenced conditions are inefficient as they will evaluate multiple bool values redundantly instead of enforcing the relations between them.

Example:

contracts/Utils.sol
137if (isBuys[0] && isCalls[0] && !isBuys[1] && isCalls[1]) {
138 // Buy BTC-19JAN24-46000-C
139 // Sell BTC-19JAN24-48000-C
140 strategy = Strategy.BuyCallSpread;
141} else if (!isBuys[0] && isCalls[0] && isBuys[1] && isCalls[1]) {
142 // Sell BTC-19JAN24-46000-C
143 // Buy BTC-19JAN24-48000-C
144 strategy = Strategy.SellCallSpread;
145} else if (!isBuys[0] && !isCalls[0] && isBuys[1] && !isCalls[1]) {
146 // Sell BTC-19JAN24-46000-P
147 // Buy BTC-19JAN24-48000-P
148 strategy = Strategy.BuyPutSpread;
149} else if (isBuys[0] && !isCalls[0] && !isBuys[1] && !isCalls[1]) {
150 // Buy BTC-19JAN24-46000-P
151 // Sell BTC-19JAN24-48000-P
152 strategy = Strategy.SellPutSpread;
153} else {
154 strategy = Strategy.NotSupported;
155}

Recommendation:

The code should first evaluate whether the isCalls entries of both strike prices are equal, and the isBuys entries of both strike prices are unequal.

If the aforementioned is false, the code can assign Strategy.NotSupported immediately. Afterwards, the code can create an upper-level if-else block based on the isCalls value of the first entry indicating whether it is a call or put strategy.

Finally, an inner if-else block can be introduced to both branches that evaluates isBuys[0] and thus can deduce that it is a buy or sell spread.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The conditions have been simplified as advised, optimizing their median evaluation gas cost.

USL-10C: Suboptimal Bit Flipping Mechanism

Description:

The Utils::getOppositeOptionTokenId function will at most flip 4 bits, however, it will do so significantly inefficiently as it will parse the full 256-bit payload, adjust array entries, and then reconstruct it.

Example:

contracts/Utils.sol
189function getOppositeOptionTokenId(uint256 optionTokenId) internal pure returns (uint256 oppositeOptionTokenId) {
190 (
191 uint16 underlyingAssetIndex,
192 uint40 expiry,
193 ,
194 uint8 length,
195 bool[4] memory isBuys,
196 uint48[4] memory strikePrices,
197 bool[4] memory isCalls,
198 uint8 sourceVaultIndex
199 ) = parseOptionTokenId(optionTokenId);
200
201 for(uint i = 0; i < length; i++) {
202 isBuys[i] = !isBuys[i];
203 }
204
205 return formatOptionTokenId(underlyingAssetIndex, expiry, length, isBuys, strikePrices, isCalls, sourceVaultIndex);
206}

Recommendation:

We advise bitwise operations (i.e. XOR) to be utilized instead to achieve the same result at a significantly lower gas cost.

Alleviation (b02fae335f62cc1f5f4236fb4d982ad16a32bd26):

The Moby team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase

USL-11C: Suboptimal Opposite Strategy Evaluation

Description:

The Utils::getOppositeStrategy function is inefficient as it implements a long if-else-if chain to deduce the opposite of a particular strategy.

Example:

contracts/Utils.sol
208function getOppositeStrategy(Strategy strategy) internal pure returns (Strategy) {
209 if (strategy == Strategy.BuyCall) {
210 return Strategy.SellCall;
211 } else if (strategy == Strategy.SellCall) {
212 return Strategy.BuyCall;
213 } else if (strategy == Strategy.BuyPut) {
214 return Strategy.SellPut;
215 } else if (strategy == Strategy.SellPut) {
216 return Strategy.BuyPut;
217 } else if (strategy == Strategy.BuyCallSpread) {
218 return Strategy.SellCallSpread;
219 } else if (strategy == Strategy.SellCallSpread) {
220 return Strategy.BuyCallSpread;
221 } else if (strategy == Strategy.BuyPutSpread) {
222 return Strategy.SellPutSpread;
223 } else if (strategy == Strategy.SellPutSpread) {
224 return Strategy.BuyPutSpread;
225 } else {
226 revert("Utils: Invalid strategy");
227 }
228}

Recommendation:

As the Strategy enum is properly structured, we advise the code to cast the input strategy to a uint8, and add or subtract 1 while re-casting it to a Strategy depending on whether the strategy is odd or even respectively.

Alleviation (a8720219a6a97e10b8d9c6a70c6345747f0fdcb3):

The Moby team evaluated this exhibit and opted to retain the current mechanism in place as they consider it to have higher legibility.

USL-12C: Unutilized Contract Level Constant

TypeSeverityLocation
Code StyleUtils.sol:L20

Description:

The referenced variable remains unutilized within the codebase.

Example:

contracts/Utils.sol
20int40 constant OFFSET19700101 = 2440588;

Recommendation:

We advise it to be omitted, optimizing the code's legibility.

Alleviation (b02fae335f62cc1f5f4236fb4d982ad16a32bd26):

The Moby team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase