OpenCover
Covered Metavault

Examined the CoveredMetavault contract, an insured ERC-4626-compatible yield vault implementing the ERC-7540 async deposit/redemption lifecycle.
report id
0674
category
DeFi & Yield
language
Solidity
final report
December 17, 2025
lines of code
916
Content

Download audit report

1

Executive Summary

This document presents the results of a security review conducted by Nethermind Security for the OpenCover's CoveredMetavault contracts.

OpenCover is a specialist DeFi risk transfer provider, enabling individuals, businesses and institutions to protect themselves against technical and economic risks onchain.

The CoveredMetavault contract is a new primitive that implements the concept of an insured yield vault, enabling composable on-demand coverage natively onchain. It is a UUPS-upgradeable vault designed to wrap an underlying ERC-4626 compatible asset. It follows and extends the ERC-7540 standard to handle asynchronous deposits and redemptions through a request-settle-claim lifecycle managed by epochs. The protocol incorporates a configurable premium-streaming mechanism that directs a portion of settled assets to a designated collector to pay for coverage progressively from collected yield rather than upfront

The audit comprises 916 lines of the Solidity code. The audit was performed using (a) manual analysis of the codebase, and (b) automated analysis tools.

Audit assessment
Audit TypeSecurity Review
Initial ReportDecember 05, 2025
Final ReportDecember 17, 2025
Initial Commit5685593c2b
Final Commit84823a3b09
Documentation AssessmentHigh
Test Suite AssessmentHigh
Total findings
9
Fixed
8
Acknowledged
1
Mitigated
0
Unresolved
0

Findings by severity

  • Critical0
  • High0
  • Medium0
  • Low2
  • Informational5
  • Best Practices2

2

Audited Files

#ContractLoCCommentsRatioBlankTotal
1src/CoveredMetavault.sol76725933.8%2371263
2src/Constants.sol55100.0%414
3src/libraries/PercentageLib.sol16850.0%630
4src/interfaces/IERC7540.sol3383251.5%16132
5src/interfaces/IERC7540Cancel.sol1937194.7%662
6src/interfaces/ICoveredMetavault.sol71104146.5%40215
7src/interfaces/IERC7575.sol57140.0%214
Total91650354.9%3111730

4

System Overview

The CoveredMetavault is a UUPS upgradeable ERC-7540 vault, which implements an asynchronous process for deposits and redemptions. The vault's primary role is to stream premium for the insurance cover.

The users can make deposit and redemption requests that wait in a queue to be settled by entitled roles only. Post settlement, users can claim the processed requests.

The critical operations and management of the vault are governed by a role-based access control system with segregated roles for settlement, configuration, and pausability.

4.1 Asynchronous life cycle

The deposits and redemptions in CoveredMetavault are implemented as a three-phase process.

  • Request phase: A user initiates a request for a deposit or a redemption by transferring assets or vault shares to the contract. The request is queued and is pending for processing.
  • Settlement phase: A privileged actor, Keeper, periodically calls the settle function, which processes pending requests for deposit and settlement in a batch. Every settlement also advances the vault's epoch while computing the proportional assets or shares for the pending requests.
    Before settling the pending request, the vault computes the pending premiums and streams them to the premium collector account.
    As part of internal tracking, the vault clearly segregates between assets liable for premium and newly received assets that are yet to settle.
  • Claim phase: The actual computation of receivables is performed in the vault at the time of the claiming process. Using internal accounting, the vault calculates the claim amount based on the user's proportional assets and shares.
CoveredMetavault Interactions
Fig. 2: CoveredMetavault Interactions

4.2 Actors and Entry points

