Sturdy security record

Ethereum, Fantom, Optimismsturdy.financeX

As of 21 Oct 2026, Sturdy has 4 audit reports by 3 security firms on Trustblock; the latest is by ChainSecurity, dated 13 Oct 2023. Those reports list no open critical findings.

Critical findings
No open critical findings
Across 4 reports.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits4

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Sturdy AggregatorSecurity firm: ChainSecurityDate: Scope: Files named in the reportNo open critical findingsOpen · chainsecurity.com
Sturdy security assessmentSecurity firm: ZellicDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Sturdy (Aura Integration And Leverage)Security firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com
SturdySecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com

Findings65

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

SeverityOpenAcknowledgedFixed
Critical000
High101
Medium3210
Low12522
MediumzkAllocation May Not Behave as ExpectedSturdy Aggregator · ChainSecurity · 13 Oct 2023Acknowledged

The report gives no description here.

Open the report
MediumBalancer LP Token Price May Be IncorrectSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The approach taken to calculate the Balancer LP Token value involves multiplying the price of the cheaper of the two tokens (`WETH` and `STETH`) by the value returned by `BALWSTETHWETH.getRate()`. This price calculation would be most accurate when the price of `WETH` is the same as `STETH`. However, the `WETH/STETH` price can fluctuate significantly, and the further they diverge, the less accurate the price calculation will be. While the calculation provides a reliable lower bound for the price, it is unclear how different this lower bound will be from the actual price. If the price calculation is too low, it could result in premature liquidation of users who have deposited the Balancer LP token as collateral.

Open the report
LowborrowAsset() Slippage ProtectionSturdy Aggregator · ChainSecurity · 13 Oct 2023Acknowledged

The report gives no description here.

Open the report
Low`enterPositionWithFlashloan()` Assumes Maximum SlippageSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The function `enterPositionWithFlashloan()` assumes the maximum amount of slippage when calculating the amount to borrow for the flash loan. This can be seen in the code below from the helper function `_leverageWithFlashloan()` ``` amounts[0] = ((((_principal * _getAssetPrice(COLLATERAL)) / 10**DECIMALS) * 10**borrowAssetDecimals) / _getAssetPrice(_borrowAsset)).percentMul(_leverage).percentMul( PercentageMath.PERCENTAGE_FACTOR + _slippage ); ``` This borrowed amount is then completely swapped to collateral tokens and deposited on behalf of the user as shown in the code below from`_enterPositionWithFlashloan()`: ``` //swap borrowing asset to collateral uint256 collateralAmount = _swapTo(_borrowingAsset, _borrowedAmount); require(collateralAmount >= _minAmount, Errors.LS_SUPPLY_FAILED); //deposit collateral _supply(collateralAmount, _user); //borrow borrowing asset _borrow(_borrowingAsset, _borrowedAmount + _fee, _user); ``` A user may expect that they will only deposit `_principal + _principal*_leverage` amount of collateral if the swap slippage is zero. They may be surprised to see that they have a more leveraged position than they expected. The `zapLeverageWithFlashloan()` suffers from the same issue.

Open the report
Low`STETH` Price Feed Is a Single Point of FailureSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The `BALWSTETHWETHOracle` contract's primary purpose is to provide a price for the `B-stETH-STABLE ` token. However, the correct calculation of the price is highly dependent on having the correct `STETH` price. Currently, the only source of this data is the Chainlink price feed. In the event of any large-scale attack/disruption of the Chainlink network, the protocol could be severely impacted.

Open the report
LowDirectly Transferred Tokens Are SwappedSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

As the `_swapTo()` and `_swapFrom()` functions in the `AURAWSTETHWETHLevSwap` contract return the whole contract balance of Balancer LP Tokens and WETH, respectively, such tokens, which were sent directly to the contract by accident, will be returned to the next user swapping to that asset. So, under such conditions, users will unexpectedly receive more tokens than perhaps anticipated from swaps. This of course does not seem problematic but could lead to unexpected behavior. The inflated amount of collateral would be used to create leverage, possibly resulting in a much larger position than desired. Of course, a user could immediately withdraw again and make a profit, but the user may also be confused by the protocol's behavior.

Open the report
LowEvents Not Emitted at Key State TransitionsSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The `registerLevSwapper()` function represents a critical state change for the protocol, as it changes or disables the leverage swap functionality for a particular collateral asset. The function should emit an event, so this key state transition can be monitored and responded to.

