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: 45/133
Findings: 3
Award: $114.27
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MiloTruck
Also found by: 0x52, 8olidity, Ruhum, __141345__, cccz, codexploder, cryptonue, hansfriese, sseefried
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/HomeFi.sol#L115 https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/HomeFi.sol#L194
The contract doesn't verify that the lenderFee
doesn't exceed 100%. If that is the case, it will trigger an underflow in the Community contract. That affects the availability of the protocol.
The admin controls the variable's value, but mistakes can happen. Adding the necessary safe guards in your contract is a better form of protection.
https://github.com/code-423n4/2022-08-rigor/blob/main/contracts/Community.sol#L397
If the fee is over 100%, the fee will be bigger than the lending amount causing an underflow. Currently, the fee logic uses the wrong formula but that's another issue. If the correct formula is used, this issue is valid.
none
Add a reasonable limit for the fee, e.g. 5%
#0 - horsefacts
2022-08-06T21:57:54Z
Duplicate of https://github.com/code-423n4/2022-08-rigor-findings/issues/400 (lender can set a greater than 100% fee that underflows) Maybe duplicate of https://github.com/code-423n4/2022-08-rigor-findings/issues/252 (lender can extract 100% fee)
🌟 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
42.5475 USDC - $42.55
tokenCurrency
variables inside Community contract are unusedHomeFiProxy.getLatestAddress()
doesn't follow the comment's specsConsider using a two-step process for transferring the ownership of a contract. While it costs a little more gas, it's safer than transferring directly.
Here's an example from the Compound Timelock contract: https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol#L45-L58
Inside its initialize()
function it should also initialize ERC2771ContextUpgradeable
and ReentrancyGuardUpgradeable
.
tokenCurrency
variables inside Community contract are unusedAll three tokenCurrency
variables are unused. This could point to a faulty implementation although I think that the call to HomeFi
is correct. I'd advise deleting them.
The compact signature representation (EIP-2098) is a more efficient way to handle signatures. Your protocol doesn't support them because it only allows signatures that have a length of 65
. The short version only has a length of 64
.
OpenZeppelin's library supports them: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol#L58-L87
An attacker can modify the signature slightly to get another valid signature for the content. I couldn't find any relevant attack vector for this issue. But, you might introduce one in one of your upgrades. I'd advise fixing the issue now.
You can find a solution for it in the OZ library: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol#L154-L165
A more detailed explanation: https://medium.com/immunefi/intro-to-cryptography-and-signatures-in-ethereum-2025b6a4a33d
After the initialization, it's not possible to modify the currencies the protocol uses. All the other options can be configured. Only the currencies are immutable. In our space, this might cause you some headaches. I think the design of having three hardcoded currencies is not the best choice here.
A mapping where you can a dynamic amount of valid currencies seems to be a better solution. There seems to be no clear reason why there should only be three currencies. This way you could also disable and reenable currencies on the fly.
Would also save some gas in your validCurrency()
function since you can check the mapping instead of each currency individually.
The HomeFiProxy
contract only allows the admin to add a new contract. But, it can't deactivate deprecated ones. This makes the concept of "active" contracts obsolete. Every contract that was created through the HomeFi contract is always active. So you can get all the active contracts by calling getLatestAddress()
using the values from allContractNames
.
HomeFiProxy.getLatestAddress()
doesn't follow the comment's specsThe function is supposed to return a specific contract's current implementation address. But, it just returns the contract's proxy address. That address is always the same. It doesn't change. To get the implementation contract, you have to call the proxy's function.
🌟 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
22.0296 USDC - $22.03
++1
when incrementing a variablenonZero()
check in HomeFiProxy++1
when incrementing a variable++1
is the cheapest way to increment.
./contracts/mock/HomeFiMock.sol:203: projectCount += 1; ./contracts/HomeFi.sol:297: projectCount += 1; ./contracts/Project.sol:179: hashChangeNonce += 1; ./contracts/Project.sol:250: _taskCount += 1; ./contracts/Project.sol:290: hashChangeNonce += 1; ./contracts/libraries/Tasks.sol:181: for (uint256 i = 0; i < _length; i++) _alerts[i] = _self.alerts[i]; ./contracts/Community.sol:627: for (uint256 i = 0; i < _communities[_communityID].memberCount; 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/Project.sol:603: for (; i < _changeOrderedTask.length; i++) { ./contracts/HomeFiProxy.sol:87: for (uint256 i = 0; i < _length; i++) { ./contracts/HomeFiProxy.sol:136: for (uint256 i = 0; i < _length; i++) {
Realistacally the iterator can't overflow because of the loop's condition. Because of that, you can generally use the unchecked
block to save some gas:
for (uint i; i < length;) { unchecked { ++i; } }
./contracts/libraries/Tasks.sol:181: for (uint256 i = 0; i < _length; i++) _alerts[i] = _self.alerts[i]; ./contracts/Community.sol:627: for (uint256 i = 0; i < _communities[_communityID].memberCount; 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/Project.sol:603: for (; i < _changeOrderedTask.length; i++) { ./contracts/HomeFiProxy.sol:87: for (uint256 i = 0; i < _length; i++) { ./contracts/HomeFiProxy.sol:136: for (uint256 i = 0; i < _length; i++) {
Instead of reading the same variable from storage multiple times, you should cache it in memory.
e.g.
nonZero()
check in HomeFiProxyYou don't have to call the nonZero
modifier here because the underlying OZ library already checks for that. It doesn't allow the implementation address to not be a contract.