Incident,
totalETH() value, resulting in the withdrawal of more ETH than deposited by the attacker.
Source: De.Fi incident database.
- Audits before it
- 2.
- Audits since
- 1, the latest dated 2 Jul 2021.
As of 3 Nov 2026, Rari Capital has 3 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 2 Jul 2021. Those reports list no open critical findings. It also holds 1 incident (latest 7 May 2021).
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Rari Capital (On-chain Governance) | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
| RariCapital | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
| RariCapital V2 | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 6 |
| Medium | 2 | 2 | 10 |
| Low | 9 | 33 | 20 |
The `getRawFundBalance()` function should return the total balance of all RFT holders’ funds and all unclaimed fees of all currencies, in USD. However, the computation on L503 assumes that all currencies are worth 1 USD. This has significant impact on the entire system, including accrued interest, fees, deposits and withdrawals.
Open the reportThe `getRawFundBalance()` function should return the total balance of all RFT holders’ funds and all unclaimed fees of all currencies, in USD. However, the computation on L503 assumes that all currencies are worth 1 USD. This has significant impact on the entire system, including accrued interest, fees, deposits and withdrawals.
Open the reportThere is a typo on L27 of `RariGovernanceToken.sol`, namely 8570000 should be 8750000 according to the comment on L23: "Initializer that reserves 8.75 million RGT for liquidity mining and 1.25 million RGT to the team.". This will conflict with L157 of `RariGovernanceTokenDistributor.sol`: `finalRgtDistribution = 8750000e18`.
Open the reportThere is a typo on L27 of `RariGovernanceToken.sol`, namely 8570000 should be 8750000 according to the comment on L23: "Initializer that reserves 8.75 million RGT for liquidity mining and 1.25 million RGT to the team.". This will conflict with L157 of `RariGovernanceTokenDistributor.sol`: `finalRgtDistribution = 8750000e18`.
Open the reportThe `AggregatorV3Interface private _ethUsdPriceFeed` state variable defined on L234 in `RariGovernanceTokenDistributor.sol` is never initialized (assigned a value). However, it is used in the `getEthUsdPrice` function. This means that the `getEthUsdPrice` will always return `0`, which will affect the Ethereum fund pool of Rari.
Open the reportThe `AggregatorV3Interface private _ethUsdPriceFeed` state variable defined on L234 in `RariGovernanceTokenDistributor.sol` is never initialized (assigned a value). However, it is used in the `getEthUsdPrice` function. This means that the `getEthUsdPrice` will always return `0`, which will affect the Ethereum fund pool of Rari.
Open the reportThe function `_price` queries various Chainlink oracles, however, the decimal value associated with each price feed is not considered. This may lead to over/undervaluing the price of the `underlying` token being considered.
Open the reportThe function `_price` consults several oracles without ensuring that the returned data is recent. This may lead to stale pricing results and potential arbitrage.
Open the reportThe issue is visible in the `rari-yield-pool-contracts` repo, in the `_withdrawFrom` function in `RariFundManager.sol`: 1. L666 computes: `uint256 contractBalance = token.balanceOf(_rariFundControllerContract);` 1. L668-683 iterate over all pools in order to withdraw the remaining balance and add it to `contractBalance` 1. L685 checks: `require(amount <= contractBalance, "Available balance not enough to cover amount even after withdrawing from pools.");` 1. L686 recomputes the same value as on L666 into another variable: `uint256 realContractBalance = token.balanceOf(_rariFundControllerContract);` 1. L709 checks if `realContractBalance < amount ? realContractBalance : amount` and transfers the resulting value. This clearly shows that the following condition is possible: `realContractBalance < amount <= contractBalance`, which would indicate that the amounts withdrawn from the pools in the `for`-loop on L668-683 is discarded.
Open the reportThe issue is visible in the `rari-yield-pool-contracts` repo, in the `_withdrawFrom` function in `RariFundManager.sol`: 1. L666 computes: `uint256 contractBalance = token.balanceOf(_rariFundControllerContract);` 1. L668-683 iterate over all pools in order to withdraw the remaining balance and add it to `contractBalance` 1. L685 checks: `require(amount <= contractBalance, "Available balance not enough to cover amount even after withdrawing from pools.");` 1. L686 recomputes the same value as on L666 into another variable: `uint256 realContractBalance = token.balanceOf(_rariFundControllerContract);` 1. L709 checks if `realContractBalance < amount ? realContractBalance : amount` and transfers the resulting value. This clearly shows that the following condition is possible: `realContractBalance < amount <= contractBalance`, which would indicate that the amounts withdrawn from the pools in the `for`-loop on L668-683 is discarded.
Open the reportThe array index of the left-hand side member of the assignment in the following code snippet located in `RariFundManager.sol` does not change for any loop iteration and it is out of bounds for the `acceptedCurrencies` array: ``` for (uint256 i = 0; i < _supportedCurrencies.length; i++) if (_acceptedCurrencies[_supportedCurrencies[i]]) acceptedCurrencies[acceptedCurrencies.length] = _supportedCurrencies[i]; ``` Therefore this loop will not fill in all the supported currencies as the function is expected to do and the return values will be incorrect.
Open the reportThe array index of the left-hand side member of the assignment in the following code snippet located in `RariFundManager.sol` does not change for any loop iteration and it is out of bounds for the `acceptedCurrencies` array: ``` for (uint256 i = 0; i < _supportedCurrencies.length; i++) if (_acceptedCurrencies[_supportedCurrencies[i]]) acceptedCurrencies[acceptedCurrencies.length] = _supportedCurrencies[i]; ``` Therefore this loop will not fill in all the supported currencies as the function is expected to do and the return values will be incorrect.
Open the reportMost functions will return a value indicating success or failure. It's important to ensure that every necessary function is checked. Otherwise, the caller just assumes that the function call was successful and continues execution. This is the case for the function call `cErc20.accrueInterest() ` on L49 in `CompoundPoolController.sol`, whose return value is not checked.
Open the reportMost functions will return a value indicating success or failure. It's important to ensure that every necessary function is checked. Otherwise, the caller just assumes that the function call was successful and continues execution. This is the case for the function call `cErc20.accrueInterest() ` on L49 in `CompoundPoolController.sol`, whose return value is not checked.
Open the reportIf a user upgrades, but is then sent old fund tokens (which seems possible since it's an ERC20), that user cannot upgrade the received tokens. Further, if token transfers from an already updated account occur, the conditional on L69 will never hold, because there will be old tokens in an account that cannot be upgraded (since it was already upgraded). Therefore, `finished` will never be set to true.
Open the reportIf a user upgrades, but is then sent old fund tokens (which seems possible since it's an ERC20), that user cannot upgrade the received tokens. Further, if token transfers from an already updated account occur, the conditional on L69 will never hold, because there will be old tokens in an account that cannot be upgraded (since it was already upgraded). Therefore, `finished` will never be set to true.
Open the reportThe function `getUnderlyingPrice` appears to be copied from `FixedEthPriceOracle.sol`, and therefore relates the token price to ETH, not EUR.
Open the reportThe following snippet was removed, however, it seems this check is necessary based on the function name. ``` // Make sure minter is whitelisted if (enforceWhitelist && !whitelist[minter]) { return uint(Error.SUPPLIER_NOT_WHITELISTED); } ```
Open the reportThe Checks-Effects-Interactions coding pattern is meant to mitigate any chance of other contracts manipulating the state of the blockchain in unexpected and possibly malicious ways before control is returned to the original contract. As the name implied, only after checking whether appropriate conditions are met and acting internally on those conditions should any external calls to, or interactions with, other contracts be done.
Open the reportThe Checks-Effects-Interactions coding pattern is meant to mitigate any chance of other contracts manipulating the state of the blockchain in unexpected and possibly malicious ways before control is returned to the original contract. As the name implied, only after checking whether appropriate conditions are met and acting internally on those conditions should any external calls to, or interactions with, other contracts be done.
Open the reportThere is a cast to `int256` on L515 in the `RariFundManager`, which would cause a large enough unsigned value to be converted to a negative value. However, this is highly unlikely to occur.
Open the reportThere is a cast to `int256` on L515 in the `RariFundManager`, which would cause a large enough unsigned value to be converted to a negative value. However, this is highly unlikely to occur.
Open the reportThe duration of the distribution period is set to `345600` blocks on L152 in `RariGovernanceTokenDistributor.sol`. Assuming that the average block duration over a 60 day period is 15 seconds results in 60 days. However, according to the latest [statistics on Etherscan](https://etherscan.io/chart/blocktime) we foresee an average block duration of 13 seconds, which would reduce the distribution period to 52 days. However, this is also an approximate estimate as the actual duration could be even lower.
Open the reportThe duration of the distribution period is set to `345600` blocks on L152 in `RariGovernanceTokenDistributor.sol`. Assuming that the average block duration over a 60 day period is 15 seconds results in 60 days. However, according to the latest [statistics on Etherscan](https://etherscan.io/chart/blocktime) we foresee an average block duration of 13 seconds, which would reduce the distribution period to 52 days. However, this is also an approximate estimate as the actual duration could be even lower.
Open the reportThe fallback function is meant to only be “called by 0x exchange to refund unspent protocol fee.” However, there are no restrictions/checks in place to guarantee this. This means that anyone could send funds to this contract by mistake.
Open the reportThe fallback function is meant to only be “called by 0x exchange to refund unspent protocol fee.” However, there are no restrictions/checks in place to guarantee this. This means that anyone could send funds to this contract by mistake.
Open the reportAs described in this [forum update] (https://forum.openzeppelin.com/t/problem-in-upgrades-plugins-for-users-of-unsafeallowcustomtypes/5066), `truffle-upgrades` supports the `unsafeAllowCustomTypes` flag. Due to an implementation error of this flag, older versions might not display some storage layout errors for enum and struct.
Open the reportThe comment on L23 says 20 million tokens will be minted, but on L27 only 10 million are minted.
Open the reportThe comment on L23 says 20 million tokens will be minted, but on L27 only 10 million are minted.
Open the report`distributionEndBlock` is a critical parameter used on the interface for governance distribution `IRariGovernanceTokenDistributor`. This method is only called in `RariFundManager` when the block number is below `distributionEndBlock`. No example implementation of `IRariGovernanceTokenDistributor` is presented, and `distributionEndBlock` must be used there to make this parameter effective.
Open the reportSmart contracts will often have `owner` variables to designate the person with special privileges to make modifications to the smart contract. In particular: 1. In `RariFundController.sol`, the owner can invoke `setEnzymeComptroller` at any point. 1. In `RariGovernanceTokenUniswapDistributor.sol`, if the owner changes the Uniswap pair address using `setRgtEthUniswapV2Pair`, users that have already deposited may not be able to withdraw.
Open the reportIn the function `withdraw()`, the amount of withdrawal credits is rounded up on L81. It seems that if all users would choose to redeem credits and some would get rounded-up, then the last user to withdraw would fail due to lack of credits.
Open the reportIn the function `withdraw()`, the amount of withdrawal credits is rounded up on L81. It seems that if all users would choose to redeem credits and some would get rounded-up, then the last user to withdraw would fail due to lack of credits.
Open the reportIn `initialize`, it is unclear why `addPool(5)`, which would correspond to Enzyme, is not invoked.
Open the reportEvery Solidity file specifies in the header a version number of the format `pragma solidity (^)0.5.*`. The caret (`^`) before the version number implies an unlocked pragma, meaning that the compiler will use the specified version _and above_, hence the term "unlocked."
Open the reportEvery Solidity file specifies in the header a version number of the format `pragma solidity (^)0.5.*`. The caret (`^`) before the version number implies an unlocked pragma, meaning that the compiler will use the specified version _and above_, hence the term "unlocked."
Open the reportAttached through a shared contract address. Our incident data ends in Oct 2026.
totalETH() value, resulting in the withdrawal of more ETH than deposited by the attacker.
Source: De.Fi incident database.
The reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="rari-capital"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something 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