Open the report
LowFunctions Unnecessarily Marked as `payable`Sturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The functions `setConfiguration()` and `processExtraYield()` in the `AuraBalancerLPVault` contract are marked as `payable`, yet we were unable to find a reason for why that should be the case. It seems like there would be no use for ETH transferred via these methods and the funds would become stuck.

Open the report
LowLeverage Parameters Are Unconventional or UnintuitiveSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The definition of leverage the protocol uses might differ from a user's expectations. For example, most users would expect when using "2X" leverage that an additional 100% of the provided collateral would be borrowed. Yet in this protocol, a leverage of `2` will result in an additional `200%` being borrowed, which for some users might lead to unexpectedly creating higher leverage than desired. While the used definition differs, the documentation properly describes how the leverage is set and calculated in the protocol.

Open the report
LowPrivileged Roles and OwnershipSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The `LeverageSwapManager` possesses a function `registerLevSwapper()` which is callable by the administrator role of the contract. This function allows the admin to change the official implementation of the `*LevSwap` contract for a particular collateral to an entirely different one. They may also disable the leveraged swap functionality for the collateral by calling `registerLevSwapper()` with the zero address for the `swapper` parameter. Users should be made aware of the consequences of this privileged role and its consequences. The team should also warn users in advance if the leveraged swapped functionality is being disabled or changed for a collateral asset.

Open the report
Show 10 more
LowReentrancy Risks Can Be MitigatedSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

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: * `GeneralVault`/`AuraBalancerLPVault.sol` * `depositCollateral()` * `depositCollateralFrom()` * `withdrawCollateral()` * `processYield()`

Open the report
LowReliance on External Contracts to Secure FundsSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

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

Open the report
LowSwap Slippage Is Amplified by LeverageSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

The exploit scenario described in QSP-2 demonstrates that the effect of swap slippage can be greatly amplified. In that scenario, the swap slippage was only `10%,` and yet the user lost `45%` of their position's value. The amplification of slippage is primarily caused by the need to pay back the exact amount of borrowed tokens (plus any flash loan fees) to the flash loan provider. The flash loan provider absorbs no fraction of the loss of value due to slippage, so the entire cost is absorbed by the user. It follows that using higher leverage, and thus a higher borrow amount, leads to a higher amplification of the swap slippage rate.

Open the report
LowUnlocked PragmaSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

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".

Open the report
LowUnsafe CastsSturdy (Aura Integration And Leverage) · Quantstamp · 24 Jan 2023Acknowledged

There are multiple instances of unsafe casts within the contracts. These could lead to overflow or underflow of values and have unpredictable results. We have listed all instances of unsafe casts below: 1. `BALWSTETHWETHOracle`` 1. ``L25:uint256 minValue = Math.min(**uint256**(wstETHPrice), 1e18);` 2. `L39:return (true, int256(_get()));` 3. ``L45:return int256(_get());` 2. AURAWSTETHWETHLevSwap` 1. `L45:uint256 joinKind = **uint256**(IBalancerVault.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT);` 2. `L75:uint256 exitKind = **uint256**(IBalancerVault.ExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT);`

Open the report
Low`vaultYieldInPrice` Returns Incorrect Value for All VaultsSturdy · Quantstamp · 13 Oct 2022Acknowledged

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; } ```

Open the report
Low`YearnVault.processYield()` May Run Out of GasSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
LowClone-and-OwnSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
LowFixed Slippage ValueSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
LowOwnership Can Be RenouncedSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
Show 7 more
LowReentrancy Risks Can Be MitigatedSturdy · Quantstamp · 13 Oct 2022Acknowledged

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()`

Open the report
LowReliance on External Contracts to Secure FundsSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
LowReliance on Oracle Contracts that Are Not Within the ScopeSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report
LowStable Debt Functionality Not RemovedSturdy · Quantstamp · 13 Oct 2022Acknowledged

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`

Open the report
LowToken Balances Not ValidatedSturdy · Quantstamp · 13 Oct 2022Acknowledged

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`

Open the report
LowUnlocked PragmaSturdy · Quantstamp · 13 Oct 2022Acknowledged

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".

Open the report
LowUser Funds Will Be Blocked if Sent Directly to VaultsSturdy · Quantstamp · 13 Oct 2022Acknowledged

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.

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

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.

Content

Facts

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.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="sturdy"
></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