Below are the actors and their entry points.

  • User: Any one depositing into the CoveredMetavault.
    • requestDeposit: Submits assets to the vault to be queued for a future deposit.
    • requestRedeem: Submits vault shares to be queued for a future redemption.
    • deposit/mint: Claims vault shares after a deposit request has been settled by a Keeper.
    • redeem/withdraw: Claims underlying assets after a redemption request has been settled by a Keeper.
    • cancelDepositRequest: Cancels a pending deposit request and withdraws the submitted assets.
    • cancelRedeemRequest: Cancels a pending redemption request and withdraws the submitted shares.
  • Keeper: Entitled role responsible for settling the pending requests.
    • settle: Processes all pending deposit and redemption requests, making them claimable by users.
    • cancelDepositRequestForController: Cancels a pending deposit on behalf of a specific user.
    • pushDepositShares: Forcefully mints and sends shares to a user from their settled deposit balance.
    • pushRedeemAssets: Forcefully redeems and sends assets to a user from their settled redemption balance.
  • Anyone:
    • settleMaturedRedemption: Settles a specific redemption request if it has remained pending beyond a predefined maturity period, providing an exit path for users.
  • Guardian: Entitled role with permissions to pause or unpause the CoveredMetavault.
    • pause: To pause the functionality of the vault in case of unexpected scenarios.
    • unpause: To release the vault from a paused state.
  • Manager: Entitled role to configure the critical parameters of CoveredMetavault.
    • setPremiumRateBps: Configures the premium rate.
    • setPremiumCollector: Configures the address that receives the streamed premium.
    • setMinimumRequestAssets: Configures the minimum amount of assets that can be deposited into the vault.

4.3 Segregated Buckets

The segregation of assets in the CoveredMetavault functions as a tiered system that separates user intent from protocol accounting. The lifecycle begins with Pending Assets, which act as a temporary loading dock. The funds physically exist in the contract's balance but are mathematically ignored by the vault’s valuation logic. This isolation ensures that unverified deposits cannot manipulate the share price, while simultaneously allowing users to cancel and receive a full refund without affecting the broader system.

Once the Keeper executes a settlement, these funds move into the Settled Assets category.This qualifies the assets to be charged the management fee, yet they remain excluded from the exchange rate calculation.

The final stage of the life cycle occurs when the user manually claims their deposit, converting Claimable Assets into Active Assets. Only at this point are shares minted to the users at the current rate.

4.4 Time clocks

The CoveredMetavault uses two distinct Time Clocks to manage the state of user deposits.While they both track time, they serve completely different purposes.

  • Epochs: Epochs serve as a batching mechanism designed to organize incoming deposits into discrete groups. When a user deposits, their funds enter a pending state associated with the current epoch. Once the Keeper calls settle(), that epoch closes, a snapshot of the exchange rate between units and pending assets is taken, and these pending assets are effectively converted into claimable units.
  • Eras: Eras function as a solvency safeguard that acts as a hard reset for the claimable deposit pool in the rare event that all assets are consumed. Because the pending pool is subject to continuous premium fees, it is mathematically possible for the asset balance to decay to zero. if this happens, the contract increments the Era counter, effectively declaring bankruptcy on the old pool and wiping out any unclaimed units associated with it. This ensures that when new liquidity enters the vault, it starts fresh in a new Era without being diluted by the debt of users who spent their funds in the previous Era.

5

Risk Rating Methodology

The risk rating methodology used by Nethermind Security follows the principles established by the OWASP Foundation. The severity of each finding is determined by two factors: Likelihood and Impact.

Likelihood measures how likely the finding is to be uncovered and exploited by an attacker. This factor will be one of the following values:

  1. High: the issue is trivial to exploit and has no specific conditions that need to be met;
  2. Medium: the issue is moderately complex and may have some conditions that need to be met;
  3. Low: the issue is very complex and requires very specific conditions to be met.

When defining the likelihood of a finding, other factors are also considered. These can include but are not limited to motive, opportunity, exploit accessibility, ease of discovery, and ease of exploit.

Impact is a measure of the damage that may be caused if an attacker exploits the finding. This factor will be one of the following values:

  1. High: the issue can cause significant damage, such as loss of funds or the protocol entering an unrecoverable state;
  2. Medium: the issue can cause moderate damage, such as impacts that only affect a small group of users or only a particular part of the protocol;
  3. Low: the issue can cause little to no damage, such as bugs that are easily recoverable or cause unexpected interactions that cause minor inconveniences.

