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
Rank: 90/116
Findings: 1
Award: $12.58
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xA5DF
Also found by: 0xmystery, 7ashraf, AkshaySrivastav, CipherSleuths, NentoR, SBSecurity, Tendency, ZanyBonzy, ZdravkoHr, dd0x7e8, hals, haxatron, invitedtea, jasonxiale, juancito, kaden, krikolkk, ladboy233, oakcobalt, peanuts, petro_1912, pkqs90, plasmablocks, ravikiranweb3, rbserver, rokinot, souilos
12.582 USDC - $12.58
The smart contract repository review identified key issues, primarily the need for equal-length array checks in functions like _processBaseOrderOffer
and others. Instances of using array objects without validation were noted in _checkTransaction
and sections of Create.sol
, suggesting the implementation of validation checks. Additionally, the executeAction
function in Kernel.sol
lacks a final else
statement with a revert, impacting event accuracy. Hardcoded zero values, as seen in Stop.sol
, could benefit from more descriptive values. Lastly, functions with empty bodies and unclear readability indicate areas for code clarity improvement.
Summary Table:
Issue Number | Title | Number of Instances |
---|---|---|
L-01 | Require the two arrays to have the same length | 6 |
L-02 | Check if the array object is valid or not | 7 |
L-03 | Add final else statement with revert to avoid false emits | 1 |
L-04 | Add a list of known roles to choose from them instead of validating roles | 1 |
L-05 | Avoid using hardcoded zero value | 1 |
L-06 | Disable fee changing when a settle payment is running | 1 |
L-07 | skim should be called after settlePayment and settlePaymentBatch | 1 |
N-01 | Hard to read function, consider cleaning | 1 |
N-02 | Empty function body | 1 |
N-03 | Emit an event on fee change | 2 |
N-04 | Reverting with an error is more convenient than returning address(0) | 1 |
N-05 | Reduce redundant code | 1 |
function _processBaseOrderOffer( Item[] memory rentalItems, SpentItem[] memory offers, uint256 startIndex ) internal pure { + require rentalItems.length == offers.length; ... }
function _processPayOrderOffer( Item[] memory rentalItems, SpentItem[] memory offers, uint256 startIndex ) internal pure { + require rentalItems.length == offers.length; ... }
function _processBaseOrderConsideration( Item[] memory rentalItems, ReceivedItem[] memory considerations, uint256 startIndex ) internal pure { + require rentalItems.length == considerations.length; ... }
function _convertToItems( SpentItem[] memory offers, ReceivedItem[] memory considerations, OrderType orderType ) internal pure returns (Item[] memory items) { + require offers.length == considerations.length; ... }
function _addHooks( Hook[] memory hooks, SpentItem[] memory offerItems, address rentalWallet ) internal { + require hooks.length == offerItems.length; ... }
function _removeHooks( Hook[] calldata hooks, Item[] calldata rentalItems, address rentalWallet ) internal { + require hooks.length == rentalItems.length; ... }
ReceivedItem memory consideration = considerations[i]; + //check for valid consideration
SpentItem memory offer = offers[i];
SpentItem memory offer = offers[i];
ReceivedItem memory consideration = considerations[i];
target = hooks[i].target;
RentalAssetUpdate memory asset = rentalAssetUpdates[i];
RentalAssetUpdate memory asset = rentalAssetUpdates[i];
else
statement with revert to avoid false emitsfunction executeAction(Actions action_, address target_) external onlyExecutor { if (action_ == Actions.InstallModule) { ensureContract(target_); ensureValidKeycode(Module(target_).KEYCODE()); _installModule(Module(target_)); } else if (action_ == Actions.UpgradeModule) { ensureContract(target_); ensureValidKeycode(Module(target_).KEYCODE()); _upgradeModule(Module(target_)); } else if (action_ == Actions.ActivatePolicy) { ensureContract(target_); _activatePolicy(Policy(target_)); } else if (action_ == Actions.DeactivatePolicy) { ensureContract(target_); _deactivatePolicy(Policy(target_)); } else if (action_ == Actions.MigrateKernel) { ensureContract(target_); _migrateKernel(Kernel(target_)); } else if (action_ == Actions.ChangeExecutor) { executor = target_; } else if (action_ == Actions.ChangeAdmin) { admin = target_; } + //add else statement and revert emit Events.ActionExecuted(action_, target_); }
function grantRole(Role role_, address addr_) public onlyAdmin { ... emit Events.RoleGranted(role_, addr_); }
bool success = ISafe(order.rentalWallet).execTransactionFromModule( // Stop policy inherits the reclaimer package. address(this), // value. 172: 0, // The encoded call to the `reclaimRentalOrder` function. abi.encodeWithSelector(this.reclaimRentalOrder.selector, order), // Safe must delegate call to the stop policy so that it is the msg.sender. Enum.Operation.DelegateCall );
function setFee(uint256 feeNumerator) external onlyByProxy permissioned { + //Add lock // Cannot accept a fee numerator greater than 10000. if (feeNumerator > 10000) { revert Errors.PaymentEscrow_InvalidFeeNumerator(); } // Set the fee. fee = feeNumerator; + //Release lock }
skim
should be called after settlePayment
and settlePaymentBatch
function settlePayment(RentalOrder calldata order) external onlyByProxy permissioned { // Settle all payments for the order. _settlePayment( order.items, order.orderType, order.lender, order.renter, order.startTimestamp, order.endTimestamp ); }
function _checkTransaction(address from, address to, bytes memory data) private view { ... }
Although it is mentioned that the function is left empty, but is not implementedbefore an audit or a final release. Also no reason was mentioned.
/** * @notice Performs any checks after execution. This is left unimplemented. * * @param txHash Hash of the transaction. * @param success Whether the transaction succeeded. */ function checkAfterExecution(bytes32 txHash, bool success) external override {} /**
function setFee(uint256 feeNumerator) external onlyByProxy permissioned { // Cannot accept a fee numerator greater than 10000. if (feeNumerator > 10000) { revert Errors.PaymentEscrow_InvalidFeeNumerator(); } // Set the fee. fee = feeNumerator; + //emit fee chanaged event }
function settlePaymentBatch( RentalOrder[] calldata orders ) external onlyByProxy permissioned { // Loop through each order. for (uint256 i = 0; i < orders.length; ++i) { // Settle all payments for the order. _settlePayment( orders[i].items, orders[i].orderType, orders[i].lender, orders[i].renter, orders[i].startTimestamp, orders[i].endTimestamp ); } }
Consider adding a revert message like failed to fetch
or hook not available
instead of returning address(0) to the user for more convenient and understandable logic
return hookStatus[hook] != 0 ? hook : address(0);
Utilize the use of removeRental inside removeRentalBatch instead of repeating the same logic all over again
Rewrite the following method
function removeRentalsBatch( bytes32[] calldata orderHashes, RentalAssetUpdate[] calldata rentalAssetUpdates ) external onlyByProxy permissioned { // Delete the orders from storage. for (uint256 i = 0; i < orderHashes.length; ++i) { // The order must exist to be deleted. if (!orders[orderHashes[i]]) { revert Errors.StorageModule_OrderDoesNotExist(orderHashes[i]); } else { // Delete the order from storage. delete orders[orderHashes[i]]; } } // Process each rental asset. for (uint256 i = 0; i < rentalAssetUpdates.length; ++i) { RentalAssetUpdate memory asset = rentalAssetUpdates[i]; // Reduce the amount of tokens for the particular rental ID. rentedAssets[asset.rentalId] -= asset.amount; } }
into something like this
function removeRentalsBatch( bytes32[] calldata orderHashes, RentalAssetUpdate[] calldata rentalAssetUpdates ) external onlyByProxy permissioned { // Delete the orders from storage. for (uint256 i = 0; i < orderHashes.length; ++i) { removeRental(orderHash[i], rentalAssetUpdates); } }
#0 - 141345
2024-01-22T13:50:04Z
334 7ashraf l r nc 0 1 10
L 1 n L 2 n L 3 n L 4 n L 5 n L 6 d dup of https://github.com/code-423n4/2024-01-renft-findings/issues/463 L 7 r N 1 n N 2 n N 3 n N 4 n N 5 n
#1 - c4-judge
2024-01-27T20:31:10Z
0xean marked the issue as grade-b