reNFT - 0x11singh99's results

Collateral-free, permissionless, and highly customizable EVM NFT rentals.

General Information

Platform: Code4rena

Start Date: 08/01/2024

Pot Size: $83,600 USDC

Total HM: 23

Participants: 116

Period: 10 days

Judge: 0xean

Total Solo HM: 1

Id: 317

League: ETH

reNFT

Findings Distribution

Researcher Performance

Rank: 63/116

Findings: 1

Award: $42.44

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: 0xAnah

Also found by: 0x11singh99, 0xhex, 0xta, SAQ, mgf15, shamsulhaq123, sivanesh_808, slvDev

Labels

bug
G (Gas Optimization)
grade-b
sufficient quality report
G-09

Awards

42.4402 USDC - $42.44

External Links

Gas Optimizations

NumberIssue
[G-01]Pack structs by putting variables that can fit together next to each other (saves 4000 Gas)
[G-02]Cache the function call instead of re-calling them
[G-03]Avoid writing storage variable with same value (Not in bot report)
[G-04]No need to cache Function call if called only once
[G-05]Don't cache state variable if it is used only once

[G-01] Pack structs by putting variables that can fit together next to each other (saves 4000 Gas)

SAVE: 4000 GAS, 2 SLOT

Note: This packing is in RentalStructs.sol file which is out of scope. But I have added that since this is used by in scope contracts and it's struct variables declared here. Occupying the storage slots of in scope of contracts. By improving size in that file can save slots actually in contracts. And saves lot of gas in scope contracts.

As the solidity EVM works with 32 bytes, variables less than 32 bytes should be packed inside a struct so that they can be stored in the same slot, this saves gas when writing to storage ~20000 gas

truncate startTimestamp and endTimestamp to uint40 and pack with rentalWallet :SAVES 4000 GAS, 2 SLOT

The uint40 type is safe for approximately 1.099511627776e+12 years. The exact number of years that a uint40 type is safe for is 1099511627776 years.

File : src/libraries/RentalStructs.sol

struct RentalOrder {
    bytes32 seaportOrderHash;
    Item[] items;
    Hook[] hooks;
    OrderType orderType;
    address lender;
    address renter;
    address rentalWallet;
+    uint40 startTimestamp;
+    uint40 endTimestamp;
-    uint256 startTimestamp;
-    uint256 endTimestamp;
}

133-143

[G-02] Cache the function call instead of re-calling them

16 Instances

Result of function toKeycode("STORE") and toKeycode("ESCRW") should be cached instead of re-calling them

File : src/policies/Admin.sol

    function configureDependencies()
        external
        override
        onlyKernel
        returns (Keycode[] memory dependencies)
    {
        dependencies = new Keycode[](2);

        dependencies[0] = toKeycode("STORE");
        STORE = Storage(getModuleAddress(toKeycode("STORE")));

        dependencies[1] = toKeycode("ESCRW");
        ESCRW = PaymentEscrow(getModuleAddress(toKeycode("ESCRW")));
    }

    ...

    function requestPermissions()
        external
        view
        override
        onlyKernel
        returns (Permissions[] memory requests)
    {
        requests = new Permissions[](8);
        requests[0] = Permissions(
            toKeycode("STORE"),
            STORE.toggleWhitelistExtension.selector
        );
        requests[1] = Permissions(
            toKeycode("STORE"),
            STORE.toggleWhitelistDelegate.selector
        );
        requests[2] = Permissions(toKeycode("STORE"), STORE.upgrade.selector);
        requests[3] = Permissions(toKeycode("STORE"), STORE.freeze.selector);

        requests[4] = Permissions(toKeycode("ESCRW"), ESCRW.skim.selector);
        requests[5] = Permissions(toKeycode("ESCRW"), ESCRW.setFee.selector);
        requests[6] = Permissions(toKeycode("ESCRW"), ESCRW.upgrade.selector);
        requests[7] = Permissions(toKeycode("ESCRW"), ESCRW.freeze.selector);
    }

40-53, 64-87


