Download audit report
This document presents the security review performed by Nethermind Security for VANA protocol smart contracts. The audit focused on pull request #13, which introduces a new stacking mechanism, i.e. VanaPool, and changes to allow migration to it. VanaPool is a staking protocol that allows users to stake VANA tokens in community-managed entities with configurable APY rates.
The audited code comprises 1116 lines of code written in the Solidity language. The audit includes the new staking contract and changes to allow migration of current stakes to the new staking mechanism.
The audit was performed using (a) manual analysis of the codebase, (b) automated analysis tools, and (c) creation of test cases.
| Audit assessment | |
| Audit Type | Security Review |
| Response from Client | Regular responses during audit engagement |
| Final Report | April 14th, 2025 |
| Repository | vana-smart-contracts |
| Commit (Audit) | 1da399c41f |
| Commit (Final) | 32b77010b9 |
| Documentation Assessment | Medium |
| Test Suite Assessment | Medium |
Findings by severity
The VANA team introduced new changes into their existing protocol, VanaPool. VanaPool is a staking protocol that allows users to stake VANA tokens into community-managed entities with configurable APY rates. The system implements a share-based model where stakers receive proportional claims on entity reward pools that appreciate in value as rewards accrue. The protocol features a secure treasury component for custody of staked tokens, with proper access controls governing all system operations. VanaPool supports staking on behalf of others and implements time-weighted reward distribution based on entity-specific APY settings. The new changes introduce three main contracts, each implemented behind its own proxy contract

