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: 13/59
Findings: 2
Award: $4,262.12
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Spearbit
Also found by: 0xsanson, Chom, IllIllI, OriDabush, Saw-mon_and_Natalie, broccoli, cccz, cmichel, csanuragjain, foobar, hack3r-0m, hickuphh3, hubble, hyh, ilan, kebabsec, mayo, oyc_109, peritoflores, rfa, scaraven, sces60107, shung, sorrynotsorry, tintin, twojoy, zkhorse, zzzitron
3722.4717 USDC - $3,722.47
_aggregateAvailable function of ReferenceFulfillmentApplier
// Set the offerer as the receipient if execution amount is nonzero. if (execution.item.amount == 0) { execution.item.recipient = payable(execution.offerer); }
Notice that comment said "if execution amount is nonzero." but code is if (execution.item.amount == 0) {
The correct comment is "if execution amount is zero."
This section also doesn't appear in ReferenceFulfillmentApplier
ERC1155 <=> ETH (match, single item) case
const consideration = [];
Clearly wrong, consideration is empty but it should have ETH to be aligned with testcase title.
As above testcase is accidentally missing consideration. Wouldn't it be great to secure it in contract at first? To prevent user error which lead to losing of the fund.
I cannot find testcase where both offer has a ERC1155 and consideration has a ERC1155.
#0 - HardlyDifficult
2022-06-20T18:35:25Z
#1 - HardlyDifficult
2022-06-20T20:52:40Z
🌟 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
539.6542 USDC - $539.65
// Ensure fraction can be applied to the value with no remainder. Note // that the denominator cannot be zero. bool exact; assembly { // Ensure new value contains no remainder via mulmod operator. // Credit to @hrkrshnn + @axic for proposing this optimal solution. exact := iszero(mulmod(value, numerator, denominator)) } // Ensure that division gave a final result with no remainder. if (!exact) { revert InexactFraction(); }
Can be simplified into
assembly { if iszero(mulmod(value, numerator, denominator)) { mstore(0, InexactFraction_error_signature) revert(0, InexactFraction_error_len) } }
Where InexactFraction_error_signature
and InexactFraction_error_len
is precomputed and stored as constant based on revert InexactFraction();
This reduce gas on storing exact
flag and duplicated if (!exact)
check
We can add
// Set the offerer as the receipient if execution amount is zero. if (execution.item.amount == 0) { execution.item.recipient = payable(execution.offerer); }
Before returning into
// Reuse execution struct with consideration amount and recipient. execution.item.amount = considerationItem.amount; execution.item.recipient = considerationItem.recipient; // Return the final execution that will be triggered for relevant items. return execution; // Execution(considerationItem, offerer, conduitKey);
which result in
// Reuse execution struct with consideration amount and recipient. execution.item.amount = considerationItem.amount; execution.item.recipient = considerationItem.recipient; // Set the offerer as the receipient if execution amount is zero. if (execution.item.amount == 0) { execution.item.recipient = payable(execution.offerer); } // Return the final execution that will be triggered for relevant items. return execution; // Execution(considerationItem, offerer, conduitKey);
To let execution with zero amount being skipped in _executeAvailableFulfillments function in OrderCombiner
// Derive aggregated execution corresponding with fulfillment. Execution memory execution = _aggregateAvailable( advancedOrders, Side.OFFER, components, fulfillerConduitKey ); // If offerer and recipient on the execution are the same... if (execution.item.recipient == execution.offerer) { // increment total filtered executions. totalFilteredExecutions += 1; } else { // Otherwise, assign the execution to the executions array. executions[i - totalFilteredExecutions] = execution; }
Which lead to following optimization path:
#0 - HardlyDifficult
2022-06-26T15:45:22Z
These optimizations should offer some savings and seem worth considering.