Omniscia 0xPhase Audit

DBV1 Code Style Findings

DBV1 Code Style Findings

DBV-01C: Inefficient Assignments of State

TypeSeverityLocation
Gas OptimizationDBV1.sol:L45, L207

Description:

The referenced assignments of state are performed within a for loop instead of being performed outside of it.

Example:

db/versions/DBV1.sol
199function add(bytes32[] memory keys, bytes32 value) public override onlyOwner {
200 Set storage valueSet = _values[value];
201
202 for (uint256 i = 0; i < keys.length; i++) {
203 bytes32 key = keys[i];
204 Set storage keySet = _keys[key];
205
206 keySet.exists = true;
207 valueSet.exists = true;
208
209 keySet.list.add(value);
210 valueSet.list.add(key);
211 }
212
213 _valueList.add(value);
214}

Recommendation:

We advise them to be relocated outside the for loop and executed solely when each loop will iterate at least once (i.e. the loop's length limit is non-zero), optimizing the codebase.

Alleviation (3dd3d7bf0c2693b2f9c23bacedfa420393f7ea84):

The exists value of the valueSet is now optimally set to true by using the condition we advised and relocating its assignment outside the for loop.

DBV-02C: Inefficient Iteration of Expected Results

TypeSeverityLocation
Gas OptimizationDBV1.sol:L118-L132

Description:

The DBV1::digest function will first identify the total items it is expected to yield and then iterate them and store their value in the result array.

Example:

db/versions/DBV1.sol
110/// @inheritdoc IDB
111function digest(
112 Opcode memory opcode
113) external view override returns (bytes32[] memory result) {
114 uint256 length = _valueList.length();
115 uint256[] memory digested = _digest(opcode);
116 uint256 totalTruthy = 0;
117
118 for (uint256 i = 0; i < length; i++) {
119 if (digested[i] > 0) {
120 totalTruthy++;
121 }
122 }
123
124 uint256 counter = 0;
125 result = new bytes32[](totalTruthy);
126
127 for (uint256 i = 0; i < length; i++) {
128 if (digested[i] > 0) {
129 result[counter] = _valueList.at(i);
130 counter++;
131 }
132 }
133}

Recommendation:

We advise the code to directly store the values to the result array, instantiating it at the maximum possible values desired (i.e. length). Once the for loop concludes, the code can use an assembly block to mutate the result array's size safely to the actual amount of values identified as digested.

To note, this is solely possible because downsizing an in-memory array is safe when performed in an assembly block in Solidity.

Alleviation (3dd3d7bf0c2693b2f9c23bacedfa420393f7ea84):

The optimization has been applied as advised, greatly reducing the gas cost of the DBV1::digest function and requiring only a single array iteration and instantiation.

DBV-03C: Loop Iterator Optimizations

TypeSeverityLocation
Gas OptimizationDBV1.sol:L41, L60, L118, L127, L161, L178, L202, L297, L301, L308, L312, L324, L335, L338, L361, L385, L388

Description:

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

Example:

db/versions/DBV1.sol
41for (uint256 i = 0; i < values.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 (3dd3d7bf0c2693b2f9c23bacedfa420393f7ea84):

All referenced loop iterators have been optimized as advised, removing their for declaration increment statement and instead performing it in an unchecked code block wherever needed (i.e. before a continue statement or at the end of the for loop's body).

DBV-04C: Redundant Instantiations of Arrays

TypeSeverityLocation
Gas OptimizationDBV1.sol:L154, L171

Description:

The referenced instantiations of empty arrays are redundant given that each function these instances are in contains an explicitly named return argument of the same type.

Example:

db/versions/DBV1.sol
149/// @inheritdoc IDB
150function getValues(
151 bytes32 key
152) external view override returns (bytes32[] memory arr) {
153 Set storage keySet = _keys[key];
154 if (!keySet.exists) return new bytes32[](0);

Recommendation:

We advise the named return argument to be yielded directly (i.e. return arr) as uninitialized variables are by default set to their "empty" state.

Alleviation (3dd3d7bf0c2693b2f9c23bacedfa420393f7ea84):

The redundant instantiations of in-memory arrays have been safely replaced by the named return variables as advised, optimizing each function's gas cost.