-import {toKeycode} from "@src/libraries/KernelUtils.sol";
+import {toKeycode, keycode} from "@src/libraries/KernelUtils.sol";
...
    function configureDependencies()
        external
        override
        onlyKernel
        returns (Keycode[] memory dependencies)
    {
        dependencies = new Keycode[](2);

+       keycode _tokeycodeSTORE = toKeycode("STORE");        
+       keycode _tokeycodeESCRW = toKeycode("ESCRW");        

-        dependencies[0] = toKeycode("STORE");
-        STORE = Storage(getModuleAddress(toKeycode("STORE")));
-
-        dependencies[1] = toKeycode("ESCRW");
-        ESCRW = PaymentEscrow(getModuleAddress(toKeycode("ESCRW")));

+        dependencies[0] = _tokeycodeSTORE;
+        STORE = Storage(getModuleAddress(_tokeycodeSTORE));
+  
+        dependencies[1] = _tokeycodeESCRW;
+        ESCRW = PaymentEscrow(getModuleAddress(_tokeycodeESCRW));
    }

    ...

    function requestPermissions()
        external
        view
        override
        onlyKernel
        returns (Permissions[] memory requests)
    {

+       keycode _tokeycodeSTORE = toKeycode("STORE");        
+       keycode _tokeycodeESCRW = toKeycode("ESCRW");

        requests = new Permissions[](8);
-        requests[0] = Permissions(
-            toKeycode("STORE"),
-            STORE.toggleWhitelistExtension.selector
-        );
-        requests[1] = Permissions(
-            toKeycode("STORE"),
-            STORE.toggleWhitelistDelegate.selector
-        );
-        requests[2] = Permissions(toKeycode("STORE"), STORE.upgrade.selector);
-        requests[3] = Permissions(toKeycode("STORE"), STORE.freeze.selector);

-        requests[4] = Permissions(toKeycode("ESCRW"), ESCRW.skim.selector);
-        requests[5] = Permissions(toKeycode("ESCRW"), ESCRW.setFee.selector);
-        requests[6] = Permissions(toKeycode("ESCRW"), ESCRW.upgrade.selector);
-        requests[7] = Permissions(toKeycode("ESCRW"), ESCRW.freeze.selector);

+        requests[0] = Permissions(
+            _tokeycodeSTORE,
+            STORE.toggleWhitelistExtension.selector
+        );
+        requests[1] = Permissions(
+            _tokeycodeSTORE,
+            STORE.toggleWhitelistDelegate.selector
+        );
+        requests[2] = Permissions(_tokeycodeSTORE, STORE.upgrade.selector);
+        requests[3] = Permissions(_tokeycodeSTORE, STORE.freeze.selector);

+        requests[4] = Permissions(_tokeycodeESCRW, ESCRW.skim.selector);
+        requests[5] = Permissions(_tokeycodeESCRW, ESCRW.setFee.selector);
+        requests[6] = Permissions(_tokeycodeESCRW, ESCRW.upgrade.selector);
+        requests[7] = Permissions(_tokeycodeESCRW, ESCRW.freeze.selector);
    }

Result of function toKeycode("STORE") should be cached

File : src/policies/Guard.sol

import {toKeycode} from "@src/libraries/KernelUtils.sol";
...
    function configureDependencies()
        external
        override
        onlyKernel
        returns (Keycode[] memory dependencies)
    {
        dependencies = new Keycode[](1);

        dependencies[0] = toKeycode("STORE");
        STORE = Storage(getModuleAddress(toKeycode("STORE")));
    }

...
    function requestPermissions()
        external
        view
        override
        onlyKernel
        returns (Permissions[] memory requests)
    {
        requests = new Permissions[](2);
        requests[0] = Permissions(toKeycode("STORE"), STORE.updateHookPath.selector);
        requests[1] = Permissions(toKeycode("STORE"), STORE.updateHookStatus.selector);
    }

10-95

File : src/policies/Guard.sol

-import {toKeycode} from "@src/libraries/KernelUtils.sol";
+import {toKeycode,keycode} from "@src/libraries/KernelUtils.sol";
...
    function configureDependencies()
        external
        override
        onlyKernel
        returns (Keycode[] memory dependencies)
    {
        dependencies = new Keycode[](1);

+       keycode _tokeycodeSTORE = toKeycode("STORE");        


-        dependencies[0] = toKeycode("STORE");
-        STORE = Storage(getModuleAddress(toKeycode("STORE")));

+        dependencies[0] = _tokeycodeSTORE;
+        STORE = Storage(getModuleAddress(_tokeycodeSTORE));
    }

