Platform: Code4rena
Start Date: 05/09/2023
Pot Size: $50,000 USDC
Total HM: 2
Participants: 16
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 2
Id: 284
League: ETH
Rank: 6/16
Findings: 1
Award: $451.09
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Sathish9098
Also found by: Baki
451.087 USDC - $451.09
Gas Optimizations | Issues | Instances |
---|---|---|
[G-01] | processSpentItems should have non-array input to save gas | 1 |
Total issues: 1 instances across 1 issue Total gas saved: 1189 gas
Total gas saved: 1189 gas
https://github.com/code-423n4/2023-09-delegate/blob/main/src/libraries/CreateOffererLib.sol#L346
Before VS After Function execution cost: 45583 - 44394 = 1189 gas saved (min.)
Instead of this:
function processSpentItems(SpentItem[] calldata minimumReceived, SpentItem[] calldata maximumSpent) internal view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { if (!(minimumReceived.length == 1 && maximumSpent.length == 1)) revert CreateOffererErrors.NoBatchWrapping(); if (minimumReceived[0].itemType != ItemType.ERC721 || minimumReceived[0].token != address(this) || minimumReceived[0].amount != 1) { revert CreateOffererErrors.MinimumReceivedInvalid(minimumReceived[0]); } if (maximumSpent[0].itemType != ItemType.ERC721 && maximumSpent[0].itemType != ItemType.ERC20 && maximumSpent[0].itemType != ItemType.ERC1155) { revert CreateOffererErrors.MaximumSpentInvalid(maximumSpent[0]); } offer = new SpentItem[](1); offer[0] = SpentItem({itemType: minimumReceived[0].itemType, token: minimumReceived[0].token, identifier: minimumReceived[0].identifier, amount: minimumReceived[0].amount}); consideration = new ReceivedItem[](1); consideration[0] = ReceivedItem({ itemType: maximumSpent[0].itemType, token: maximumSpent[0].token, identifier: maximumSpent[0].identifier, amount: maximumSpent[0].amount, recipient: payable(address(this)) }); }
Use this:
function processSpentItems(SpentItem memory minimumReceived, SpentItem memory maximumSpent) internal view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { if (minimumReceived.itemType != ItemType.ERC721 || minimumReceived.token != address(this) || minimumReceived.amount != 1) { revert CreateOffererErrors.MinimumReceivedInvalid(minimumReceived); } if (maximumSpent.itemType != ItemType.ERC721 && maximumSpent.itemType != ItemType.ERC20 && maximumSpent.itemType != ItemType.ERC1155) { revert CreateOffererErrors.MaximumSpentInvalid(maximumSpent); } offer = new SpentItem[](1); offer[0] = SpentItem({itemType: minimumReceived.itemType, token: minimumReceived.token, identifier: minimumReceived.identifier, amount: minimumReceived.amount}); consideration = new ReceivedItem[](1); consideration[0] = ReceivedItem({ itemType: maximumSpent.itemType, token: maximumSpent.token, identifier: maximumSpent.identifier, amount: maximumSpent.amount, recipient: payable(address(this)) }); }
#0 - GalloDaSballo
2023-10-02T08:51:23Z
Great refactoring imo
#1 - c4-judge
2023-10-02T09:08:45Z
GalloDaSballo marked the issue as grade-a
#2 - 0xfoobar
2023-10-03T23:21:09Z
This isn't possible to change, this is the interface that Seaport expects
#3 - c4-sponsor
2023-10-03T23:21:22Z
0xfoobar (sponsor) disputed
#4 - GalloDaSballo
2023-10-04T07:50:48Z
This seems to compile
Rewriting the tests looks very laborious
CreateOfferer.sol
function generateOrder(address fulfiller, SpentItem[] calldata minimumReceived, SpentItem[] calldata maximumSpent, bytes calldata context) external checkStage(Enums.Stage.generate, Enums.Stage.transfer) onlySeaport(msg.sender) returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { if (context.length != 160) revert Errors.InvalidContextLength(); Structs.Context memory decodedContext = abi.decode(context, (Structs.Context)); /// @audit Add some validation here (offer, consideration) = Helpers.processSpentItems(minimumReceived[0], maximumSpent[0]); Helpers.updateTransientState(transientState, fulfiller, minimumReceived[0], maximumSpent[0], decodedContext); }
CreateOffererLib.sol
function processSpentItems(SpentItem calldata minimumReceived, SpentItem calldata maximumSpent) internal view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { if (minimumReceived.itemType != ItemType.ERC721 || minimumReceived.token != address(this) || minimumReceived.amount != 1) { revert CreateOffererErrors.MinimumReceivedInvalid(minimumReceived); } if (maximumSpent.itemType != ItemType.ERC721 && maximumSpent.itemType != ItemType.ERC20 && maximumSpent.itemType != ItemType.ERC1155) { revert CreateOffererErrors.MaximumSpentInvalid(maximumSpent); } offer = new SpentItem[](1); offer[0] = SpentItem({itemType: minimumReceived.itemType, token: minimumReceived.token, identifier: minimumReceived.identifier, amount: minimumReceived.amount}); consideration = new ReceivedItem[](1); consideration[0] = ReceivedItem({ itemType: maximumSpent.itemType, token: maximumSpent.token, identifier: maximumSpent.identifier, amount: maximumSpent.amount, recipient: payable(address(this)) }); }