When defining the impact of a finding, other factors are also considered. These can include but are not limited to data and state integrity, loss of availability, financial loss, and reputation damage. After defining the likelihood and impact of an issue, the severity is determined according to the table below.

Impact / LikelihoodLowMediumHigh
HighMediumHighCritical
MediumLowMediumHigh
LowInfo / Best PracticesLowMedium
UndeterminedUndeterminedUndeterminedUndetermined

To address issues that do not fit a High, Medium or Low severity, Nethermind Security also uses three more finding severities: Informational, Best Practices, and Undetermined.

  1. Informational findings do not pose any risk to the application, but they carry some information that the audit team intends to pass to the client formally;
  2. Best Practice findings are used when some piece of code does not conform with smart contract development best practices;
  3. Undetermined findings are used when we cannot predict the impact or likelihood of the issue.

6

Issues

6.1 Pending assets are not reset when moving to a new era, allowing claims on the new pool

FileFix commitTypeStatus
src/CoveredMetavault.solb34107eLowFixed

The CoveredMetavault utilizes a concept of "claimable eras" to manage the lifecycle of the deposit pool. When the totalClaimableAssets of the vault are fully drained (e.g., due to premium streaming), the claimableEra increments, effectively marking the previous pool as dead.

The _syncEpoch(...) function is responsible for updating a controller's state to match the current vault state. It handles two main transitions:

  1. Era Sync: If the controller's claimableEra is behind the global era, it resets the controller's claimableUnits to zero and updates the era;
  2. Epoch Sync: If the controller's lastSyncedEpoch is behind the current epoch, it processes any pendingAssets by calculating the corresponding unit assignment and adding them to claimableUnits;

The issue is that pendingAssets represents deposits that were requested in a previous epoch and settled into the global pool at that time. If the global claimableEra has incremented since the user's last sync, it implies that the pool—to which those pendingAssets were contributed—has been fully exhausted.

However, the logic in _syncEpoch clears claimableUnits upon an era change but does not clear pendingAssets. The execution flows immediately into the epoch sync block, where it converts these stale pendingAssets (using the historical epochAllocations) into units and adds them to the controller's claimableUnits.

Since the controller's era was just updated to the *new* era, these units are now valid claims against the *new* era's pool. This effectively allows a user to carry over a claim from a drained/dead pool into a fresh pool, potentially diluting new depositors or stealing assets they did not contribute to.

Recommendation

Consider resetting pendingAssets to zero and updating the lastSyncedEpoch when a controller is detected to be in an outdated era. This ensures that claims tied to a fully drained pool are correctly discarded.

Resolution

Fixed in commit b34107. Resolved by pre-minting metavault shares during settlement, simplifying vault accounting and eliminating the need for eras.

6.2 Users may be restricted from withdrawing their full remaining balance if it is below the minimum request amount

FileFix commitTypeStatus
src/CoveredMetavault.solb5a250cLowFixed

The CoveredMetavault enforces a minimum asset amount ( minimumRequestAssets) for asynchronous redemption requests initiated via requestRedeem(...).

This check ensures that the resulting underlying asset value from the requested shares meets the minimum threshold.

The constraint is enforced within requestRedeem(...) before the redemption request is recorded:

function requestRedeem(uint256 shares, address controller, address owner)
external
override
// ...
returns (uint256 requestId)
{
require(shares != 0, ZeroShares());
require(controller == owner, InvalidController(controller));
require(owner == msg.sender, InvalidOwner(owner));

uint256 redeemAssetAmount = _convertToAssets(shares, Math.Rounding.Floor);
_requireMinimumRequestAssets(redeemAssetAmount); // The minimum request assets check is enforced here.

// ...
}

If a user's total remaining share balance corresponds to an underlying asset value that is less than minimumRequestAssets, they will be blocked from initiating a withdrawal using requestRedeem(...) because the check in _requireMinimumRequestAssets(...) will revert.

