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: 54/133
Findings: 2
Award: $84.43
🌟 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
62.7082 USDC - $62.71
However, it is indeed possible to submit a project with a total budget of 0. Observe the following passing test cases:
projectTests.ts
it('is able to add task with 0 budget', async () => { const hashArray = ['0x']; const costArray = [0]; // 0 total cost taskList.push(0); const data = { types: ['bytes[]', 'uint256[]', 'uint256', 'address'], values: [ hashArray, costArray, await project.taskCount(), project.address, ], }; const [encodedData, signature] = await multisig(data, [signers[0]]); const tx = await project.addTasks(encodedData, signature); await expect(tx) .to.emit(project, 'TasksAdded') .withArgs(costArray, hashArray); expect(await project.taskCount()).to.equal(1); const { cost, subcontractor, state } = await project.getTask(1); expect(cost).to.equal(costArray[0]); expect(subcontractor).to.equal(ethers.constants.AddressZero); expect(state).to.equal(1); });
communityTests.ts (edit createTasks
in projectHelpers.ts so costArray is [0,0,0])
it('should publish project with 0 budget', async () => { apr = 10; const communityId = 1; expect(await project.projectCost()).to.eq(0); //indeed 0 budget const data = { types: ['uint256', 'address', 'uint256', 'uint256', 'uint256', 'bytes'], values: [ communityId, projectAddress, apr, publishFeeAmount, (await communityContract.communities(communityId)).publishNonce, sampleHash, ], }; const [encodedData, signature] = await multisig(data, [ community1Owner, signers[0], ]); const tx = await communityContract.publishProject(encodedData, signature); await expect(tx) .to.emit(communityContract, 'ProjectPublished') .withArgs( communityId, projectAddress, apr, publishFeeAmount, publishFeeAmount.gt(0) ? false : true, sampleHash, ); const [ projectApr, projectLendingNeeded, projectTotalLent, publishFee, publishFeePaid, ] = await communityContract.projectDetails(communityId, projectAddress); expect(projectApr).to.be.equal(apr); expect(projectLendingNeeded).to.be.equal(0); expect(projectTotalLent).to.be.equal(0); expect(publishFee).to.be.equal(publishFeeAmount); expect(publishFeePaid).to.be.equal(false); const { publishNonce } = await communityContract.communities(communityId); expect(publishNonce).to.equal(1); });
changeOrder
to lower the cost of a task after it has been allocated without any sort of consent from the community (only contractors).Perhaps include the relevant community/community owner as a signer for such a change.
Community.sol::849
, The comment indicates that wrapped tokens are burned when they are in fact minted.
Myriad internal/private functions lack a prepended _
:
Community.sol
checkSignatureValidity
claimInterest
Disputes.sol
resolveHandler
executeTaskAdd
executeTaskChange
executeTaskPay
HomeFi.sol
mintNFT
Project.sol
autoWithdraw
checkSignature
checkSignatureTask
checkSignatureValidity
checkPrecision
HomeFi.sol::200
, there is no corresponding event emitted when updating the trustedForwarder
in the setTrustedForwarder
function.
Tasks.sol::146,158
, These comments do not match what the corresponding functions do. These comments are copies of line 136 “Task the task being set as funded” on function fundTask
.
🌟 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.7223 USDC - $21.72
onlyInitializing
modifier from the initializable.sol
openzeppelin contract. Therefore the initializer modifier is gas inefficient, as it unnecessarily performs logic for _initializing
.Potentially inherit from another more gas-efficient initializable implementation or implement a simpler initializable functionality that avoids such gas inefficiencies.
contracts/Community.sol::624 => for (uint256 i = 0; i < _communities[_communityID].memberCount; i++) { contracts/HomeFiProxy.sol::87 => for (uint256 i = 0; i < _length; i++) { contracts/HomeFiProxy.sol::136 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::248 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::311 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::322 => for (uint256 i = 0; i < _length; i++) { contracts/libraries/Tasks.sol::181 => for (uint256 i = 0; i < _length; i++) _alerts[i] = _self.alerts[i];
contracts/Community.sol::618 => // Initiate empty equal equal to member count length contracts/HomeFiProxy.sol::78 => uint256 _length = allContractNames.length; contracts/HomeFiProxy.sol::80 => // Revert if _implementations length is wrong. Indicating wrong set of _implementations. contracts/HomeFiProxy.sol::81 => require(_length == _implementations.length, "Proxy::Lengths !match"); contracts/HomeFiProxy.sol::87 => for (uint256 i = 0; i < _length; i++) { contracts/HomeFiProxy.sol::130 => uint256 _length = _contractNames.length; contracts/HomeFiProxy.sol::132 => // Revert if _contractNames and _contractAddresses length mismatch contracts/HomeFiProxy.sol::133 => require(_length == _contractAddresses.length, "Proxy::Lengths !match"); contracts/HomeFiProxy.sol::136 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::243 => // Revert if IPFS hash array length is not equal to task cost array length. contracts/Project.sol::244 => uint256 _length = _hash.length; contracts/Project.sol::245 => require(_length == _taskCosts.length, "Project::Lengths !match"); contracts/Project.sol::248 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::306 => // Revert if taskList array length not equal to scList array length. contracts/Project.sol::307 => uint256 _length = _taskList.length; contracts/Project.sol::308 => require(_length == _scList.length, "Project::Lengths !match"); contracts/Project.sol::311 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::321 => uint256 _length = _taskList.length; contracts/Project.sol::322 => for (uint256 i = 0; i < _length; i++) { contracts/Project.sol::367 => uint256 _length = taskCount; contracts/Project.sol::368 => for (uint256 _taskID = 1; _taskID <= _length; _taskID++) { contracts/Project.sol::592 => taskCount - j + _changeOrderedTask.length - i contracts/Project.sol::601 => if (_changeOrderedTask.length > 0) { contracts/Project.sol::602 => // Loop from lastAllocatedChangeOrderTask to _changeOrderedTask length (until _maxLoop) contracts/Project.sol::603 => for (; i < _changeOrderedTask.length; i++) { contracts/Project.sol::635 => if (i == _changeOrderedTask.length) { contracts/Project.sol::707 => uint256 _length = taskCount; contracts/Project.sol::710 => for (uint256 _taskID = 1; _taskID <= _length; _taskID++) { contracts/libraries/SignatureDecoder.sol::25 => if (messageSignatures.length % 65 != 0) { contracts/libraries/Tasks.sol::180 => uint256 _length = _alerts.length; contracts/libraries/Tasks.sol::181 => for (uint256 i = 0; i < _length; i++) _alerts[i] = _self.alerts[i];
contracts/Community.sol::261 => if (projectPublished[_project] > 0) { contracts/Community.sol::425 => // First claim interest if principal lent > 0 contracts/Community.sol::427 => _communities[_communityID].projectDetails[_project].lentAmount > 0 contracts/Community.sol::764 => require(_repayAmount > 0, "Community::!repay"); contracts/Community.sol::840 => if (_interestEarned > 0) { contracts/Disputes.sol::107 => _actionType > 0 && _actionType <= uint8(ActionType.TaskPay), contracts/HomeFi.sol::245 => return projectTokenId[_project] > 0; contracts/Project.sol::195 => require(_cost > 0, "Project::!value>0"); contracts/Project.sol::380 => if (_leftOutTokens > 0) { contracts/Project.sol::601 => if (_changeOrderedTask.length > 0) { contracts/Project.sol::691 => if (_loopCount > 0) emit TaskAllocated(_tasksAllocated);
contracts/Community.sol::686 => _communityProject.lastTimestamp) / 86400; // 24*60*60