- $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
builder
creates aproject
by calling thecreateProject()
function on HomeFi. It will trigger the deployment of a new project thanks to the project factory. -
Builder
invites ageneral contractor
. It requires signing data that includes thecontractor
address and theproject
address by both thecontractor
and 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
project
address. Bothbuilder
andcontractor
have 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). -
Builder
is 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
builder
andcommunity owner
have 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 owner
lends 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
project
address. Bothbuilder
andcontractor
have to sign the data. The signatures and data are used to calladdTasks(bytes _data, bytes _signature)
-
Contractor
assign tasks tosubcontractor
by 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 theproject
address.builder
,contractor
andsubcontractor
have 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.
-
Builder
repays 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 frombuilder
tocommunity owner
. -
If for instance an offchain repayment occurred,
community owner
can 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 betweenbuilder
andlender
will 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
virtual
modifier 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
override
modifier when overriding a V1function
ormodifier
- 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/
ContractName
V2Mock.sol and its tests inside ./test/utils/contractName
UpgradabilityTests.ts and simply run theUpgradability.ts
test. - 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 compile
to 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.ts
file, update the following variable accordingly to your deployment:homeFiProxyAddress
: address of homeFiProxyproxyBytes2Name
: name of the proxy to upgrade. For ex:PF
forProjectFactor
. Refer./contracts/HomeFiProxy.sol
for proxies and there name.newImplementationName
: address of the underlying implementation. For ex: address of newProjectFactory
contract.taskLibraryAddress
: only required when upgradingProjectFactory
proxy. Address ofTaskLibrary
that is linked toProject
contract.
- Run the
upgrade
script,yarn hardhat run scripts/upgrade.ts --network <your preferred network>
- The
upgrade
script will automatically run thetests
if using thehardhat
network.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