Platform: Code4rena
Start Date: 01/08/2022
Pot Size: $50,000 USDC
Total HM: 26
Participants: 133
Period: 5 days
Judge: Jack the Pug
Total Solo HM: 6
Id: 151
League: ETH
Rank: 68/133
Findings: 2
Award: $62.45
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Lambda
Also found by: 0x1f8b, 0x52, 0xA5DF, 0xNazgul, 0xNineDec, 0xSmartContract, 0xSolus, 0xf15ers, 0xkatana, 0xsolstars, 8olidity, Aymen0909, Bahurum, Bnke0x0, CertoraInc, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, Extropy, Funen, GalloDaSballo, Guardian, IllIllI, JC, Jujic, MEP, Noah3o6, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, SooYa, Soosh, Throne6g, TomJ, Tomio, TrungOre, Waze, Yiko, _Adam, __141345__, a12jmx, ajtra, ak1, arcoun, asutorufos, ayeslick, benbaessler, berndartmueller, bin2chen, bobirichman, brgltd, bulej93, byndooa, c3phas, codexploder, cryptonue, cryptphi, defsec, delfin454000, dipp, djxploit, erictee, exd0tpy, fatherOfBlocks, gogo, hake, hansfriese, horsefacts, hyh, ignacio, indijanc, joestakey, kaden, mics, minhquanym, neumo, obront, oyc_109, p_crypt0, pfapostol, poirots, rbserver, robee, rokinot, rotcivegaf, sach1r0, saian, samruna, saneryee, scaraven, sikorico, simon135, sseefried, supernova
40.692 USDC - $40.69
Wherever possible, the contract should follow the check-effects-interaction pattern.
On the changeOrder
function, the state is modified tasks[_taskID].cost = _newCost
after an external call is made autoWithdraw(_withdrawDifference)
.
File: contracts/Project.sol 435: autoWithdraw(_withdrawDifference); 464: tasks[_taskID].cost = _newCost;
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
If the external call throws, the state should be reverted. Also, since the value for tasks[_taskID].cost
is already cached on the _taskCost
variable, updating the state before the external call shouldn't impact the bussiness logic.
Even if the function is already using the nonReetrant modifier, I would still recommend updating the state before making external calls.
$ git diff --no-index Project.sol.orig Project.sol diff --git a/Project.sol.orig b/Project.sol index 7e0ae45..2644d87 100644 --- a/Project.sol.orig +++ b/Project.sol @@ -411,6 +411,9 @@ contract Project is // Local variable indicating if subcontractor is already unapproved. bool _unapproved = false; + // Store new cost for the task + tasks[_taskID].cost = _newCost; + // If task cost is to be changed. if (_newCost != _taskCost) { // Check new task cost precision. Revert if too precise. @@ -460,9 +463,6 @@ contract Project is } } - // Store new cost for the task - tasks[_taskID].cost = _newCost; - emit ChangeOrderFee(_taskID, _newCost); }
Critical inputs of type address should not be able to unintentionally receive the value zero.
Not checking agaist input address zero can cause misuse of tokens and force redeployments.
File: contracts/Project.sol function initialize( address _currency, address _sender, address _homeFiAddress ) external override initializer { // Initialize variables homeFi = IHomeFi(_homeFiAddress); disputes = homeFi.disputesContract(); lenderFee = homeFi.lenderFee(); builder = _sender; currency = IDebtToken(_currency); }
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
There's a typo on the following comment.
File: contracts/libraries/SignatureDecoder.sol 8: * @notice Decodes signatures that a encoded as bytes
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/libraries/SignatureDecoder.sol
The function signatureSplit
should contain a description of the returned rsv
data. Consider adding the @return
tag to ensure natspec best practices.
File: contracts/libraries/SignatureDecoder.sol /** * @dev Divides bytes signature into `uint8 v, bytes32 r, bytes32 s`. * @dev Make sure to perform a bounds check for @param pos, to avoid out of bounds access on @param signatures * @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access * @param signatures concatenated rsv signatures */ function signatureSplit(bytes memory signatures, uint256 pos) internal pure returns ( uint8 v, bytes32 r, bytes32 s ) {
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/libraries/SignatureDecoder.sol
File: contracts/Community.sol 709: function isTrustedForwarder(address _forwarder)
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Community.sol
Instead of using the number of seconds, solidity allows the usage of time units such as days and weeks to improve redability.
File: contracts/Community.sol 686: _communityProject.lastTimestamp) / 86400; // 24*60*60
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Community.sol
🌟 Selected for report: c3phas
Also found by: 0x040, 0x1f8b, 0xA5DF, 0xNazgul, 0xSmartContract, 0xSolus, 0xc0ffEE, 0xkatana, 0xsam, 8olidity, Aymen0909, Bnke0x0, CertoraInc, Chinmay, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, Extropy, Fitraldys, Funen, GalloDaSballo, Guardian, IllIllI, JC, Lambda, MEP, Metatron, MiloTruck, Noah3o6, NoamYakov, PaludoX0, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, SooYa, TomJ, Tomio, Waze, _Adam, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, ballx, benbaessler, bharg4v, bobirichman, brgltd, cryptonue, defsec, delfin454000, dharma09, djxploit, durianSausage, eierina, erictee, fatherOfBlocks, gerdusx, gogo, hake, hyh, ignacio, jag, kaden, kyteg, lucacez, mics, minhquanym, oyc_109, pfapostol, rbserver, ret2basic, robee, rokinot, sach1r0, saian, samruna, scaraven, sikorico, simon135, supernova, teddav, tofunmi, zeesaw
21.7554 USDC - $21.76
Saves about 5 gas per loop.
File: contracts/Project.sol 248: for (uint256 i = 0; i < _length; i++) { 603: for (; i < _changeOrderedTask.length; i++) { 625: _loopCount++; 710: for (uint256 _taskID = 1; _taskID <= _length; _taskID++) {
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
The solidity compiler will apply arithmetic checks for the increment step during for loops. This can be disabled since the value being incremented won't surpass the upper bound that's checked on the break condition.
Adding uncheck can save 30-40 gas per loop.
File: contracts/Project.sol 248: for (uint256 i = 0; i < _length; i++) { 603: for (; i < _changeOrderedTask.length; i++) { 710: for (uint256 _taskID = 1; _taskID <= _length; _taskID++) {
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
Instead of computing array.length for every iteration, the value for array.length should be cached before the loop to save gas.
File: contracts/Project.sol 603: for (; i < _changeOrderedTask.length; i++) {
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
File: contracts/Project.sol 248: for (uint256 i = 0; i < _length; i++) {
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
Saves about 6 gas.
File: contracts/Project.sol 195: require(_cost > 0, "Project::!value>0"); 691: if (_loopCount > 0) emit TaskAllocated(_tasksAllocated);
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
File: contracts/Project.sol 123: require(!contractorConfirmed, "Project::GC accepted"); 132: require(_projectAddress == address(this), "Project::!projectAddress");
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Project.sol
File: contracts/Community.sol 69: require(_address != address(0), "Community::0 address"); 75: require(_msgSender() == homeFi.admin(), "Community::!admin"); 81: require(
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Community.sol
File: contracts/HomeFi.sol 142: require(!addrSet, "HomeFi::Set"); 191: require(lenderFee != _newLenderFee, "HomeFi::!Change");
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/HomeFi.sol