code-423n4 / 2022-05-opensea-seaport-findings

1 stars 0 forks source link

Gas Optimizations #103

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

Gas optimizations

Cache arrays length on loops

Caching the array length in memory can yield significant gas savings when the array length is high.

On file contracts/lib/OrderCombiner.sol you can cache the offers length and the consideration length;

diff --git a/contracts/lib/OrderCombiner.sol b/contracts/lib/OrderCombiner.sol
index babef49..6a5dd59 100644
--- a/contracts/lib/OrderCombiner.sol
+++ b/contracts/lib/OrderCombiner.sol
@@ -242,9 +242,10 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {

                 // Retrieve array of offer items for the order in question.
                 OfferItem[] memory offer = advancedOrder.parameters.offer;
-
+                // cache offer length
+                uint256 offerLength = offer.length;
                 // Iterate over each offer item on the order.
-                for (uint256 j = 0; j < offer.length; ++j) {
+                for (uint256 j = 0; j < offerLength; ++j) {
                     // Retrieve the offer item.
                     OfferItem memory offerItem = offer[j];

@@ -594,8 +595,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
                     advancedOrder.parameters.consideration
                 );

+                uint256 considerationLength = consideration.length;
                 // Iterate over each consideration item to ensure it is met.
-                for (uint256 j = 0; j < consideration.length; ++j) {
+                for (uint256 j = 0; j < considerationLength; ++j) {
                     // Retrieve remaining amount on the consideration item.
                     uint256 unmetAmount = consideration[j].startAmount;

On file contracts/lib/OrderFulfiller.sol you can cache the offers length and the consideration length;

diff --git a/contracts/lib/OrderFulfiller.sol b/contracts/lib/OrderFulfiller.sol
index 0060ba9..3878254 100644
--- a/contracts/lib/OrderFulfiller.sol
+++ b/contracts/lib/OrderFulfiller.sol
@@ -214,7 +214,8 @@ contract OrderFulfiller is
             }

             // Iterate over each offer on the order.
-            for (uint256 i = 0; i < orderParameters.offer.length; ) {
+            uint256 totalOrderParameters = orderParameters.offer.length;
+            for (uint256 i = 0; i < totalOrderParameters; ) {
                 // Retrieve the offer item.
                 OfferItem memory offerItem = orderParameters.offer[i];

Use pre increment and pre decrement instead of post increment and post decrement

++i costs less gas compared to i++ or i += 1 for unsigned integers. This is because the pre-increment operation is cheaper. Source

diff --git a/contracts/lib/OrderCombiner.sol b/contracts/lib/OrderCombiner.sol
index babef49..915ed2f 100644
--- a/contracts/lib/OrderCombiner.sol
+++ b/contracts/lib/OrderCombiner.sol
@@ -226,7 +226,7 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
                 orderHashes[i] = orderHash;

                 // Decrement the number of fulfilled orders.
-                maximumFulfilled--;
+                --maximumFulfilled;

                 // Place the start time for the order on the stack.
                 uint256 startTime = advancedOrder.parameters.startTime;
@@ -487,7 +487,7 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
                 // If offerer and recipient on the execution are the same...
                 if (execution.item.recipient == execution.offerer) {
                     // increment total filtered executions.
-                    totalFilteredExecutions += 1;
+                    ++totalFilteredExecutions;
                 } else {
                     // Otherwise, assign the execution to the executions array.
                     executions[i - totalFilteredExecutions] = execution;
@@ -512,7 +512,7 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
                 // If offerer and recipient on the execution are the same...
                 if (execution.item.recipient == execution.offerer) {
                     // increment total filtered executions.
-                    totalFilteredExecutions += 1;
+                    ++totalFilteredExecutions;
                 } else {
                     // Otherwise, assign the execution to the executions array.
                     executions[
@@ -765,7 +765,7 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
                 // If offerer and recipient on the execution are the same...
                 if (execution.item.recipient == execution.offerer) {
                     // increment total filtered executions.
-                    totalFilteredExecutions += 1;
+                    ++totalFilteredExecutions;
                 } else {
                     // Otherwise, assign the execution to the executions array.
                     executions[i - totalFilteredExecutions] = execution;

Assembly casting of address/bytes32 to uint256

function toUint256(bytes32 value) internal pure returns (uint256 result) {
    assembly {
        result := value
    }
}

function toUint256(address value) internal pure returns (uint256 result) {
    assembly {
        result := value
    }
}

In ConduitController.sol

In OrderValidator.sol, L280(18-36 gas):

- if (msg.sender != offerer && msg.sender != zone) {
+ if (toUint256(msg.sender) != toUint256(offerer) && toUint256(msg.sender) != toUint256(zone)) {

In SignatureVerification.sol, L97(18 gas):

- } else if (recoveredSigner != signer) {
+ } else if (toUint256(recoveredSigner) != toUint256(signer)) {

In ZoneInteraction.sol

In Verifiers.sol, L76(18 gas):

- if (offerer == msg.sender) {
+ if (toUint256(offerer) == toUint256(msg.sender)) {
HardlyDifficult commented 2 years ago

Cache arrays length on loops Use pre increment and pre decrement instead of post increment and post decrement

These should offer some savings.

Assembly casting of address/bytes32 to uint256

Although these recommendations are not great for code readability, they do offer savings and follow the general pattern in this project of using Yul to optimize as much as possible.