...
    function requestPermissions()
        external
        view
        override
        onlyKernel
        returns (Permissions[] memory requests)
    {
        requests = new Permissions[](2);

+       keycode _tokeycodeSTORE = toKeycode("STORE"); 

-        requests[0] = Permissions(toKeycode("STORE"), STORE.updateHookPath.selector);
-        requests[1] = Permissions(toKeycode("STORE"), STORE.updateHookStatus.selector);
+        requests[0] = Permissions(_tokeycodeSTORE, STORE.updateHookPath.selector);
+        requests[1] = Permissions(_tokeycodeSTORE, STORE.updateHookStatus.selector);
    }

[G-03] Avoid writing storage variable with same value (Not in bot report)

In Solidity, optimizing gas usage involves avoiding unnecessary storage updates when the new value equals the existing one. This can save significant gas, around 2900 for 'Gsreset.' However, this may result in a 2100 gas cost for 'Gcoldsload' or a 100 gas cost for 'Gwarmaccess.'

To enhance gas efficiency, add a check to compare new and current values before any storage update: bypass the operation if they are the same.

1 Instance

File : src/Kernel.sol

192: function setActiveStatus(bool activate_) external onlyKernel {
193:        isActive = activate_;
194:    }

192-194

[G-04] No need to cache Function call if called only once

Don't cache _calculateFee(paymentAmount)

File : src/modules/PaymentEscrow.sol

242:  if (fee != 0) {
243:  // Calculate the new fee.
244:  uint256 paymentFee = _calculateFee(paymentAmount);
245:
246:  // Adjust the payment amount by the fee.
247:   paymentAmount -= paymentFee;
248:  }

242-248

File : src/modules/PaymentEscrow.sol

    if (fee != 0) {
        // Calculate the new fee.
-        uint256 paymentFee = _calculateFee(paymentAmount);

            // Adjust the payment amount by the fee.
-            paymentAmount -= paymentFee;
+            paymentAmount -= _calculateFee(paymentAmount);
        }

Don't cache 0

File : src/modules/PaymentEscrow.sol

402:  uint256 trueBalance = IERC20(token).balanceOf(address(this));

402

File : src/modules/PaymentEscrow.sol

function skim(address token, address to) external onlyByProxy permissioned {
        // Fetch the currently synced balance of the escrow.
        uint256 syncedBalance = balanceOf[token];

        // Fetch the true token balance of the escrow.
-        uint256 trueBalance = IERC20(token).balanceOf(address(this));

        // Calculate the amount to skim.
-        uint256 skimmedBalance = trueBalance - syncedBalance;
+        uint256 skimmedBalance = IERC20(token).balanceOf(address(this)) - syncedBalance;

        // Send the difference to the specified address.
        _safeTransfer(token, to, skimmedBalance);

        // Emit event with fees taken.
        emit Events.FeeTaken(token, skimmedBalance);
    }

[G-05] Don't cache state variable if it is used only once

Don't cache balanceOf[token]

File : src/modules/PaymentEscrow.sol

399: uint256 syncedBalance = balanceOf[token];

397-412

File : src/modules/PaymentEscrow.sol

function skim(address token, address to) external onlyByProxy permissioned {
        // Fetch the currently synced balance of the escrow.
-       uint256 syncedBalance = balanceOf[token];

        // Fetch the true token balance of the escrow.
        uint256 trueBalance = IERC20(token).balanceOf(address(this));

        // Calculate the amount to skim.
-        uint256 skimmedBalance = trueBalance - syncedBalance;
+        uint256 skimmedBalance = trueBalance - balanceOf[token];

        // Send the difference to the specified address.
        _safeTransfer(token, to, skimmedBalance);

        // Emit event with fees taken.
        emit Events.FeeTaken(token, skimmedBalance);
    }

#0 - c4-pre-sort

2024-01-21T18:26:18Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-01-22T08:45:15Z

516 0x11singh99 l r nc 1 1 3

G 1 r G 2 l G 3 n G 4 n G 5 n

#2 - c4-judge

2024-01-27T20:21:51Z

0xean marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter