Sturdy

QuantstampFiles named in the report

Quantstamp reviewed Sturdy on 13 Oct 2022: 26 findings, 0 critical; 12 marked fixed, 12 acknowledged, 2 open.

No open critical findings
Open the reportOpen Sturdy’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings26

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High000
Medium205
Low0127
Low`vaultYieldInPrice` Returns Incorrect Value for All VaultsAcknowledged

The `GeneralVault.vaultYieldInPrice()` function always returns `0` and is not overridden by any vault contracts. As such, this function always returns the incorrect value. This can be seen in the function definition below: ``` function vaultYieldInPrice() external view virtual returns (uint256) { return 0; } ```

Low`YearnVault.processYield()` May Run Out of GasAcknowledged

The `YearnVault.processYield()` function iterates over all debt assets multiple times and performs a swap for each asset. If the number of debt assets is sufficiently large, the function could easily run out of gas and fail. The function first iterates over all reserves when it calls `LendingPool.getBorrowingAssetAndVolumes()` through the `YearnVault._getAssetYields()` function. Then the `YearnVault._getAssetYields()` function iterates over every debt asset as shown from `L274-L286` below: ``` for (uint256 i; i < length; ++i) { assetYields[i].asset = assets[i]; if (i == length - 1) { // without calculation, set remained extra amount assetYields[i].amount = extraWETHAmount; } else { // Distribute wethAmount based on percent of asset volume assetYields[i].amount = _amount.percentMul( (volumes[i] * PercentageMath.PERCENTAGE_FACTOR) / totalVolume ); extraWETHAmount -= assetYields[i].amount; } } ``` The `YearnVault.processYield()` function then performs another iteration over every debt asset as shown from `L57-L62` below: ``` for (uint256 i; i < length; ++i) { // FTM -> Asset and Deposit to pool if (assetYields[i].amount != 0) { _convertAndDepositYield(assetYields[i].asset, assetYields[i].amount); } } ``` Since `YearnVault._convertAndDepositYield()` will perform a swap, this will result in significantly increased gas usage, possibly enough to make the entire transaction fail.

LowClone-and-OwnAcknowledged

The clone-and-own approach involves copying and adjusting open source code at one's own discretion. From the development perspective, it is initially beneficial as it reduces the amount of effort. However, from the security perspective, it involves some risks as the code may not follow the best practices, may contain a security vulnerability, or may include intentionally or unintentionally modified upstream libraries. Many contracts reference a locally cloned copy of OpenZeppelin contracts stored in the `protocol/dependencies/openzeppelin/*` folder.

LowFixed Slippage ValueAcknowledged

The `LidoVault` contract implements the `setSlippage()` function, allowing an administrator to change the slippage value when swapping stETH for ETH in Curve. 1. `YearnVault` uses a fixed one-percent slippage when swapping tokens in Uniswap. 2. `YearnRETHWstETHVault` uses a fixed two-percent slippage when swapping tokens in Curve.

LowOwnership Can Be RenouncedAcknowledged

If the owner renounces their ownership, all ownable contracts will be left without an owner. Consequently, any function guarded by the `onlyOwner` modifier will no longer be able to be executed. Many of the contracts have key administrative functions which are only accessible by the owner.

LowReentrancy Risks Can Be MitigatedAcknowledged

To avoid reentrancy risks and reduce the attack surface, it is highly recommended to inherit from OpenZeppelin's `ReentrancyGuard` contract and add the `nonReentrant` modifier to all functions that: 1. Involve asset manipulation. 2. Could be accessed by any external address. 3. Are not expected to be reentered The following is a non-exhaustive list of functions we recommend adding a `nonReentrant` modifier to: * `LendingPool` * `deposit()` * `withdraw()` * `borrow()` * `repay()` * `liquidationCall()` * `setUserUseReserveAsCollateral()` * `GeneralVault` * `depositCollateral()` * `depositCollateralFrom()` * `withdrawCollateral()` * `processYield()`

