CertiK reviewed Reform DAO (ReformDAO) on 3 Apr 2024: 3 findings, 0 critical; none marked fixed.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 2 | 0 | 0 |
| Medium | 0 | 0 | 0 |
| Low | 1 | 0 | 0 |
In the contract `RFRMBonding` the role of `owner` has authority over the functions shown below. - `setLimits()` : Set the limit for the bonding process. - `setDiscounts()` : Set the discount values. - `setWhitelist()`: Set whitelisting status for contracts to bypass the modifier `notContract`, which is related to the usage of `buyRFRM`. - `setBondStatus()`: Set the bounding status. - `setPriceInfo()` : Set the price value of the `rfrm`. - `setContractState()` : Enable/Disable the contract. - `setReceiver()` : Set the receiver address used during the buying process. - `transferToken()`: Transfer **all** the tokens of the current contract to the owner. - `transferOwnership()`: Set new owner for the contract. - `renounceOwnership()`: Set address(0) as the new owner. Any compromise to the `owner` account may allow a hacker to take advantage of this authority and change the price of the token or drain the balance of the current contract. It is worth noting when deploying the `RFRMBonding`, the contract will approve `type(uint256).max` allowance `rfrm` token to the staking contract address if there are any misconfiguration during the deployment will cause the `rfrm` token loss. Additionally, the owner can allow other contracts to interact with the `RFRMBonding` via the `setWhitelist` function, which could be inconsistent with the comment `Modifier to ensure transactions are not initiated by other contracts`. --- In the contract `RFRMOracle` the role of `owner` has authority over the functions shown below. - `setChainlink()` : Set the address of the price feed provider (Chainlink). - `transferOwnership()`: Set new owner for the contract. - `renounceOwnership()`: Set address(0) as the new owner. Any compromise to the `owner` account may allow a hacker to take advantage of this authority and change the value of the price by adding a malicious oracle address. --- In the contract `RFRMStaking` the role of `owner` has authority over the functions shown below. - `add()` : Add a new pool to the contract. There are two kinds of pools for ERC721 and ERC20, misconfiguration will cause unexpected errors. - `set()` : Update the given pool information. - `setPoolLock()` : Update the `claimFee` and the `lockPeriodInSeconds` of a given pool. **Note:** `claimFee` refers to the early withdrawal fee which can be large as the 100% based on the current design. - `setWallets()` : Set the `rewardWallet` and the `feeWallet`. - `setPercentagePerDay()` : Set the percentage value per day. - `setBulkAllocPoints()` : Set multiples pools `allocPoints`. - `setForcedUnlockState()` : Lock/Unlock the state of multiple pools. - `setVesting()` : Set the vesting contract. - `transferOwnership()`: Set new owner for the contract. - `renounceOwnership()`: Set address(0) as the new owner. Any compromise to the `owner` account may allow a hacker to take advantage of this authority and update incorrect information into an arbitrary pool or unlock the state of arbitrary pools. --- In the contract `RFRMNFT` the role of `owner` has authority over the function shown below. - `mint()`: Mint `RFRMNFT` to a given address. - `transferOwnership()`: Set new owner for the contract. - `renounceOwnership()`: Set address(0) as the new owner. Any compromise to the `owner` account may allow a hacker to take advantage of this authority and mint an unlimited amount of `RFRMNFT`. --- In the contract `RFRMVesting` the role of `DEFAULT_ADMIN_ROLE` has authority over the functions shown below. - `transferAdmin()`: Transfer the admin to a given address. - `addStakingContract()`: Set the role `STAKING_CONTRACT_ROLE` for the given address. - `removeStakingContract()`: Revoke the role of the stacking address. - `setUnlockDisableTime()`: Set the minimum date before the unlock. - `grantRole()`: Grant a role to an account, for example, `STAKING_CONTRACT_ROLE` and `DEFAULT_ADMIN_ROLE`. - `revokeRole()`: Revoke a role from an account. Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow a hacker to take advantage of this authority and abuse the contract functionalities. --- In the contract `RFRMVesting` the role of `STAKING_CONTRACT_ROLE` has authority over the functions shown below. - `addVesting()`: Add vesting position. - `mint()`: Mint `rfrm` token to one account. - `burn()`: Burn `rfrm` token from arbitrary account. Any compromise to the `STAKING_CONTRACT_ROLE` account may allow a hacker to take advantage of this authority and affect the token distribution for `rfrm`.
All of the `RFRM` tokens are sent to the contract deployer when deploying the contract. This could be a centralization risk as the deployer can distribute tokens without obtaining the community's consensus. Updated in commit [5112f38c9b91ceba45e138e5b33a5cf3698c44d9](https://github.com/RFRMDAO/DAO-Contracts/commit/5112f38c9b91ceba45e138e5b33a5cf3698c44d9) the total supply is updated with `1_000_000_000 * 10 ** decimals()`. ```solidity constructor() ERC20("Reform", "$RFRM") { // Mint 1 Billion tokens and assign them to the contract creator _mint(msg.sender, 1_000_000_000 * 10 ** decimals()); } ```
If chainlink oracle is not used for acquiring the RFRM price, a sliding window oracle will be applied to acquiring the price of RFRM. ```solidity function getPriceInETH(uint256 tokenAmount) public view returns (uint256 ethAmount) { if (!isUsingChainlink) { ethAmount = consult(address(token), tokenAmount, weth); } else { // Price of 1 RFRM including decimals (uint80 roundId, int256 price, , uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData(); if (roundId != answeredInRound) revert PriceNotUpdated(); if (updatedAt == 0) revert PriceNotUpdated(); if (price == 0) revert PriceNotUpdated(); ethAmount = (uint256(price) * tokenAmount) / 10 ** token.decimals(); } } ``` However, as there will be some lag of prices using sliding window oracle, it may create some arbitrage opportunities. Especially after January 28 2024, when users can unlock their bought RFRM instantly after purchase. For example, suppose sometime after January 28 2024, the current market price of RFRM is 100 USD while according to the sliding window Oracle, the price can be 80 USD. In this case, the arbitrager can buy RFRM in the Bonding contract, withdraw the bought RFRM from the staking contract, pay claim fees, and sell the tokens in the market for profit, which could lead to the price drop of the RFRM tokens.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (10)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us