CertiK reviewed Cadabra (Cadabra Finance) on 22 Aug 2023: 20 findings, 0 critical; 14 marked fixed, 6 open.
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 | 3 | 0 | 0 |
| Medium | 0 | 0 | 2 |
| Low | 3 | 0 | 12 |
### Privileged Roles In BalancerUpgradeable - The role `DEFAULT_ADMIN_ROLE` has authority to give the following privileged roles: - `TIMELOCK_ADMIN_ROLE`; - `ACTIVATE_ADAPTER_ROLE`; - `DEACTIVATE_ADAPTER_ROLE`; - `REBALANCE_ROLE`; - `COMPOUND_ROLE`; - `RECOVER_FUNDS_ROLE`; Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and give themselves these privileged roles to do any of the privileged actions explained below. - The role `TIMELOCK_ADMIN_ROLE` has authority to give the following privileged roles: - `ADD_ADAPTER_ROLE`; - `REMOVE_ADAPTER_ROLE`; - `TAKE_PERFORMANCE_FEE_ROLE`; - `UPGRADE_ROLE`; Any compromise to the `TIMELOCK_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and give themselves these privileged roles to do any of the privileged actions explained below. - The role `ACTIVATE_ADAPTER_ROLE` has authority over the function `activateAdapter()`. Any compromise to the `ACTIVATE_ADAPTER_ROLE` account may allow the hacker to take advantage of this authority and activate any adapter that is added. If the hacker is able to also compromise the `ADD_ADAPTER_ROLE`, then they may activate a malicious adapter that the balancer can interact with that will steal any users funds that are sent to that adapter. - The role `ADD_ADAPTER_ROLE` has authority over the function `addAdapter()`. Any compromise to the `ADD_ADAPTER_ROLE` account may allow the hacker to take advantage of this authority and add a malicious adapter so that it may be activated. - The role `COMPOUND_ROLE` has authority over the function `compound()`. Any compromise to the `COMPOUND_ROLE` account may allow the hacker to take advantage of this authority and input malicious swaps data that can steal all claimed rewards and tokens held by the adapter for themselves. - The role `DEACTIVATE_ADAPTER_ROLE` has authority over the function `deactivateAdapter()`. Any compromise to the `DEACTIVATE_ADAPTER_ROLE` account may allow the hacker to take advantage of this authority and deactivate an adapter to enact a denial of service attack. - The role `REBALANCE_ROLE` has authority over the function `rebalance()`. Any compromise to the `REBALANCE_ROLE` account may allow the hacker to take advantage of this authority and steal the funds of all adapters. - The role `REMOVE_ADAPTER_ROLE` has authority over the function `removeAdapter()`. Any compromise to the `REMOVE_ADAPTER_ROLE` account may allow the hacker to take advantage of this authority and remove an adapter to enact a denial of service attack. - The role `TAKE_PERFORMANCE_FEE_ROLE` has authority over the function `takePerformanceFee()`. Any compromise to the `TAKE_PERFORMANCE_FEE_ROLE` account may allow the hacker to take advantage of this authority and lock up to 1 percent of the total value and mint shares for the currently locked fee value to the `$feeReceiver`. It should be noted that this can only be called 1 days after the last time it was called. - The role `RECOVER_FUNDS_ROLE` has authority over the function `recoverFunds()`. Any compromise to the `RECOVER_FUNDS_ROLE` account may allow the hacker to take advantage of this authority and take any tokens held by the adapter that is not returned by the `utilizedTokens()` function. - In the contract `BalancerUpgradeable` the role `UPGRADE_ROLE` has authority over the function `_authorizeUpgrade()`. Any compromise to the `UPGRADE_ROLE` account may allow the hacker to take advantage of this authority and upgrade to a new implementation. For more information see the finding titled **Centralized Control of Contract Upgrade**. ### Privileged Roles In BaseAdapter - The `BALANCER` role has authority over the following functions: - `invest()`; - `redeem()`; - `compound()`; - `recoverFunds()`; Any compromise to the `BALANCER` account may allow the hacker to take advantage of this authority and steal all funds held by the adapter. It should be noted that `BALANCER` is immutable, but will point to a proxy. Users should verify it is set to `BalancerUpgradeable` proxy with the correct implementation and that any upgrades to the implementation do not allow the abuse of these privileges.
In the contract `BalancerUpgradeable`, the role `UPGRADE_ROLE` has the authority to update the implementation contract. Any compromise to the `UPGRADE_ROLE` account may allow a hacker to take advantage of this authority and change the implementation contract which is pointed by proxy and therefore execute potential malicious functionality. In the worst-case scenario, this can be used to steal all funds held by the adapters.
In the function `takePerformanceFee()`, the input `feeValue` will eventually be locked after 7 days allowing shares to be minted equal to that value provided it does not exceed 1% of the total value. It appears this is designed to take a fee on the profits obtained from potential value accrued by LP tokens. However, this allows for users to exit the pool so they are not subjected to the fee before, which then redistributes the fee across the remaining users. This allows for users to receive less than their initial deposit back.
When performance fees are taken or an adapter is compounded, the profits and fees are locked. The profits are locked and unlocked over 7 days, while the fees are increased over 7 days. The locked amount at a given time are subtracted from the total value from all adapters and can cause potential issues if the locked amount is a significant portion of the total value as it can affect the rate for shares. For example if the total supply of shares is `1e36` and the net asset value is 1 (for example the total asset value could be `201e18`, but the locked profits are `200e18`), then the rate would be: $$\frac{1e36 + 1e18}{1+1e18} = 1e18$$ Allowing a user to receive a large rate. If there are a significant amount of locked profits, this allows the user to receive a large portion of them for a small investment. In addition, any amount of profit that is still locked when `_lockValue()` is called will be distributed over an additional 7 days. This allows for profits to take longer to distribute than intended if `compound()` is called frequently. For example if `compound()` is called and a profit of 70 USD is locked, then there should be at least 10 USD unlocked each day so that 70 USD is unlocked after the 7 days. However, if `compound()` is called daily (assume for simplicity rewards are reduced so each day only 1 USD is profited): - Day 1, 10 USD has been unlocked, 60 USD (leftover profit) + 1 USD (new profit) = 61 new locked amount; - Day 2, 61/7 = 8 USD is unlocked, 53 USD (leftover profit) + 1 USD (new profit) = 54 USD new locked amount; - Day 3, 54/7 = 7 USD is unlocked, 47 USD (leftover profit) + 1 USD (new profit) = 48 USD new locked amount; Similarly this happens when `takePerformanceFee()` is called as it calls `_lockValue(0, feeValue)`. This demonstrates how profits can be distributed over longer period than 7 days if `compound()` or `takePerformanceFee()` are called within the 7 day unlocking period.
In general, `for` loops can have the increment placed in the body of the loop inside an unchecked block to save gas as they are bounded.
In the following logic: ```solidity SafeCast.toInt56(SafeCast.toInt256(uint256(twapInterval))) ``` `twapInterval` is a `uint32`, thus its maximum value is `2^32 - 1` so it cannot exceed `2^255 - 1` which is the maximum `int256` value. Thus it can be cast as an `int256` without the use of `SafeCast` to save gas.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (9)
1,044 projects reviewed · 1,393 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