Platform: Code4rena
Start Date: 20/05/2022
Pot Size: $1,000,000 USDC
Total HM: 4
Participants: 59
Period: 14 days
Judge: leastwood
Id: 128
League: ETH
Rank: 39/59
Findings: 1
Award: $485.02
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Dravee
Also found by: 0x1f8b, 0x29A, 0xalpharush, Chom, Czar102, Hawkeye, IllIllI, MaratCerby, MiloTruck, NoamYakov, OriDabush, RoiEvenHaim, Spearbit, Tadashi, TerrierLover, TomJ, asutorufos, cccz, cmichel, csanuragjain, defsec, delfin454000, djxploit, ellahi, foobar, gzeon, hake, hickuphh3, ignacio, ilan, joestakey, kaden, mayo, ming, oyc_109, peritoflores, rfa, sach1r0, sashik_eth, shung, sirhashalot, twojoy, zer0dot, zkhorse
485.0212 USDC - $485.02
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];
++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;
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
- if (address(uint160(bytes20(conduitKey))) != msg.sender) { + if (toUint256(conduitKey) != toUint256(msg.sender)) {
- if (newPotentialOwner == address(0)) { + if (toUint256(newPotentialOwner) == 0) {
- if (msg.sender != _conduits[conduit].potentialOwner) { + if (toUint256(msg.sender) != toUint256(_conduits[conduit].potentialOwner)) {
conduit = address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), address(this), conduitKey, _CONDUIT_CREATION_CODE_HASH ) ) ) ) ); - if (msg.sender != _conduits[conduit].potentialOwner) { + if (toUint256(msg.sender) != toUint256(_conduits[conduit].potentialOwner)) {
- if (msg.sender != _conduits[conduit].owner) { + if (toUint256(msg.sender) != toUint256(_conduits[conduit].owner)) {
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
- msg.sender != zone && - msg.sender != offerer + toUint256(msg.sender) != toUint256(zone) && + toUint256(msg.sender) != toUint256(offerer)
- msg.sender != zone && - msg.sender != offerer + toUint256(msg.sender) != toUint256(zone) && + toUint256(msg.sender) != toUint256(offerer)
In Verifiers.sol, L76(18 gas):
- if (offerer == msg.sender) { + if (toUint256(offerer) == toUint256(msg.sender)) {
#0 - HardlyDifficult
2022-06-26T15:41:17Z
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.