Omniscia Moby Audit
Utils Code Style Findings
Utils Code Style Findings
USL-01C: Inefficient Extraction of Option Entry
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L230-L233, L235-L238, L240-L243, L245-L248 |
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:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L111, L180 |
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:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.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:
96// Sort the strikePrices array and adjust isBuys accordingly97for (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 strikePrices102 (strikePrices[j], strikePrices[j + 1]) = (strikePrices[j + 1], strikePrices[j]);103 // Swap isBuys and isCalls104 (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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.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:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L97, L98, L111, L180, L201 |
Description:
The linked for
loops increment / decrement their iterator "safely" due to Solidity's built - in safe arithmetics (post-0.8.X
).
Example:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L250-L253, L255-L258, L260-L263, L265-L268, L270-L273, L275-L278 |
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:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L173, L174 |
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:
173underlyingAssetIndex = uint16((optionTokenId >> 240) & 0xFFFF); // 16 bits174expiry = 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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.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:
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 strikePrices102 (strikePrices[j], strikePrices[j + 1]) = (strikePrices[j + 1], strikePrices[j]);103 // Swap isBuys and isCalls104 (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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L137-L155 |
Description:
The referenced conditions are inefficient as they will evaluate multiple bool
values redundantly instead of enforcing the relations between them.
Example:
137if (isBuys[0] && isCalls[0] && !isBuys[1] && isCalls[1]) {138 // Buy BTC-19JAN24-46000-C139 // Sell BTC-19JAN24-48000-C140 strategy = Strategy.BuyCallSpread;141} else if (!isBuys[0] && isCalls[0] && isBuys[1] && isCalls[1]) {142 // Sell BTC-19JAN24-46000-C143 // Buy BTC-19JAN24-48000-C144 strategy = Strategy.SellCallSpread;145} else if (!isBuys[0] && !isCalls[0] && isBuys[1] && !isCalls[1]) {146 // Sell BTC-19JAN24-46000-P147 // Buy BTC-19JAN24-48000-P148 strategy = Strategy.BuyPutSpread;149} else if (isBuys[0] && !isCalls[0] && !isBuys[1] && !isCalls[1]) {150 // Buy BTC-19JAN24-46000-P151 // Sell BTC-19JAN24-48000-P152 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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L189-L206 |
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:
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 sourceVaultIndex199 ) = 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
Type | Severity | Location |
---|---|---|
Gas Optimization | Utils.sol:L208-L228 |
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:
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
Type | Severity | Location |
---|---|---|
Code Style | Utils.sol:L20 |
Description:
The referenced variable remains unutilized within the codebase.
Example:
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