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: 2/133
Findings: 11
Award: $5,180.34
🌟 Selected for report: 4
🚀 Solo Findings: 1
🌟 Selected for report: scaraven
Also found by: 0x52, Deivitto, Lambda, TrungOre, auditor0517, hansfriese, rbserver, simon135, smiling_heretic, sseefried
165.6336 USDC - $165.63
Currently, it calculates interest using the number of days and builders would pay nearly half or less interest than they should.
So lenders wouldn't get the interest as expected and it means builders can steal the interest from lenders.
As we can see here, interest is calculated using _noOfDays
so below 2 scenarios would be possible.
lendingNeeded
every day using toggleLendingNeeded().lastTimestamp
will be updated properly here.Solidity Visual Developer of VSCode
I think we should use _noOfSeconds
here instead of _noOfDays
.
uint256 _noOfSeconds = block.timestamp - _communityProject.lastTimestamp; uint256 _unclaimedInterest = _lentAmount * _communities[_communityID].projectDetails[_project].apr * _noOfSeconds / 365 / 86400 / 1000;
#0 - horsefacts
2022-08-06T20:40:12Z
changeOrder() can be used to change subcontractor.
But if a project builder approves the signature by fault or the contractor is delegated, a malicious contractor and subcontractor might reinitialize an already completed task and complete again to receive the task cost twice.
Currently changeOrder()
doesn't check whether the task is completed or not so the below scenario would be possible.
Inactive
here.Solidity Visual Developer of VSCode
Once the task has been completed, we should prevent changing any properties of the task because the cost was paid already and it's irreversible.
We should add a below require()
here.
require(tasks[_taskID].state != TaskStatus.Complete, "Completed already");
#0 - 0xA5DF
2022-08-11T11:18:00Z
Duplicate of #95
🌟 Selected for report: hansfriese
1567.6074 USDC - $1,567.61
The changeOrder()
function will revert when it's called for the tasks that don't have subcontractors.
As a result, the project builder and contractor can't change the cost of a task until the subcontractor is confirmed.
The changeOrder()
is used to change the cost or subcontractor of a task and there is no documentation that this function must be called only after a subcontractor is confirmed.
Also, it's reasonable to be able to change the cost when a subcontractor isn't confirmed yet.
But when it checks signature here, it assumes the task has a confirmed subcontractor already and checkSignatureTask()
will revert in other cases.
So this function will be useless for non SCConfirmed tasks.
Solidity Visual Developer of VSCode
We should check separately in case the subcontractor is confirmed or not here.
if (getAlerts(_taskID)[2]) { // If subcontractor has confirmed. checkSignatureTask(_data, _signature, _taskID); } else { // If subcontractor not has confirmed. checkSignature(_data, _signature); }
#0 - 0xA5DF
2022-08-11T14:27:32Z
I think this is invalid, since currently checkSignatureTask
will pass if SC is the zero address and the signature isn't a valid signature (i.e. builder and GC can just pass zero as the signature).
This will only be valid if the sponsor fix other bugs by making checkSignatureValidity()
revert on invalid signature.
#1 - jack-the-pug
2022-08-27T09:37:20Z
Will downgrade to Medium given this is a feature being malfunctioning.
🌟 Selected for report: MiloTruck
Also found by: 0x52, 8olidity, Ruhum, __141345__, cccz, codexploder, cryptonue, hansfriese, sseefried
49.6901 USDC - $49.69
https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L115 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/HomeFi.sol#L185
The owner may set the lenderFee
as high as he wants and claim almost 100% of the funds.
From this comment, lenderFee can be in range [1, 1000].
But there is no requirement when admin sets lenderFee
here and here.
So the admin can even set lenderFee = 1000000
, then he will take around 1000000/(1000000 + 1000) = 99.9%
as a fee.
Solidity Visual Developer of VSCode
It's recommended to set some reasonable upper bounds for the lenderFee like 5%.
We can add below requirements here and here.
require(_lenderFee >= 1, "zero lenderFee"); require(_lenderFee <= 50, "more than 5%");
#0 - zgorizzo69
2022-08-08T17:41:04Z
we could indeed have a max lenderFee
#1 - jack-the-pug
2022-08-27T12:18:48Z
Duplicate of #400
🌟 Selected for report: Lambda
Also found by: hansfriese
705.4233 USDC - $705.42
https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L891 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L842-L862
According to the Glossary, "Builder can act as a contractor." and "Contractor can act as a subcontractor.".
But Project.checkSignatureTask()
might revert when a builder or contractor is the same as a subcontractor.
According to the Glossary, it's possible that the builder or contractor is the same as the subcontractor.
Logically, it's logical also because the builder or contractor would confirm some tasks as a subcontractor and complete by themselves because other subcontractors don't like to manage them as the tasks are too difficult or costs are low.
It would be important for builders to complete all tasks and recover remaining tokens.
Then in checkSignatureTask(), it's possible builder == _sc
or contractor == _sc
.
And in checkSignatureValidity(), approvedHashes
plays an important role to check validity.
Btw the approvedHashes
mapping is deleted after the validation and it won't work properly when builder == _sc
or contractor == _sc
.
So these validations might be failed in this case.
I don't think the builder won't set the same contractor so I don't mention this part.
Solidity Visual Developer of VSCode
I think we don't need to check a signature for _sc
when it's the same as builder or contractor.
We can modify checkSignatureTask() like below.
function checkSignatureTask( bytes calldata _data, bytes calldata _signature, uint256 _taskID ) internal { // Calculate hash from bytes bytes32 _hash = keccak256(_data); // Local instance of subcontractor. To save gas. address _sc = tasks[_taskID].subcontractor; // When there is no contractor if (contractor == address(0)) { // Just check for B and SC sign checkSignatureValidity(builder, _hash, _signature, 0); if(_sc != builder) { checkSignatureValidity(_sc, _hash, _signature, 1); } } // When there is a contractor else { // When builder has delegated his rights to contractor if (contractorDelegated) { // Check for GC and SC sign checkSignatureValidity(contractor, _hash, _signature, 0); if(_sc != contractor) { checkSignatureValidity(_sc, _hash, _signature, 1); } } // When builder has not delegated rights to contractor else { // Check for B, SC and GC signatures checkSignatureValidity(builder, _hash, _signature, 0); checkSignatureValidity(contractor, _hash, _signature, 1); if(_sc != builder && _sc != contractor) { checkSignatureValidity(_sc, _hash, _signature, 2); } } } }
#0 - parv3213
2022-08-24T13:22:45Z
@code423n4 this a duplicate of #https://github.com/code-423n4/2022-08-rigor-findings/issues/86
🌟 Selected for report: hansfriese
285.6964 USDC - $285.70
https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L455 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L484 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Community.sol#L509
Builders can't repay when the system is paused so they must pay more interest for the paused period.
Builders can repay to lenders using 3 functions, repayLender(), reduceDebt(), and escrow().
But they all don't work when the system is paused and builders have no way to avoid it.
Furthermore, the HomeFi admin is the main lender of builders and there is no assurance that the admin would pause the community for a while to get more interest.
Solidity Visual Developer of VSCode
Recommend thinking of an approach to make 3 repay functions work for paused or modify the interest calculation formula not to add interest for the paused period.
#0 - parv3213
2022-08-07T06:11:51Z
The pause period is to fix severe bugs, and we don't want extra logic to handle extra interest. Hopefully, during that downtime, no builders will need to make repayment right away. Also, moving forward, HomeFi admin will be a decentralized DAO.
According to the document, "Note that you cannot submit a project with no total budget. Therefore it requires at least one task with a budget > 0.".
But publishProject()
doesn't have such a requirement.
If a builder publishes his project without any tasks, he can't increase lendingNeeded
because of this condition and projectCost() = 0
.
So he can't request anything even after he paid the publishFee.
Also, such a project is needless for the community owner as he can't lend funds and it will just increase the memory to save the useless project.
Solidity Visual Developer of VSCode
Recommend adding an additional require here like the document.
require(_projectInstance.projectCost() > 0, "cost = 0");
#0 - parv3213
2022-08-11T15:12:01Z
Duplicate of #348
🌟 Selected for report: hansfriese
Also found by: cccz
705.4233 USDC - $705.42
setComplete()
function might be called successfully using the past signature when it shouldn't work.
As a result, a task might be completed when a builder doesn't want it.
approveHash() function can set only true so there is no method to cancel already approved hash without passing validation here.
So the below scenario would be possible.
setComplete()
might revert.setComplete()
might revert when the funds haven't been allocated and a builder signed by fault.setComplete()
using the previous signature and complete the task without additional work.I don't know if there would be similar problems with other functions that use signature and I think it would reduce the risk a little if we add an option to cancel the approved hash.
Solidity Visual Developer of VSCode
Recommend modifying approveHash() like below.
function approveHash(bytes32 _hash, bool _bool) external override { //++++++++++++++++++++ address _sender = _msgSender(); // Allowing anyone to sign, as its hard to add restrictions here. // Store _hash as signed for sender. approvedHashes[_sender][_hash] = _bool; //+++++++++++++++++++ emit ApproveHash(_hash, _sender, _bool); //++++++++++++++++++++++ }
I am not so sure that a similar scenario would be possible in the Community contract also and recommend to change both functions together.
🌟 Selected for report: hansfriese
Also found by: Lambda
705.4233 USDC - $705.42
addTasks()
function checks this require() to make sure _taskCount
is correct.
But it might revert when this function is called after a dispute because it takes a certain time to resolve disputes and other tasks might be added meanwhile.
The below scenario would be possible.
_taskCount = 10
and taskCount
will be 11 after addition here._taskCount = 10
, but it will revert here.So currently, the project builder and contractor shouldn't add new tasks to make their previous dispute valid.
I think it's reasonable to modify that they can add other tasks even though there is an active dispute.
Solidity Visual Developer of VSCode
I think we can modify not to compare taskCount when it's called from disputes contract.
So we can modify this part like below.
if (_msgSender() != disputes) { require(_taskCount == taskCount, "Project::!taskCount"); } else { _taskCount = taskCount; }
🌟 Selected for report: hyh
Also found by: hansfriese
705.4233 USDC - $705.42
Tasks.unApprove() doesn't reset the past subcontractor after unapprove so some unexpected result might happen.
unApprove()
function is used in Project.changeOrder() and such scenario is possible.
changeOrder()
with higher cost and same SC.Originally, it's not logical to remain a subcontractor after unapprove because the old subcontractor can be confirmed again if he wants.
Solidity Visual Developer of VSCode
Recommend removing subcontractor in unApprove().
function unApprove(Task storage _self) internal { // State/ lifecycle // _self.alerts[uint256(Lifecycle.SCConfirmed)] = false; _self.state = TaskStatus.Inactive; _self.subcontractor = address(0); //+++++++++++++++++++++++++++++++++++ }
#0 - parv3213
2022-08-24T13:30:19Z
🌟 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.8799 USDC - $40.88
https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L903 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L253 https://github.com/code-423n4/2022-08-rigor/blob/5ab7ea84a1516cb726421ef690af5bc41029f88f/contracts/Project.sol#L417
The task of zero cost is useless and there would be no subcontractors to accept such task as no payment after finish.
So if such task is added by mistake, it would require more time and effort to finish because builder must complete it by himself to recover tokens.
It will work properly when _amount = 0.
function checkPrecision(uint256 _amount) internal pure { // Divide and multiply amount with 1000 should be equal to amount. // This ensures the amount is not too precise. require( ((_amount / 1000) * 1000) == _amount, "Project::Precision>=1000" ); }
Solidity Visual Developer of VSCode
Recommend changing like below.
function checkPrecision(uint256 _amount) internal pure { require(_amount > 0, "zero amount"); // Divide and multiply amount with 1000 should be equal to amount. // This ensures the amount is not too precise. require( ((_amount / 1000) * 1000) == _amount, "Project::Precision>=1000" ); }