The VanaPoolEntityImplementation contract serves as the management layer for staking entities (pools). It handles entity creation, configuration, and reward distribution mechanics. When an admin or authorized user creates a new entity, they must provide a registration stake and basic parameters like the entity name and owner address. The contract calculates reward distribution rates based on the configured APY and manages pool rewards. Entity owners add rewards to their pools, which are then gradually released to stakers based on the configured APY.
The VanaPoolStakingImplementation contract handles users' core staking and unstaking operations. It acts as the interface between users and the entity system, managing individual staker positions and share calculations. When users stake VANA, the contract calculates the appropriate number of shares based on the current conversion rate from the corresponding entity. These shares represent the user's proportional claim on the entity's reward pool.
The staking process involves transferring funds to the treasury and updating both the user's share balance and the entity's total shares. For unstaking, the contract performs the reverse operation, converting shares back to VANA at the current rate and ensuring the entity owner maintains their minimum required stake.
The VanaPoolTreasuryImplementation contract serves as the secure custodian for all staked funds and rewards. Its primary function is to receive, hold, and disburse VANA tokens as directed by the other contracts in the system. The treasury operates with strict access controls, only processing transactions initiated by authorized contracts or administrators.
Main flows through the treasury include receiving staked funds during user deposits, holding rewards added by entity owners, and processing withdrawals when users unstake.
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:
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:
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 / Likelihood | Low | Medium | High |
| High | Medium | High | Critical |
| Medium | Low | Medium | High |
| Low | Info / Best Practices | Low | Medium |
| Undetermined | Undetermined | Undetermined | Undetermined |
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.
| File | Fix commit | Type | Status |
VanaPoolEntityImplementation.sol | PR #13 | Critical | Fixed |
When creating an entity, the createEntity function requires the owner to deposit a minimum registration stake, which is intended to remain locked indefinitely.
function unstake(uint256 entityId, uint256 shareAmount) external override nonReentrant whenNotPaused {
// --SNIP
if (entityInfo.ownerAddress == _msgSender()) {
uint256 ownerMinShares = (vanaToShare * vanaPoolEntity.minRegistrationStake()) / 1e18;
if (stakerShares - shareAmount < ownerMinShares) {
revert CannotRemoveRegistrationStake();
}
}
// --SNIP
}However, the above check can be easily bypassed. The updateEntity(...) allows the update of the owner and does not move the registration shares to the new owner.
function updateEntity(
uint256 entityId,
EntityRegistrationInfo calldata entityRegistrationInfo
) external override whenNotPaused nonReentrant onlyEntityOwner(entityId) {
// --SNIP
// Update fields
entity.ownerAddress = entityRegistrationInfo.ownerAddress;
emit EntityUpdated(entityId, entityRegistrationInfo.ownerAddress, entityRegistrationInfo.name);
}The entity owner can leverage this to change the ownerAddress to another andress he controls via updateEntity and then calls unstake to freely withdraw his registration stake.
Recommendation
Consider moving the registration shares to the new entity owner when updating the owner.
processRewards(...) cause higher reward rate, regardless of time elapsed| File | Fix commit | Type | Status |
VanaPoolEntityImplementation.sol | PR #13 PR #13 PR #13 | Critical | Fixed |
The processRewards(...) is a permissionless function which updates the rewards for an entity. It calculates the rewards to distribute based on the time elapsed and the rate of reward.
function processRewards(uint256 entityId) public override whenNotPaused {
// ...
// Calculate theoretical rewards based on maxAPY
uint256 toDistribute = (entity.activeRewardPool * entity.ratePerSecond * timeElapsed) / 1e18;
if (toDistribute > entity.lockedRewardPool) {
toDistribute = entity.lockedRewardPool;
}
entity.lockedRewardPool -= toDistribute;
// @audit activeRewardPool itself is increased by toDistribute
entity.activeRewardPool += toDistribute;
// ...
}However, the function uses activeRewardPool to calculate the rewards and activeRewardPool itself is increased by the rewards to distribute. This creates an accumulating growth in the reward amount over time, which could lead to excessively increasing rewards rather than a more predictable, linear growth based on time elapsed. As a result, the rewards are influenced not only by the time elapsed but also by the number of times processRewards(...) is called.
For example, let's say activeRewardPool is 1000 and ratePerSecond is 2e18. Consider these scenarios:
Scenario A:
processRewards(...) with a time elapsed of 100 results in an activeRewardPool of 201,000;processRewards(...) with a time elapsed of 200 results in an activeRewardPool of 80,400,000;Scenario B:
processRewards(...) with a time elapsed of 300 results in an activeRewardPool of 600,000;In both the scenarios, time elapsed is the same ( 300), but the resulting activeRewardPool is significantly different.
Recommendation
Consider using two separate variables: one for the activeRewardPool and another for the totalStake. The totalStake should be used to calculate the rewards to distribute, ensuring that the rewards grow linearly with respect to time elapsed.
Resolution
Fixed in commits 97de94d, 265bcde, and 2177752.
| File | Fix commit | Type | Status |
DLPRootImplementation.sol | PR #13 | Critical | Fixed |
The DLPRootImplementation contract introduces the migrateStakeToVanaPool(...) function, which allows users to migrate their existing DLP stakes to the VANA pool. This function stakes the user's existing amount plus any claimable rewards into the pool for a specific entity.
function migrateStakeToVanaPool(uint256 stakeId, uint256 entityId) external payable nonReentrant whenNotPaused {
dlpRootEpoch.createEpochs();
Stake storage stake = _stakes[stakeId];
if (stake.endBlock == 0) {
stake.movedAmount = stake.amount;
_closeStake(_msgSender(), stakeId);
stake.movedAmount = 0;
}
uint256 toClaim = stake.amount + _calculateStakeRewardUntilEpoch(stakeId, dlpRootEpoch.epochsCount() - 1, true);
if (toClaim == 0) {
revert NothingToClaim();
}
bool success = dlpRootRewardsTreasury.transferVana(payable(address(this)), toClaim);
if (!success) {
revert TransferFailed();
}
vanaPoolStaking.stake{value: toClaim}(entityId, stake.stakerAddress);
emit StakeMigratedToVanaPool(stakeId, stake.amount, entityId);
}However, the function does not update the stake.amount to 0 or delete its entry from the _stakes mapping, allowing users to call this function repeatedly.
Recommendation
Consider clearing the _stakes entry or updating stake.amount to 0 for the migrated stake.
| File | Fix commit | Type | Status |
VanaPoolEntityImplementation | PR #13 | Critical | Fixed |
Users can create entities via createEntity(...), and stakers can allocate their funds to a specific entity ID through VanaPoolStakingImplementation::stake(...). However, entity owners can remove an entity at any time, updating its status to Removed:
function removeEntity(uint256 entityId) external override whenNotPaused nonReentrant onlyEntityOwner(entityId) {
Entity storage entity = _entities[entityId];
if (entity.status != EntityStatus.Active) {
revert InvalidEntityStatus();
}
// Process any pending rewards
processRewards(entityId);
==> entity.status = EntityStatus.Removed;
_activeEntityIds.remove(entityId);
emit EntityStatusUpdated(entityId, EntityStatus.Removed);
}Once an entity is removed, stakers who have already staked funds into it become unable to unstake, as the contract restricts unstaking from non-active entities:
function unstake(uint256 entityId, uint256 shareAmount) external override nonReentrant whenNotPaused {
// --SNIP
==> vanaPoolEntity.updateEntityPool(entityId, shareAmount, exactVanaAmount, false);
bool success = vanaPoolTreasury.transferVana(payable(_msgSender()), exactVanaAmount);
if (!success) {
revert TransferFailed();
}
}
function updateEntityPool(
uint256 entityId,
uint256 shares,
uint256 amount,
bool isStake
) external override whenNotPaused onlyRole(VANA_POOL_ROLE) {
if (!hasRole(VANA_POOL_ROLE, msg.sender)) {
revert NotAuthorized();
}
Entity storage entity = _entities[entityId];
if (entity.status != EntityStatus.Active) {
==> revert InvalidEntityStatus();
}
// --SNIP
}As a result, stakers' funds become permanently locked, as they are unable to unstake from a removed entity.
Recommendation
There are many approaches to fix the issue, it depends on how the team intends to fix the issue. Two possible approaches include:
Resolution
Fixed by removing the removeEntity(...) function in db7afd9
| File | Fix commit | Type | Status |
DLPRootImplementation.sol | PR #13 | Critical | Fixed |
The DLPRootImplementation contract introduces the migrateStakeToVanaPool(...) function, which allows users to migrate their existing VANA stakes to the VANA pool. This function stakes the user's existing amount plus any claimable rewards into the pool for a specific entity.
function migrateStakeToVanaPool(uint256 stakeId, uint256 entityId) external payable nonReentrant whenNotPaused {
dlpRootEpoch.createEpochs();
Stake storage stake = _stakes[stakeId];
if (stake.endBlock == 0) {
stake.movedAmount = stake.amount;
_closeStake(_msgSender(), stakeId);
stake.movedAmount = 0;
}
uint256 toClaim = stake.amount + _calculateStakeRewardUntilEpoch(stakeId, dlpRootEpoch.epochsCount() - 1, true);
if (toClaim == 0) {
revert NothingToClaim();
}
bool success = dlpRootRewardsTreasury.transferVana(payable(address(this)), toClaim);
if (!success) {
revert TransferFailed();
}
vanaPoolStaking.stake{value: toClaim}(entityId, stake.stakerAddress);
emit StakeMigratedToVanaPool(stakeId, stake.amount, entityId);
}However, the function does not properly update the _stakes mapping to mark the stake as withdrawn or delete its entry, leading to a double withdrawal exploit which can be exploited by users to migrate their stakes to vana pools and withdraw them from the DLPRoot contract as well. Consider the following exploit path:
DLPRootImplementation;closeStake(...);migrateStakeToVanaPool(...);withdrawStake(...), even though it has already been migrated;Since the contract does not properly mark the stake as migrated/withdrawn, Bob effectively retrieves his original stake while also having it staked in the VANA pool, leading to contract losses.
Similarly, as there's no check to ensure that already withdrawn stakes cannot be migrated, users can migrate their already withdrawn stakes to the VANA pool.
Recommendation
Consider adding a check to restrict already withdrawn stakes and clearing the _stakes entry or set the withdrawn status to true for the migrated stake.
processRewards(...) is called too late in unstake(...)| File | Fix commit | Type | Status |
VanaPoolStakingImplementation.sol | PR #13 | High | Fixed |
The unstake(...) function allows users to withdraw their staked VANA tokens, receiving an amount proportional to their shares in the entity. However, processRewards(...) is called after the proportion calculation, which means rewards that should be included in the vanaToShare conversion are not accounted for
function unstake(uint256 entityId, uint256 shareAmount) external override nonReentrant whenNotPaused {
uint256 stakerShares = _stakers[_msgSender()].entities[entityId].shares;
if (stakerShares == 0 || shareAmount == 0) {
revert InvalidAmount();
}
IVanaPoolEntity.EntityInfo memory entityInfo = vanaPoolEntity.entities(entityId);
uint256 vanaToShare = vanaPoolEntity.vanaToEntityShare(entityId);
==> vanaPoolEntity.processRewards(entityId);
// --SNIP
uint256 exactVanaAmount = (1e18 * shareAmount) / vanaToShare;
// --SNIPSince vanaToShare is calculated before processing rewards, it does not reflect the most up-to-date active rewards, causing users to receive fewer VANA tokens than they should.
Recommendation
Consider calling vanaPoolEntity::processRewards(...) before calculating the vanaToShare.
stake(...) and unstake(...) functions lack slippage protection| File | Fix commit | Type | Status |
VanaPoolStakingImplementation.sol | PR #13 | Medium | Fixed |
Users interact with VanaPoolStakingImplementation via the stake(...) and unstake(...) functions to allocate and deallocate funds from specific entities. However, these functions lack slippage protection, exposing users to potential value losses due to fluctuations in share distribution.
For example, if a user attempts to unstake their shares, they might receive fewer VANA tokens than expected if other stake transactions are processed just before them, increasing the total shares and decreasing their withdrawal amount.
Recommendation
Allow users to specify acceptable minimum values in both stake(...) and unstake(...) functions.
unstake(...) function should use entityShareToVana(...) to round down to favor protocol| File | Fix commit | Type | Status |
VanaPoolStakingImplementation.sol | 2177752 | Medium | Fixed |
The unstake(...) function allows users to unstake a specified amount of shares and transfer equivalent amount of VANA tokens to the user. To convert the shares to VANA tokens, it divides the share amount by the result of the vanaToEntityShare(...) function.
function unstake(uint256 entityId, uint256 shareAmount) external override nonReentrant whenNotPaused {
// ...
// @audit vanaToEntityShare(...) rounds down the result
uint256 vanaToShare = vanaPoolEntity.vanaToEntityShare(entityId);
// ...
// Store the exact VANA amount corresponding to shares at this point
// @audit Dividing by a smaller value increases the result
uint256 exactVanaAmount = (1e18 * shareAmount) / vanaToShare;
// Update staker's position
_stakers[_msgSender()].entities[entityId].shares -= shareAmount;
// Update entity staking data in VanaPoolEntity contract
vanaPoolEntity.updateEntityPool(entityId, shareAmount, exactVanaAmount, false);
bool success = vanaPoolTreasury.transferVana(payable(_msgSender()), exactVanaAmount);
if (!success) {
revert TransferFailed();
}
emit Unstaked(entityId, _msgSender(), exactVanaAmount, shareAmount);
}However, the vanaToEntityShare(...) rounds down the result. As dividing by a smaller value results in a larger outcome, this leads to the user receiving more tokens than they should.
function vanaToEntityShare(uint256 entityId) external view override returns (uint256) {
Entity storage entity = _entities[entityId];
return entity.activeRewardPool > 0 ? (entity.totalShares * 1e18) / entity.activeRewardPool : 1e18;
}For example, if activeRewardPool is 43197679269104491383 and totalShares is 3499999999999991547, consider the following exploit scenario:
640854177891528377162 VANA tokens and receives 51923845460479575293 shares;51923845460479575293 shares)and receives 640854177891528385059 VANA tokens;In this case, Bob receives an extra 7897 VANA tokens due to the rounding error.
Recommendation
Consider using entityShareToVana(...) function instead of vanaToEntityShare(...) for converting shares to VANA tokens. This can be done by multiplying the share amount by the result of entityShareToVana(...) function.
migrateStakeToVanaPool(...) on behalf of others| File | Fix commit | Type | Status |
DLPRootImplementation.sol.sol | 2177752 | Medium | Fixed |
The DLPRootImplementation contract introduces the migrateStakeToVanaPool(...) function, which allows users to migrate their existing DLP stakes to the VANA pool. This function stakes the user's existing amount plus any claimable rewards into the pool for a specific entity.
function migrateStakeToVanaPool(uint256 stakeId, uint256 entityId) external payable nonReentrant whenNotPaused {
dlpRootEpoch.createEpochs();
Stake storage stake = _stakes[stakeId];
if (stake.endBlock == 0) {
stake.movedAmount = stake.amount;
_closeStake(_msgSender(), stakeId);
stake.movedAmount = 0;
}
uint256 toClaim = stake.amount + _calculateStakeRewardUntilEpoch(stakeId, dlpRootEpoch.epochsCount() - 1, true);
if (toClaim == 0) {
revert NothingToClaim();
}
bool success = dlpRootRewardsTreasury.transferVana(payable(address(this)), toClaim);
if (!success) {
revert TransferFailed();
}
vanaPoolStaking.stake{value: toClaim}(entityId, stake.stakerAddress);
emit StakeMigratedToVanaPool(stakeId, stake.amount, entityId);
}However, it can be executed by anyone for any stakeId. Thus, allowing anyone to migrate stake of other users to any entity.
Recommendation
Consider adding a check to ensure that the migrateStakeToVanaPool(...) can only be executed by the owner of the stakeId.
| File | Fix commit | Type | Status |
VanaPoolEntityImplementation | — | Low | Acknowledged |
The VanaPoolEntityImplementation contract allows anyone to add rewards to be locked and distributed to stakers who have allocated their funds to a specific entity. The processRewards(...) function is periodically called to distribute these rewards. However, when an entity is removed via the removeEntity(...) function, the contract does not check for excessive locked rewards:
function removeEntity(uint256 entityId) external override whenNotPaused nonReentrant onlyEntityOwner(entityId) {
Entity storage entity = _entities[entityId];
if (entity.status != EntityStatus.Active) {
revert InvalidEntityStatus();
}
// Process any pending rewards
processRewards(entityId);
entity.status = EntityStatus.Removed;
_activeEntityIds.remove(entityId);
emit EntityStatusUpdated(entityId, EntityStatus.Removed);
}The entity may have an excess rewards that are not processed yet, and since there is no mechanism to handle any remaining undistributed rewards, these funds become permanently locked within the treasury contract once the entity is removed.
Recommendation
Consider moving any exessive rewards when removing an entity.
Resolution
We are aware of the issue and we didn't decide yet what to do with the excessive rewards.
removeEntity(...) does not clear entityNameToId| File | Fix commit | Type | Status |
VanaPoolEntityImplementation | — | Low | Acknowledged |
When creating or updating an entity, the contract checks that the entity name isn’t already used by verifying that the mapping value is zero. However, when an entity is removed via removeEntity(...), the mapping entry entityNameToId is not cleared:
function removeEntity(uint256 entityId) external override whenNotPaused nonReentrant onlyEntityOwner(entityId) {
Entity storage entity = _entities[entityId];
if (entity.status != EntityStatus.Active) {
revert InvalidEntityStatus();
}
// Process any pending rewards
processRewards(entityId);
entity.status = EntityStatus.Removed;
_activeEntityIds.remove(entityId);
emit EntityStatusUpdated(entityId, EntityStatus.Removed);
}Since the mapping entry for entityNameToId is not cleared, the removed entity’s name remains blocked from future use. This can be exploited by malicious users to monopolize desirable entity names by repeatedly creating and removing entities, thereby preventing legitimate users from registering those names.
Recommendation
Consider clearing the entityNameToId upon entity removal.
Resolution
This is an intended behavior.
| File | Fix commit | Type | Status |
VanaPoolStakingImplementation.sol | PR #13 | Info | Fixed |
The stake(...) function ensures that the amount staked is greater than or equal to a minimum threshold ( minStakeAmount).
function stake(uint256 entityId, address recipient) external payable override nonReentrant whenNotPaused {
// ...
uint256 stakeAmount = msg.value;
if (stakeAmount < minStakeAmount) {
revert InsufficientStakeAmount();
}
// ...
}However, the unstake(...) function does not have a similar check to ensure that after unstaking, the remaining stake is still above the minimum stake amount. This could result in a scenario where users stake an amount above the minimum, but after unstaking a portion, their remaining stake falls below the required minimum threshold.
Additionally, if a user had staked an amount above the minimum threshold and later tries to stake an amount smaller than the minimum, the transaction will revert, even though the user's total stake is still above the minimum.
Recommendation
Consider updating the check for the minimum stake amount in the stake(...) function to ensure it properly enforces the minimum threshold. Additionally, add a similar check in the unstake(...) function to ensure that users cannot have a stake less than the minimum required amount, unless they are unstaking all of their shares. This will help maintain consistency between the two functions and prevent users from inadvertently reducing their stake below the minimum threshold.
Resolution
Fixed by removing restrictions from stakers to stake below minStakeAmount in commit 97de94d
unstake(...) function allows owner to unstake below minRegistrationStake| File | Fix commit | Type | Status |
VanaPoolStakingImplementation.sol | PR #13 | Info | Fixed |
The unstake(...) function allows users to unstake a specified amount of shares and transfer equivalent amount of VANA tokens to the user. It also ensures that entity owner retains at least the minRegistrationStake remaining after unstaking.
function unstake(uint256 entityId, uint256 shareAmount) external override nonReentrant whenNotPaused {
// ...
// Get entity info from VanaPoolEntity
IVanaPoolEntity.EntityInfo memory entityInfo = vanaPoolEntity.entities(entityId);
// @audit `vanaToEntityShare(...)` rounds down the result
uint256 vanaToShare = vanaPoolEntity.vanaToEntityShare(entityId);
// ...
// If this is the entity owner, ensure they can't unstake below the registration stake
if (entityInfo.ownerAddress == _msgSender()) {
// @audit This results in an underestimated value
uint256 ownerMinShares = (vanaToShare * vanaPoolEntity.minRegistrationStake()) / 1e18;
if (stakerShares - shareAmount < ownerMinShares) {
revert CannotRemoveRegistrationStake();
}
}
// ...
}The function calculates the minimum shares that the owner should retain after unstaking by converting minRegistrationStake (in VANA tokens) to shares using the result of vanaToEntityShare(...). However, because vanaToEntityShare(...) rounds down the result, the calculated ownerMinShares is less than the actual required amount. This allows the owner to unstake more shares than intended, leaving them with fewer shares than required to meet the minRegistrationStake value.
Recommendation
Consider rounding up the result of vanaToShare when calculating ownerMinShares.
Alternatively, consider converting the remaining shares the owner will have after unstaking to VANA tokens by multiplying them with the result of entityShareToVana(...) and comparing that value with the minRegistrationStake.
migrateStakeToVanaPool(...) doesn't need to be payable| File | Fix commit | Type | Status |
DLPRootImplementation.sol | PR #13 | Info | Fixed |
The migrateStakeToVanaPool(...) function is designed to migrate stakes from the DLP system to the VANA pool. However, the function is marked as payable unnecessarily, even though it does not require native tokens to be sent.
function migrateStakeToVanaPool(uint256 stakeId, uint256 entityId) external payable nonReentrant whenNotPaused {}Recommendation
Consider removing the payable keyword from the function declaration.
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:
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.
The Vana team has provided a comprehensive walkthrough of the project. Moreover, the Vana team addressed all questions and concerns raised by the Nethermind Security team, providing valuable insights and a comprehensive understanding of the project's technical aspects.
| 39Tests passed | 0Failed |
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.
The Leading Engineers of Blockchain Infrastructure