code-423n4 / 2022-06-putty-findings

5 stars 0 forks source link

Gas Optimizations #234

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

GAS

unckecked{} and ++i can be used in for-loops to reduce gas cost of execution and deployment cost.

This does slightly reduced code readability but could be beneficial if large arrays are expected to be used.

Example


for (uint256 i = 0; i < orders.length;) {
    positionIds[i] = fillOrder(orders[i], signatures[i], floorAssetTokenIds[i]);

    unchecked {
        ++i;
    }
}

Locations where this can be implemented

[1. File: PuttyV2.sol#L556]()

[2. File: PuttyV2.sol#L598]()

[3. File: PuttyV2.sol#L615]()

[4. File: PuttyV2.sol#L641]()

[5. File: PuttyV2.sol#L651]()

[6. File: PuttyV2.sol#L662]()

[7. File: PuttyV2.sol#L674]()

[8. File: PuttyV2.sol#L732]()

[9. File: PuttyV2.sol#L746]()

Ordering require() statements from least gas to most gas when possible will make failing cheaper on average.

These four require statements can be placed at the start of fillorder()

[1. File: PuttyV2.sol#L275-293]()

// check duration is valid
require(order.duration < 10_000 days, "Duration too long");

// check order has not expired
require(block.timestamp < order.expiration, "Order has expired");

// check base asset exists
require(order.baseAsset.code.length > 0, "baseAsset is not contract");

// check floor asset token ids length is 0 unless the order type is call and side is long
order.isCall && order.isLong
    ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
    : require(floorAssetTokenIds.length == 0, "Invalid floor tokens length");

like so

// check duration is valid
require(order.duration < 10_000 days, "Duration too long");

// check order has not expired
require(block.timestamp < order.expiration, "Order has expired");

// check base asset exists
require(order.baseAsset.code.length > 0, "baseAsset is not contract");

// check floor asset token ids length is 0 unless the order type is call and side is long
order.isCall && order.isLong
    ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
    : require(floorAssetTokenIds.length == 0, "Invalid floor tokens length");

bytes32 orderHash = hashOrder(order);

// check signature is valid using EIP-712
require(SignatureChecker.isValidSignatureNow(order.maker, orderHash, signature), "Invalid signature");

// check order is not cancelled
require(!cancelledOrders[orderHash], "Order has been cancelled");

// check msg.sender is allowed to fill the order
require(order.whitelist.length == 0 || isWhitelisted(order.whitelist, msg.sender), "Not whitelisted");

The same can be done for the below require statements in exercise()

[2. File: PuttyV2.sol#L398-406]()


// check position is long
require(order.isLong, "Can only exercise long positions");

// check floor asset token ids length is 0 unless the position type is put
!order.isCall
    ? require(floorAssetTokenIds.length == order.floorTokens.length, "Wrong amount of floor tokenIds")
    : require(floorAssetTokenIds.length == 0, "Invalid floor tokenIds length");