Audit · firm’s term: audit
Quantstamp reviewed Secured Finance (Secured Finance - Diff Audit) on 29 Mar 2024: 14 findings, 0 critical; 9 marked fixed, 5 acknowledged.
Report hosted on certificate.quantstamp.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 | 0 | 0 | 0 |
| Medium | 0 | 0 | 2 |
| Low | 0 | 5 | 7 |
The function `LendingMarketOperationLogic.createZCToken()` is used to deploy two types of ZC Tokens: perpetual and non-perpetual. * Non-perpetual ZC Tokens for an underlying token `T` should have a number of decimals that match the amount of decimal of `T`. * Perpetual ZC Tokens for this same underlying token `T` should have a number of decimals that matches the number of decimals of the genesis value of `T`, which should have 18 additional decimals. However, all ZCTokens are deployed with 18 additional decimals to match the genesis value amount. As a result, users withdrawing non-perpetual ZC Tokens may think that they received 1e18 times less than expected. **Note:** This issue was raised by the client during the audit process.
Users have the capability to withdraw a specified `_amount` of ZC tokens using the `_withdrawZCToken()` and `_withdrawZCPerpetualToken()` functions. However, the presence of the `isAll` flag, as determined by the `_getWithdrawableZCTokenAmount()` function, indicates that users can withdraw their entire position for a specific maturity. This functionality effectively eliminates the option for partial withdrawals. Specifically, based on the `isAll` value, either `0` or the `_amount` specified will be forwarded to the `lock()` function. The `lock()` function interprets an amount of `0` as an instruction to lock the user's total balance or the specified amount. Consequently, if `isAll` is set to `true` for a user intending to withdraw only `8` out of `10` tokens, all `10` tokens will be locked, not just the `8` requested. This could inadvertently impact the user's risk management strategies, as it differs from their initial withdrawal intent. **Note:** This issue was raised by the client during the audit process.
Order book IDs are stored in `uint8` variables that hold a maximum value of 255. Each time a new order book is created, the previous ID will be incremented by 1. Given the expected rate of a new order book every 3 months, it will take approximately 63 years before new order books cannot be created due to overflow, effectively freezing the protocol. **Note:** This issue was raised by the client during the audit process.
Some `ZCTokens` are expected to have as many as 36 decimals. While this does not create any issues within Secured Finance, third-party applications, such as wallets, can have issues supporting tokens with too many decimals. **Note:** This issue was raised by the client during the audit process.
"Dead" code refers to code that is never executed and hence makes no impact on the final result of running a program. Dead code raises a concern, since either the code is unnecessary or the necessary code's results were ignored. `_calculatePVFromFV()` is an overloaded function. One version takes two `uint256` as input and the other takes one `uint256` and one `int256`. The version that takes two `uint256` is not called.
The event `LiquidatorFeeRateUpdated` is defined as follows: ``` event LiquidatorFeeRateUpdated(uint256 previousRate, uint256 ratio); ``` However, when the value of `Storage.slot().liquidatorFeeRate` is updated in the function `_updateLiquidationConfiguration()`, the event will use for both fields the new value, instead of using the old and the new value: ``` if (_liquidatorFeeRate != Storage.slot().liquidatorFeeRate) { Storage.slot().liquidatorFeeRate = _liquidatorFeeRate; emit LiquidatorFeeRateUpdated(Storage.slot().liquidatorFeeRate, _liquidatorFeeRate); } ``` This can negatively impact external observers of the protocol.
It is important to validate inputs, even if they only come from trusted addresses, to avoid human error: In `MixinLiquidationConfiguration.sol`: 1. In the function `_updateLiquidationConfiguration()`, there is no check making sure that `fullLiquidationThresholdRate < liquidationThresholdRate`. In `CurrencyController.sol`: 1. In the function `_updatePriceFeed()`, there is no minimum and maximum check for the value of the heartbeats.
In the contract `LendingMarketController`, the modifier `ifValidMaturity()` contains the following line of code: ``` if (isValidMaturity(_ccy, _maturity)) revert InvalidMaturity(); ``` This line is a contradiction but still works because the function `isValidMaturity()` returns `true` if the maturity does not exist, and false if the maturity does exist. As a result, this adds confusion for external readers of the code.
In rare cases, the compound factor can become very large and cause an overflow when converting from genesis value to future value. This only happens in circumstances of high interest rates for extended periods such as 200% APR for 197 years. **Note:** This issue was raised by the client during the audit process.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (77)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’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