Omniscia Tangible Audit

ArrayUtils Code Style Findings

ArrayUtils Code Style Findings

AUS-01C: Gas Inefficient Sorting Algorithm

Description:

While the insertion sort algorithm implemented by ArrayUtils::sort is efficient time wise (due to less iterations), it is not efficient in terms of memory management as it will perform multiple element swaps.

Example:

src/libraries/ArrayUtils.sol
11/**
12 * @notice Sorts an array of uint256 numbers in ascending order.
13 * @dev Uses insertion sort algorithm for sorting.
14 * @param arr The array of uint256 numbers to be sorted.
15 * @return sortedArr The sorted array.
16 */
17function sort(uint256[] memory arr) internal pure returns (uint256[] memory) {
18 for (uint256 i = 1; i < arr.length; ) {
19 uint256 key = arr[i];
20 uint256 j = i - 1;
21
22 // Loop to find the correct position for the element.
23 while (j != type(uint256).max && arr[j] > key) {
24 arr[j + 1] = arr[j];
25 unchecked {
26 --j;
27 }
28 }
29
30 unchecked {
31 arr[j + 1] = key;
32 ++i;
33 }
34 }
35
36 return arr;
37}

Recommendation:

We advise a selection sort algorithm to be implemented instead, performing a single assignment per sorted element and thus greatly reducing the overall gas cost of the function.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The referenced function is no longer utilized in the Tangible Baskets codebase, rendering this exhibit's application inconsequential to the project's gas costs. As such, we consider it nullified.

AUS-02C: Redundant Usage of Checked Arithmetics

Description:

The i - 1 statement within the for loop and the j + 1 statement within the inner while loop of the ArrayUtils::sort function are inefficiently performed using checked arithmetics.

Example:

src/libraries/ArrayUtils.sol
11/**
12 * @notice Sorts an array of uint256 numbers in ascending order.
13 * @dev Uses insertion sort algorithm for sorting.
14 * @param arr The array of uint256 numbers to be sorted.
15 * @return sortedArr The sorted array.
16 */
17function sort(uint256[] memory arr) internal pure returns (uint256[] memory) {
18 for (uint256 i = 1; i < arr.length; ) {
19 uint256 key = arr[i];
20 uint256 j = i - 1;
21
22 // Loop to find the correct position for the element.
23 while (j != type(uint256).max && arr[j] > key) {
24 arr[j + 1] = arr[j];
25 unchecked {
26 --j;
27 }
28 }
29
30 unchecked {
31 arr[j + 1] = key;
32 ++i;
33 }
34 }
35
36 return arr;
37}

Recommendation:

The i variable starts at 1 and contains a value up to arr.length - 1, meaning that it will always be safe to perform i - 1. Similarly, the j variable has a value from 0 up to i - 1 rendering the j + 1 calculation safe to perform under all cases.

We advise the whole for loop body to be wrapped in an unchecked statement as all arithmetic operations preformed within are safe to calculate.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The referenced function is no longer utilized in the Tangible Baskets codebase, rendering this exhibit's application inconsequential to the project's gas costs. As such, we consider it nullified.