- $47,500 USDC main award pot
- $2,500 USDC gas optimization award pot
- Join C4 Discord to register
- Submit findings using the C4 form
- Read our guidelines for more details
- Starts August 01, 2022 20:00 UTC
- Ends August 06, 2022 20:00 UTC
| Glossary | |
|---|---|
| Builder | One who contracts for and supervises the construction of real estate. Builders are the one to create projects |
| Project | Entity defined by a sum of tasks, a budget. It represents a real estate construction and is linked to a builder, contractor and subcontractor. |
| Contractor | One that agrees to furnish materials or perform services at a specified price, especially for construction work. He is invited by a builder on a project. He can assign a subcontractor on project tasks. Builder can act as a contractor. |
| Subcontractor | One who agrees to perform the work specified in a project task. He is assigned to the task by the project's contractor. Contractor can act as a subcontractor. |
| Community | Entity with an owner and members aiming at providing loans to projects. Members can publish their projects to the community. A project can only be published to one community at a time. |
| Community owner | only member able to provide loans to published projects |
| Task | Entity defined by a cost, a subcontractor, and a status. a task can be Inactive, Active or Complete. Tasks can be flagged as Allocated when budget for this task has bee provisioned. When subcontractor confirms the assignment on a task it is being flagged as SCConfirmed |
| Debt token | ERC20 token mint and burn capable but with disabled transfer. Only the community contract is able to mint and burn these tokens. They represent the debt owned by the builder to the community owner when a loan has been supplied to the builder for a project |
| HomeFi | acronym for Home Finance it is the module where all the protocol modules are linked. Addresses of allowed currencies, project factory, community, treasury and dispute contracts are registered there. |
| Token Currency | Crypto currencies used for payment by the protocol. For native currencies like ETH or XDAI we use the wrapped version only. |
The focus is to try and find any logic errors or ways to drain funds from the protocol in a way that is advantageous for an attacker at the expense of users with funds invested in the protocol.
Here are some areas that are of interest :
- signatures order/concatenation included in sensitive calls.
- meta transaction
- smart contract updates and clone factory
- debt issuance and interest calculation.
- malicious parties teaming up against other parties. i.e can a contractor and a subcontractor freeze or drain funds without the builder agreement ?
- funds sitting on project getting stuck.
HomeFi protocol is a generalized protocol that provides public, permission less, decentralized financial infrastructure for home finance. Our mission is to make home finance open, accessible and positive-sum for everyone on earth.
The Protocol is divided into modules with different areas of concerns.
All modules are behind proxies. HomeFi Proxy is responsible for initializing all the modules contract in the correct sequential order and generate upgradable proxy for them.
To improve the user experience of construction professionals using the protocol, we implemented eip-2771 meta transactions thanks to OpenZeppelin base contracts.
Here are some sequence diagrams for the main rigor flows. Home builder will create a project and add tasks to the project. A per task budget is defined. The builder can publish the project to communities he is a member of. Community provide fix APR loans to builders . Thanks to the loan tasks will be funded and each time a subcontractor completes them he will receive the assigned budget. Finally after selling the real estate the builder is able to repay the loan with interest. Of course depending on the step different signatures will be required to execute the transaction onchain. The repayment can be mark as done off chain through an escrow and directly between the community owner (aka lender) and the builder.
-
First a
buildercreates aprojectby calling thecreateProject()function on HomeFi. It will trigger the deployment of a new project thanks to the project factory. -
Builderinvites ageneral contractor. It requires signing data that includes thecontractoraddress and theprojectaddress by both thecontractorand thebuilder. The signatures and data are used to callinviteContractor(bytes _data, bytes _signature) -
Builder add tasks to the project. It requires signing data that includes tasks costs, a hash (task metadata), tasks count and the
projectaddress. Bothbuilderandcontractorhave to sign the data. The signatures and data are used to calladdTasks(bytes _data, bytes _signature) -
Community Owner creates a community by calling
createCommunity(bytes _hash, address _currency)on the community contract where all the communities are registered. It requires the address of the currency used by the community for lending. The currency must be register in HomeFi to be valid. It also requires a hash (community metadata). -
Builderis invited to be a member of that new community by thecommunity owner. They both have to sign data including the community ID, the new member address and a message hash (the message can be anything). The data and the signatures in the right order is required to calladdMember(bytes _data, bytes _signatures)on the community contract. It will add the builder as a community member allowing its projects to be published in the community. -
Builder publishes his project to the community. It requires signing data that includes community ID, APR, publishing fee and nonce . Both
builderandcommunity ownerhave to sign the data. The signatures and data are used to callpublishProject(bytes _data, bytes _signature). Note that you cannot submit a project with no total budget. Therefore it requires at least one task with a budget > 0. -
Optional the builder can adjust the amount of the loan requested to a community by calling
toggleLendingNeeded(uint256 _communityID, address _project, uint256 _lendingNeeded) -
Community ownerlends fund to the published project by callinglendToProject(uint256 _cost). This call will update accrued interest, mint debt token for the community owner and transfer tokens to the project contract address.
-
Builder add tasks to the project. It requires signing data that includes tasks costs, a hash (task metadata), tasks count and the
projectaddress. Bothbuilderandcontractorhave to sign the data. The signatures and data are used to calladdTasks(bytes _data, bytes _signature) -
Contractorassign tasks tosubcontractorby callinginviteSC(uint256[] _index, address[] _to)providing tasks ID and subcontractor address. Subcontractor accepts by callingacceptInviteSC(uint256[] _taskList). -
Tasks need to be funded to be marked as completed. Although it can be funded through the community, the builder can also fund its project by calling directly
lendToProject(uint256 _cost)on the project contract address. -
Task is completed by calling
setComplete(bytes _data, bytes _signature). It requires signing data that includes task ID and theprojectaddress.builder,contractorandsubcontractorhave to sign the data. If there is no ongoing dispute about that project, task status is updated and payment is made. Indeed tokens are transferred from the project to the subcontractor's address.
-
Builderrepays the loan by callingrepayLender(uint256 _communityID, address _project, int256 _repayAmount)on community. It will calculate the owned interest and update the remaining debt. This will trigger the burnt of lender's debt and will transfer tokens frombuildertocommunity owner. -
If for instance an offchain repayment occurred,
community ownercan triggerreduceDebt(uint256 _communityID, address _project, uint256 _repayAmount, bytes _details). It will also calculate the owned interest update the remaining debt and burncommunity owner's debt token. Note that no token transfer betweenbuilderandlenderwill happen in that case.
- Clone this repository
git clone https://github.com/code-423n4/2022-08-rigor.git
cd 2022-08-rigor- Install dependencies with yarn
yarn- Create .env file (you can copy ".sample.env")
cp .sample.env .env- to compile run
yarn compile
- to run tests
yarn test
- to test coverage
yarn coverage
All the contracts in this section are to be reviewed. A further breakdown of contracts and their dependencies can be found here
| File | nSLOC | SLOC | Lines |
|---|---|---|---|
| Contracts (7) | |||
| contracts/DebtToken.sol | 35 | 55 | 106 |
| contracts/ProjectFactory.sol | 37 | 56 | 106 |
| contracts/HomeFiProxy.sol 🌀 | 70 | 93 | 231 |
| contracts/Disputes.sol 🧮 | 112 | 144 | 273 |
| contracts/HomeFi.sol | 117 | 197 | 323 |
| contracts/Project.sol 🧮 | 406 | 474 | 911 |
| contracts/Community.sol 🧮 | 422 | 569 | 919 |
| Libraries (2) | |||
| contracts/libraries/SignatureDecoder.sol 🖥🧮🔖 | 34 | 50 | 86 |
| contracts/libraries/Tasks.sol | 68 | 86 | 198 |
| Total (over 9 files): | 1301 | 1724 | 3153 |
| File | nSLOC | SLOC | Lines |
|---|---|---|---|
| Interfaces (6) | |||
| contracts/interfaces/IDebtToken.sol | 8 | 13 | 50 |
| contracts/interfaces/IProjectFactory.sol | 9 | 14 | 58 |
| contracts/interfaces/IHomeFi.sol | 41 | 64 | 206 |
| contracts/interfaces/IDisputes.sol | 45 | 68 | 160 |
| contracts/interfaces/IProject.sol | 57 | 87 | 331 |
| contracts/interfaces/ICommunity.sol | 114 | 175 | 440 |
| Total (over 6 files): | 274 | 421 | 1245 |
- Upgradability proxy as documented by OpenZeppelin
The main entry point for the HomeFi Smart Contract ecosystem. Administrative actions are executed through this contract; new project contracts are created from this contract with accompanying ERC721 for each project.
- ERC2771 compatible with ERC2771Context from OpenZeppelin.
- It uses Reentrancy Guard from OpenZeppelin.
Child contract deployed from HomeFi.sol, Project.sol contains the primary logic around construction project management. Onboarding contractors, fund escrow, and completion tracking are all managed here. Significant multi-signature and meta-transaction functionality is included here.
- It uses Reentrancy Guard from OpenZeppelin.
- It uses SafeERC20Upgradeable from OpenZeppelin for all debt token transfers.
- As we are heavily using signatures as params for sensitive operations. We created a Signature decoder library that is able to recover multiple signatures compacted in a bytes format as well as recover the address who signed the message.
- Tasks are a big part of a project. We created a task library for all task related operations.
- ERC2771 compatible with ERC2771Context from OpenZeppelin.
Technically separate from HomeFi.sol but can only be accessed by HomeFi.sol.
- Uses clones to achieve the minimal use of gas when deploying new Project contracts.
- ERC2771 compatible with ERC2771Context from OpenZeppelin.
Contains all project publication and lender funding logic. Lenders fund project contracts through Community.sol, and Builders repay lenders through Community.sol as well.
- It uses Reentrancy Guard from OpenZeppelin.
- It inherits OpenZeppelin Pausable base class.
- It uses SafeERC20Upgradeable from OpenZeppelin for all debt token transfers.
- It uses our Signature decoder library.
- ERC2771 compatible with ERC2771Context from OpenZeppelin.
In the event that a contractor (general or sub) does not get their funds and should have received them, or if there is negligence or malfeasance in the relationship between a builder and lender, participants permissioned in the project have the ability to raise a dispute that HomeFi's admins (in our case Rigor) are able to arbitrate to make sure funds arrive in the correct user's wallet.
- It uses Reentrancy Guard from OpenZeppelin.
- It uses our Signature decoder library.
- ERC2771 compatible with ERC2771Context from OpenZeppelin.
Used to wrap Ether, USDC, or Dai and collateralize a given project. hTokens are given to lenders in the Community.sol contract as a receipt to track their lending into the project. On an builder's repayment of a project, hTokens are instantly destroyed, and the underlying collateral is returned + interest for the loan duration.
- It inherits OpenZeppelin ERC20 base class. Note that transfer are disabled and mint and burn are only available to the community contract.
Internal library used in Project. Contains functions specific to a task actions and lifecycle.
Decodes signatures that are encoded as bytes.
- @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
- @openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol
- @openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol
- @openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol
- @openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol
- @openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol
- @openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol
- @openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol
- @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol
- @openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol
- @openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol
Interest on a loan are calculated on the principal only and doesn't include interest on the accrued interest.
When a repayment occurs we first repay the interest and if their is money left the principal is repaid. Afterwards the interest will be calculated on the remaining principal.
Here is some examples on a spreadsheet.
REPORT_GAS=true yarn test
- Run the following command to deploy smart contracts (for local)
yarn deploy-local
- Run the following command to deploy smart contracts (for rinkeby)
yarn deploy-rinkeby
HomeFiProxy contract stores the proxies for HomeFi, Community, Disputes,
ProjectFactory, and all the three DebtTokens. These proxies' implementation can be upgraded individually. Only the admin can upgrade implementations.
All proxies are stored in an array inside HomeFiProxy with a bytes2 name associated to them.
| Contract Proxy | Bytes2 Name |
|---|---|
| HomeFi | HF |
| Community | CN |
| Disputes | DP |
| ProjectFactory | PF |
| Native Currency Debt Token | DA |
| Token Currency 1 Debt Token | US |
| Token Currency 2 Debt Token | NT |
This is valid for all HomeFi proxies- HomeFi, Community, Disputes, ProjectFactory, and DeptTokens.
- Add
virtualmodifier to all the functions of old implementation(V1) that are needed to be upgraded. - Make the new implementation(V2) inherit V1.
- Rules:
- Add new variable and functions normally
- Use
overridemodifier when overriding a V1functionormodifier - Cannot override or modify an
event
- Test the V2 contracts plus regression the V2 implementation with V1 tests. To make this process easier, paste V2 implementation in ./contract/mocks/
ContractNameV2Mock.sol and its tests inside ./test/utils/contractNameUpgradabilityTests.ts and simply run theUpgradability.tstest. - Before upgrading on production(xDai), deploy upgrades development testnets(rinkeby) and test.
- Deploy upgrades on production(xDai) and test.
- Save the new proxy implementation according to the step above.
- Run
yarn compileto compile all contracts, including the new implementation. - Deploy the new implementation, possibly by creating a new script, and save its address.
- In the
./scripts/upgrade.tsfile, update the following variable accordingly to your deployment:homeFiProxyAddress: address of homeFiProxyproxyBytes2Name: name of the proxy to upgrade. For ex:PFforProjectFactor. Refer./contracts/HomeFiProxy.solfor proxies and there name.newImplementationName: address of the underlying implementation. For ex: address of newProjectFactorycontract.taskLibraryAddress: only required when upgradingProjectFactoryproxy. Address ofTaskLibrarythat is linked toProjectcontract.
- Run the
upgradescript,yarn hardhat run scripts/upgrade.ts --network <your preferred network> - The
upgradescript will automatically run thetestsif using thehardhatnetwork.Look at various tested V2 mocks inside
./contract/mocks
ProjectFactory proxy upgrade is mostly required to upgrade the underlying Project contract implementation. To make this upgrade, the new implementation of ProjectFactory must add a function to change the underlying address with new Project implementation. Potentially also updating the interface for Project contract. Check ./contracts/mock/ProjectFactoryV2Mock.sol and contracts/mock/ProjectV2Mock.sol for reference.
Latest contract addresses can be found under "deployments/<network>.json"
- Do you have a link to the repo that the contest will cover?
https://github.com/RigorHQ/Rigor-ProtocolV2
- How many (non-library) contracts are in the scope?
7
- Total sLoC in these contracts?
2105
- How many library dependencies?
2
- How many separate interfaces and struct definitions are there for the contracts within scope?
7 interfaces 4 structs
- Does most of your code generally use composition or inheritance?
We are mostly using Inheritance for storing the basic schema of our contracts. Storing external functions params, returns, events, structs, and enums, without any implementation.
- How many external calls?
Two. Calling transfer() and transferFrom() on supported tokens. As of now, we are supporting USDC, WXDAI, and WETH.
- Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol?
false
- Does it use an oracle?
false
- Does the token conform to the ERC20 standard?
We have a debt token that is a modified ERC20
- Are there any novel or unique curve logic or mathematical models?
No
- Does it use a timelock function?
No
- Is it an NFT?
We use NFTs
- Does it have an AMM?
No
- Is it a fork of a popular project?
false
- Does it use rollups?
false
- Is it multi-chain?
false
- Does it use a side-chain?
false