LowReliance on External Contracts to Secure FundsAcknowledged

The platform generates yields by sending the collateral tokens stored by borrowers to other protocols such as Lido and Convex. If any of these yield-generating protocols got hacked, the protocol users would also lose their funds.

LowReliance on Oracle Contracts that Are Not Within the ScopeAcknowledged

The `SturdyOracle` and lending rate oracle contracts are heavily used among the vault, lending pool, and interest rate contracts. However, they are not within the scope of the current audit. So it is worth noting here that the security of the platform relies on the security of these oracles. If the oracles are breached, the platform could behave unexpectedly, for instance, liquidating assets. And users could suffer from a great loss of their funds.

LowStable Debt Functionality Not RemovedAcknowledged

Although the stable debt functionality is not currently used by the Sturdy Protocol, the original AAVE code for the stable debt functionality has not been removed. Since the Sturdy Protocol does not currently utilize the stable debt functionality, it is unlikely to have been carefully tested and could thus result in unexpected bugs. The additional code also results in unnecessary additional attack surface for the protocol. There are many areas within the code where stable debt functionality still exists, including the following: * `DefaultReserveInterestRateStrategy` * `LendingPool` * `LendingPoolCollateralManager` * `LendingPoolConfigurator` * `GenericLogic` * `ReserveLogic` * `ValidationLogic`

LowToken Balances Not ValidatedAcknowledged

It is highly recommended to check the changes in the balance after the token transfer is expected to avoid most of the non-standard behavior of external token contracts (e.g. deflationary tokens) from being used by a hacker as a tool to manipulate the contract. Specifically, we recommend verifying the token balances at the following locations in the code: 1. `LendingPool.sol#L146` 2. `LendingPool.sol#L186` 3. `LendingPool.sol#L480` 4. `LendingPoolCollateralManager.sol#L250` 5. `ConvexCurveLPVault.sol#L192` 6. `LidoVault.sol#L133` 7. `YearnRETHWstETHVault.sol#L167` 8. `YearnVault.sol#L182`

Show 2 more
LowUnlocked PragmaAcknowledged

Every Solidity file specifies in the header a version number of the format `pragma solidity ^0.8.0`. The `^` before the version number implies an unlocked pragma, meaning that the compiler will use the specified version and above, hence the term "unlocked".

LowUser Funds Will Be Blocked if Sent Directly to VaultsAcknowledged

Some vault contracts implement the `receive()` function to receive ETH or FTM. However, it also opens the possibility for a user to send these native tokens directly to the contract by mistake.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (24)

  • LendingPoolAddressesProvider.sol
  • LidoVault.sol
  • LendingPoolAddressesProviderRegistry.sol
  • VariableDebtToken.sol
  • ConvexCurveLPVault.sol
  • DebtTokenBase.sol
  • SturdyInternalAsset.sol
  • LendingPool.sol
  • DefaultReserveInterestRateStrategy.sol
  • AToken.sol
  • YearnVault.sol
  • LendingPoolCollateralManager.sol
  • GenericLogic.sol
  • BaseImmutableAdminUpgradeabilityProxy.sol
  • ValidationLogic.sol
  • CollateralAdapter.sol
  • VersionedInitializable.sol
  • ReserveLogic.sol
  • InitializableImmutableAdminUpgradeabilityProxy.sol
  • YearnRETHWstETHVault.sol
  • IncentiveVault.sol
  • ATokenForCollateral.sol
  • GeneralVault.sol
  • LendingPoolConfigurator.sol

Part of Sturdy’s security record

Sturdy
Audits4 audits by 3 firmsLast audit Oct 2023, by ChainSecurity.
Critical findingsNo open critical findingsAcross 4 reports.
Open Sturdy’s security record

Security firm

Quantstamp

236 projects reviewed · 332 reports · latest Oct 2026

See Quantstamp’s audits

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