While the assets are not permanently locked, this mechanism hinders a user's ability to fully exit the position if their final balance is low.

Recommendation

Consider modifying the minimum request asset requirement to allow requests that are either greater than or equal to the configured minimum, OR requests that represent the user's entire remaining balance. This maintains the protection against "dust" operations while guaranteeing that users can always fully exit their position.

Resolution

Fixed in commit b5a250. Removed this restriction for redemptions, allowing users to always redeem their full remaining balance. Minimums are now enforced by the offchain settlement engine that operates the keeper, while users can still settle and claim redemptions permissionlessly via settleMaturedRedemption.

6.3 Floor rounding in premium calculation causes the claimable pool to overpay premiums

FileFix commitTypeStatus
src/CoveredMetavault.solb34107eInfoFixed

The _streamPremium() function calculates and transfers a premium fee based on the total settled assets of the vault. Since the vault's assets are logically partitioned into "backed assets" and "claimable assets", the premium payment must be proportionally deducted from both pools.

When updating the totalClaimableAssets to reflect the deduction, the contract calculates the new balance using the ratio of assets remaining after the premium payment:

// src/CoveredMetavault.sol

// ...
if (claimableAssetsTotal != 0) {
uint256 newClaimableAssetTotal =
claimableAssetsTotal.mulDiv(assetsAfter, assetsBefore, Math.Rounding.Floor); // @audit-issue Rounding down the remaining assets
// ...
_vaultStorage.totalClaimableAssets = newClaimableAssetTotal;
// ...

By using Math.Rounding.Floor to calculate newClaimableAssetTotal, the contract rounds down the remaining assets in the claimable pool. This effectively means the reduction applied to the claimable pool (the premium paid by it) is rounded *up*.

Consequently, the claimable pool bears the burden of the rounding error, paying slightly more premium than the backed assets pool. Over time, this slight overpayment creates a disadvantage for users whose assets are sitting in the claimable pool waiting to be claimed, as their claimable units will correspond to a slightly smaller total asset amount than mathematically precise.

Recommendation

The current implementation places the burden of precision loss on the claimable pool. While switching to Math.Rounding.Ceil would shift this burden to the backed pool, the current design systematically favors the backed assets.

Consider evaluating this trade-off. If the intent is to protect the backed pool, no change is needed, but consider documenting this behavior to incentivize users to finalize claims.

Resolution

Fixed in commit b34107. Resolved by pre-minting shares at settlement so that premiums are applied via the share price only, eliminating the need to scale the claimable pool.

6.4 Premium streaming can be enabled prematurely before the first settlement

FileFix commitTypeStatus
src/CoveredMetavault.sol3fc8d33InfoFixed

The CoveredMetavault uses the lastPremiumTimestamp state variable to track when the premium was last streamed. A value of 0 is intended to serve as a sentinel indicating that the vault has not yet undergone its first settlement, ensuring that no premium is accrued or charged for the period preceding the first epoch. This logic is enforced in the _streamPremium function, which checks that lastPremiumTimestamp != 0 before calculating the premium.

However, the lastPremiumTimestamp variable can be set to a non-zero value before the first settlement in two scenarios:

  1. During Initialization: The initialize(...) function explicitly sets lastPremiumTimestamp to block.timestamp if the initial premiumRateBps is non-zero;
  2. Updating Premium Rate: The setPremiumRateBps(...) function calls _streamPremium() to settle accrued premiums before updating the rate. The _streamPremium() function unconditionally updates lastPremiumTimestamp to the current timestamp at the end of execution, even if the calculation was skipped because the previous timestamp was 0;

In both cases, lastPremiumTimestamp becomes non-zero before the first settle() call. As a result, when the first settlement eventually occurs, the vault will calculate and charge premiums for the duration elapsed since initialization or the rate update. Note that is not expected for the vault to hold funds at this time.

Recommendation

Consider modifying the initialize(...) function to ensure lastPremiumTimestamp remains 0, even if a premium rate is set. Additionally, update the setPremiumRateBps(...) function to check if lastPremiumTimestamp is 0; if so, it can skip the _streamPremium() call and simply update the rate, preserving the sentinel value until the first settlement.

Resolution

Fixed in commit 3fc8d3. Resolved by removing the lastPremiumTimestamp assignment from the initialiser and guarding premium streaming in setPremiumRateBps to preserve the sentinel value until the first settlement.

6.5 The requestRedeem function uses an external call to transfer vault's shares

FileFix commitTypeStatus
src/CoveredMetavault.sol915324eInfoFixed

The CoveredMetavault contract functions has the requestRedeem function to return shares to the vault in exchange for assets. The function explicitly validates that the caller is the owner of the shares or has the appropriate permissions.

However, the contract uses an external call to transfer the shares from the user to the contract itself.

function requestRedeem(uint256 shares, address controller, address owner) external {
// ...
if (msg.sender != owner) {
revert InvalidOwner(owner);
}
// ...
// @audit-issue This requires prior approval and unnecessarily increases gas costs.
IERC20(address(this)).safeTransferFrom(owner, address(this), shares);
// ...
}

By using safeTransferFrom on address(this), the contract triggers the standard ERC20 allowance check. This creates a circular dependency where the user must approve the vault contract to spend the vault's own tokens, even though the user is the one initiating the transaction.

This leads to a degraded User Experience as the user must sign and broadcast two transactions (approve and redeem) instead of one. Additionally, it increases the gas cost due to the external CALL opcode and the storage operations required for the allowance check.

Recommendation

Consider replacing the external safeTransferFrom call with the internal _transfer function inherited from the ERC20Upgradeable contract. This will bypass the redundant allowance check and allow users to redeem their shares in a single transaction.

Resolution

Fixed in commit 915324. Resolved by replacing the external safeTransferFrom with an internal _transfer call when locking vault shares in requestRedeem.

6.6 Unfair unit distribution in _computeUnitAssignment favors early claimers

FileFix commitTypeStatus
src/CoveredMetavault.solb34107eInfoFixed

The _computeUnitAssignment function determines the share of claimable units a controller receives based on their pending assets. The algorithm calculates maxAssignableUnits by determining the minimum number of units that must be reserved for the remaining unassignedAssets.

However, the function implements an "optimistic" rounding mechanism. If the standard pro-rata calculation ( unitsToAssign) is less than maxAssignableUnits and the division results in a remainder, the function explicitly increments unitsToAssign by 1.

// ...
// Start from floor and clamp for extra safety.
unitsToAssign = pendingAssets.mulDiv(epochTotalUnits, epochTotalAssets, Math.Rounding.Floor);
if (unitsToAssign > maxAssignableUnits) unitsToAssign = maxAssignableUnits;

// @audit-issue This logic rounds up the assignment, consuming units meant for later users.
// Grant at most +1 unit if it won't breach the remaining units limit.
if (unitsToAssign < maxAssignableUnits) {
uint256 remainderNumerator = mulmod(pendingAssets, epochTotalUnits, epochTotalAssets);
if (remainderNumerator != 0) {
unitsToAssign += 1;
if (unitsToAssign > maxAssignableUnits) unitsToAssign = maxAssignableUnits;
}
}
}

This logic systematically favors users who sync their state (claim) earlier in the epoch. By rounding up their unit allocation, early claimers extract more value from the epochTotalUnits pool than their exact pro-rata share. Since the total number of units is fixed, this over-allocation reduces the unassignedUnits available for subsequent users. Consequently, users who claim later will inevitably receive fewer units than their fair share to balance the epoch, resulting in an unfair distribution based on claim order.

Recommendation

Consider removing the conditional block that increments unitsToAssign by 1. Relying on standard floor rounding ( Math.Rounding.Floor) for all unit assignments ensures that every user receives a consistent and fair share of the units relative to their assets, regardless of the order in which they sync.

Resolution

Fixed in commit b34107. Replaced _computeUnitAssignment with a simpler, per-epoch, floor-based share allocation that removes order-dependent bias.

6.7 claimableRedeemRequest returns non-zero for already claimed request

FileFix commitTypeStatus
src/CoveredMetavault.sol—InfoAcknowledged

claimableRedeemRequest is a view function that returns the number of shares that can be claimed, if the redeem request is already settled. But, this number is valid only until the actual claim is processed. After the shares are already claimed via redeem, the request will not have claimable shares as they were already processed.

function claimableRedeemRequest(uint256 requestId, address controller)
external
view
override
returns (uint256 claimableShares)
{
RedeemRequestStorage storage redeemRequestStorage = _vaultStorage.redeemRequests[requestId];
if (redeemRequestStorage.controller != controller || redeemRequestStorage.timestamp == 0) return 0;

// Audit - this is valid only under the shares are not claimed by the user. Post redeem call, this function should return 0 shares considering the shares were already claimed.
==> return redeemRequestStorage.settled ? redeemRequestStorage.shares : 0;
}

Recommendation

Consider documenting this behavior for integrators and users that can use this feature.

Resolution

Documented the current behaviour in both NatSpec comments and the integrator guides.

6.8 Single-step ownership transfer can lead to permanent loss of ownership

FileFix commitTypeStatus
src/CoveredMetavault.sol5960af6Best PracticesFixed

The transferOwnership() function from the Vault contract is used to transfer control of the contract to a new address. The function validates that the newOwner is not the zero address, revokes the owner role from the caller, and grants the owner role to the newOwner address.

However, the function performs this transfer in a single step. It does not verify that the newOwner account is accessible or correct beyond the zero address check.

This means that if the current owner accidentally passes an incorrect address, the ownership of the vault will be permanently lost. Since the current owner revokes their own role immediately during the call, there is no way to recover access.

function transferOwnership(address newOwner) external {
// ...
require(newOwner != address(0), "Invalid address");

_revokeRole(OWNER_ROLE, msg.sender);
// @audit If newOwner is incorrect, ownership is lost forever.
_grantRole(OWNER_ROLE, newOwner);
}

Recommendation

Consider implementing a two-step ownership transfer process. This pattern allows the current owner to propose a newOwner address, but the transfer is not finalized until the newOwner explicitly accepts it. This approach ensures that the newOwner address is valid and under control before the previous owner relinquishes their rights.

Resolution

Fixed in commit 5960af. The contract now implements a two-step ownership transfer process.

6.9 Use Namespaced storage for the vault

FileFix commitTypeStatus
src/CoveredMetavault.sol08e0746Best PracticesFixed

CoveredMetavault is an upgradeable contract. In order to minimise the storage layout conflicts, it is recommended to use Namespaced storage instead of using the storage slot of this contract.

Recommendation

Use namespace storage for storage variables.

Resolution

Fixed in commit 08e074. Resolved by using ERC-7201 compliant namespaced storage for vault state.

7

Documentation Evaluation

Software documentation refers to the written or visual information that describes the functionality, architecture, design, and implementation of software. It provides a comprehensive overview of the software system and helps users, developers, and stakeholders understand how the software works, how to use it, and how to maintain it. Software documentation can take different forms, such as user manuals, system manuals, technical specifications, requirements documents, design documents, and code comments. Software documentation is critical in software development, enabling effective communication between developers, testers, users, and other stakeholders. It helps to ensure that everyone involved in the development process has a shared understanding of the software system and its functionality. Moreover, software documentation can improve software maintenance by providing a clear and complete understanding of the software system, making it easier for developers to maintain, modify, and update the software over time. Smart contracts can use various types of software documentation. Some of the most common types include:

  • Technical whitepaper: A technical whitepaper is a comprehensive document describing the smart contract’s design and technical details. It includes information about the purpose of the contract, its architecture, its components, and how they interact with each other;
  • User manual: A user manual is a document that provides information about how to use the smart contract. It includes step-by-step instructions on how to perform various tasks and explains the different features and functionalities of the contract;
  • Code documentation: Code documentation is a document that provides details about the code of the smart contract. It includes information about the functions, variables, and classes used in the code, as well as explanations of how they work;
  • API documentation: API documentation is a document that provides information about the API (Application Programming Interface) of the smart contract. It includes details about the methods, parameters, and responses that can be used to interact with the contract;
  • Testing documentation: Testing documentation is a document that provides information about how the smart contract was tested. It includes details about the test cases that were used, the results of the tests, and any issues that were identified during testing;
  • Audit documentation: Audit documentation includes reports, notes, and other materials related to the security audit of the smart contract. This type of documentation is critical in ensuring that the smart contract is secure and free from vulnerabilities.

These types of documentation are essential for smart contract development and maintenance. They help ensure that the contract is properly designed, implemented, and tested, and they provide a reference for developers who need to modify or maintain the contract in the future.

8

Remarks & Test Suite

The development team provided a detailed overview for the CoveredMetaVault contract and its purpose. The code also contains inline comments to describe the functionality.The development team was collaborative during the audit, providing answers to all questions raised by the Nethermind Security team during calls and through asynchronous communications.

The AuditAgent is an AI-powered smart contract auditing tool that analyses code, detects vulnerabilities, and provides actionable fixes. It accelerates the security analysis process, complementing human expertise with advanced AI models to deliver efficient and comprehensive smart contract audits. Available at https://app.auditagent.nethermind.io.

Test suite
224Tests passed0Failed

The complete test output is in the full report.

As auditors, we recommend that any changes or updates made to the audited codebase undergo a re-audit or security review to address potential vulnerabilities or risks introduced by the modifications. By conducting a re-audit or security review of the modified codebase, you can significantly enhance the overall security of your system and reduce the likelihood of exploitation. However, we do not possess the authority or right to impose obligations or restrictions on our clients regarding codebase updates, modifications, or subsequent audits. Accordingly, the decision to seek a re-audit or security review lies solely with you.

This report is based on the scope of materials and documentation provided by you to Nethermind in order that Nethermind could conduct the security review outlined in 1. Executive Summary and 2. Audited Files. The results set out in this report may not be complete nor inclusive of all vulnerabilities. Nethermind has provided the review and this report on an as-is, where-is, and as-available basis. You agree that your access and/or use, including but not limited to any associated services, products, protocols, platforms, content, and materials, will be at your sole risk. Blockchain technology remains under development and is subject to unknown risks and flaws. The review does not extend to the compiler layer, or any other areas beyond the programming language, or other programming aspects that could present security risks. This report does not indicate the endorsement of any particular project or team, nor guarantee its security. No third party should rely on this report in any way, including for the purpose of making any decisions to buy or sell a product, service or any other asset. To the fullest extent permitted by law, Nethermind disclaims any liability in connection with this report, its content, and any related services and products and your use thereof, including, without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement. Nethermind does not warrant, endorse, guarantee, or assume responsibility for any product or service advertised or offered by a third party through the product, any open source or third-party software, code, libraries, materials, or information linked to, called by, referenced by or accessible through the report, its content, and the related services and products, any hyperlinked websites, any websites or mobile applications appearing on any advertising, and Nethermind will not be a party to or in any way be responsible for monitoring any transaction between you and any third-party providers of products or services. As with the purchase or use of a product or service through any medium or in any environment, you should use your best judgment and exercise caution where appropriate. FOR AVOIDANCE OF DOUBT, THE REPORT, ITS CONTENT, ACCESS, AND/OR USAGE THEREOF, INCLUDING ANY ASSOCIATED SERVICES OR MATERIALS, SHALL NOT BE CONSIDERED OR RELIED UPON AS ANY FORM OF FINANCIAL, INVESTMENT, TAX, LEGAL, REGULATORY, OR OTHER ADVICE.