Ventuals
vHYPE Staking

Covered the Genesis staking contracts and their HIP-3 successor: the vHYPE liquid staking token, batched withdrawals, and a slashing mechanism, across two review phases.
report id
0675
category
DeFi & Yield
language
Solidity
final report
October 8, 2025
lines of code
1097
Content

Download audit report

1

Executive Summary

This document presents the results of a security review conducted by Nethermind Security for Ventuals LST contracts.

Ventuals brings the power of perps to the most innovative private companies in the world, which is a multi-trillion dollar asset class that normal investors have historically been locked out of.

Ventuals is built on Hyperliquid's proven perps orderbook infrastructure, using the HIP-3 standard.

Ventuals created its own HYPE LST (vHYPE) to raise the HIP-3 stake requirement. vHYPE is a fully transferable ERC20 token, and serves as the claim to the original HYPE plus accrued native staking yield.

The security review was performed in two phases: an initial assessment of the Genesis contracts and a subsequent review of the version adapted for HIP-3.

The initial scope covered the Genesis phase contracts, which allowed users to stake HYPE tokens for a one-year lock-up. In exchange, users received vHYPE, a transferable ERC20 token representing their staked position. The protocol featured auto-compounding for all native staking rewards and was designed for future enhancements using a UUPS upgradeable proxy pattern.

The second phase of the review focused on significant updates, headlined by the introduction of the StakingVaultManager contract to replace the GenesisVaultManager. This new contract introduces a batched withdrawal system with a 7-day delay queue. Furthermore, a slashing mechanism was implemented that can affect both actively staked funds and assets in the withdrawal queue. These changes required corresponding modifications to the RoleRegistry, StakingVault, and VHYPE contracts.

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

Audit assessment
Audit TypeSecurity Review
Final ReportSeptember 23, 2025
New Version Final ReportOctober 8, 2025
Initial Commit2e57c08c0f
Final Commitd21f1a9402
New Version Initial Commit024ff6ef3d
New Version Final Commit522f247c7c
Documentation AssessmentHigh
Test Suite AssessmentHigh
Total findings
24
Fixed
24
Acknowledged
0
Mitigated
0
Unresolved
0

Findings by severity

  • Critical1
  • High2
  • Medium8
  • Low5
  • Informational6
  • Best Practices2

2

Audited Files

#ContractLoCCommentsRatioBlankTotal
1GenesisVaultManager.sol17012975.9%62361
2RoleRegistry.sol412253.7%1477
3Base.sol40615.0%1056
4StakingVault.sol551527.3%1888
5VHYPE.sol15213.3%421
6interfaces/IStakingVault.sol1728164.7%1459
7interfaces/ICoreWriter.sol4125.0%16
8libraries/CoreWriterLibrary.sol422457.1%1076
9libraries/Converters.sol10330.0%316
10libraries/L1ReadLibrary.sol21831.4%32253
Total61223338.1%1681013

The new version includes file StakingVaultManager.sol which was introduced in the place of GenesisVaultManager.sol.

#ContractLoCCommentsRatioBlankTotal
1StakingVaultManager.sol38823660.8%157781
Total38823660.8%157781

Additionally, the new version introduced the following number of line changes to existing files:

#ContractChanged LoC
1RoleRegistry.sol4
2StakingVault.sol82
3VHYPE.sol11

4

System Overview

The Ventuals LST protocol is a multi-contract system designed to operate across two distinct layers of the Hyperliquid network.

  • HyperEVM(L2): This layer hosts the smart contracts
  • HyperCore(L1): This layer handles the native HYPE staking with the validators.

The main data flow involves HYPE tokens moving from a user's wallet on HyperEVM to the StakingVault contract, then being bridged to HyperCore where they are delegated to validators. Staking rewards accumulated on HyperCore, increasing the total HYPE held by the vault, which in turn appreciates the value of the vHYPE token.

Ventuals LST overview
Fig. 2: Ventuals LST overview

4.1 Core Components

Below is an overview of smart contracts and their functionality in the protocol.

4.1.1 GenesisVaultManager

  • a) Purpose: Is the primary entry point for users to stake Hype tokens. It manages key state variables related to the capacity of the protocol, deposits at the user level, and the exchange rate for Hype to vHype conversion.
  • b) State:
    • totalVaultCapacity: The maximum amount of Hype the protocol can accept.
    • individualDepositLimit: The maximum Hype a single user can deposit.
    • currentExchangeRate: The calculated ratio of HYPE to vHYPE. This is a dynamic value derived from the total HYPE in the system relative to the total supply of vHYPE.
  • c) Functionality:
    • deposit: The main public function to accept Hype deposits from users.
    • It interacts with StakingVault to receive the deposited HYPE and with the vHYPE contract to mint the new tokens.

4.1.2 vHYPE

  • a) Purpose: A standard ERC20 implementation with additional logic for minting and burning. It represents a proportional share of the staked HYPE.
  • b) Mechanism of Value Appreciation: As staking rewards on HyperCore increase the HYPE balance in the StakingVault, the numerator of the ratio increases while the denominator (vHYPE supply) remains constant, thus increasing the value of each vHYPE token.
  • c) Minting: The mint function is permissioned and can only be called by the GenesisVaultManager or a privileged Manager role.

4.1.3 StakingVault

  • a) Purpose: A specialized contract that acts as the secure holder of deposited HYPE and the main communication layer between HypeEVM L2 and HyperCore's L1 precompiled contracts.
  • b) L1-L2 Bridge: It utilizes HyperEVM's precompiled contracts to execute actions on HyperCore. This is a critical architectural detail that enables cross-layer communication without complex bridging protocols.
  • c) Key Functionality:
    • transferHypeToCore: Transfers HYPE from the vault's HyperEVM balance to its associated spot account on HyperCore.
    • stakingDeposit: Uses a HyperCore precompiled function to move HYPE from the spot account to a staking account on L1.
    • tokenDelegate : A key function that interacts directly with HyperCore's precompiled contracts to delegate or undelegate HYPE to a specific validator. This is how staking is initiated and managed.
    • stakingWithdraw: Initiates the withdrawal process from the staking account on HyperCore.

4.1.4 RoleRegistry

  • a) Purpose: A centralized, modular access control system based on an address => role mapping. Isolates the access control logic from the business logic.
  • b) Roles and Permissions:
    • Owner: The highest level of control. Can manage roles, and pause/unpause contracts.
    • Manager: A highly privileged role with direct control over critical vault functions such as minting vHYPE and initiating staking/unstaking processes on HyperCore.
    • Operator: A more limited role, typically for automated or off-chain agents. Their primary function is to call transferToCoreAndDelegate to move HYPE from the vault to the L1 staking system.
  • c) Mechanism: Functions in other contracts (e.g., StakingVault) use modifiers like onlyManager or onlyOperator to ensure that only addresses with the correct role can call them, as defined by the RoleRegistry.

4.2 Interactions and Data Flow

4.2.1 User Deposit

  • A user calls GenesisVaultManager's deposit function.
  • GenesisVaultManager performs a token transfer of HYPE from the user to the StakingVault contract.
  • mints vHYPE based on the current exchange rate and sends them to the user.

4.2.2 HYPE Delegation

  • transferToCoreAndDelegate function moves the HYPE from the L2 contract balance to the L1 spot account.
  • calls stakingDeposit to move the tokens to the L1 staking account.
  • Finally, it calls tokenDelegate to delegate the HYPE to a validator on HyperCore.

4.2.3 Reward Accumulation

  • Staking rewards are generated on the HyperCore L1 and automatically increase the HYPE balance in the StakingVault's staking account.
  • This balance change is not a direct L2 state update but is reflected in the total HYPE when GenesisVaultManager calculates the exchange rate, thereby increasing the value of vHYPE for all holders.

4.3 Smart Contracts in New Version

Since the initial audit, the Ventuals LST protocol has undergone significant architectural changes that transform the system capabilities and user experience. The most notable change is the complete replacement of the GenesisVaultManager with a new StakingVaultManager contract, which introduces a batch-based withdrawal system and removes several previous limitations.

4.3.1 StakingVaultManager (Replaces GenesisVaultManager)

  • Role: Serves as the primary entry point for users to stake HYPE tokens, replacing the previous GenesisVaultManager.
  • Key Additions: Introduces a batch-based withdrawal mechanism while maintaining the core deposit functionality.
  • Simplifications: Removes complex capacity management and individual deposit limits; users can deposit HYPE without constraints.
  • State Tracking: Manages withdrawal batches, their processing states, and reserved HYPE amounts for pending withdrawals for a more robust and user-friendly experience.

4.3.2 New Withdrawal Architecture

  • Batching Model:
    • Queues user withdrawal requests.
    • Processes them in batches based on available capacity.
    • Imposes a 7-day waiting period between batch finalization and claimability.
    • Supports slashing events via retroactive exchange-rate adjustments.
  • Dynamic Capacity: Calculates available withdrawal capacity from the current total balance minus the minimum stake balance and already processed withdrawals.
  • Enhanced Emergency Controls (Owner):
    • Pause batch processing.
    • Reset batches prior to finalization.
    • Apply slashing events to specific batches.
    • Execute emergency staking withdrawals and deposits.

4.3.3 Updated Flow

User Deposit (Simplified)

  • User calls StakingVaultManager.deposit() with HYPE.
  • Contract mints vHYPE based on the current exchange rate.
  • HYPE is transferred to the StakingVault contract.
  • No capacity limits or individual deposit limits are enforced.

User Withdrawal (New Process)

  • User calls queueWithdraw() with the desired vHYPE amount.
  • vHYPE is transferred from the user to the StakingVaultManager contract.
  • Withdrawal request is added to the withdrawal queue.
  • Operator processes withdrawal batches using processBatch().
  • Batches are finalized using finalizeBatch(), which nets deposits and withdrawals.
  • After 7 days, users can claim their withdrawals using claimWithdraw().

HYPE Delegation (Enhanced)

  • The system preserves the core delegation mechanism via the StakingVault.
  • Delegation now occurs as part of the batch finalization process.
  • The system automatically stakes excess deposits and unstakes amounts required for withdrawals.

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 Exchange Rate Manipulation Leading to Inflation Attack

The HYPETovHYPE function in the GenesisVaultManager contract is vulnerable to an inflation attack due to exchange rate manipulation. The exchange rate is calculated as totalHYPE / totalVHYPE, which can be artificially inflated by donating directly to the vault without minting corresponding shares.

An attacker can exploit this by: (1) depositing a minimal amount (1 wei) to receive 1 vHYPE share, (2) donating a large amount (e.g., 10 ETH) directly to the staking vault via the receive() function, (3) inflating the exchange rate to approximately (10 ETH * 1e18) / 1 wei ≈ 10^22, and (4) causing victim deposits to result in 0 vHYPE shares due to integer division truncation when hypeAmount * 1e18 < _exchangeRate in the Math.mulDiv(hypeAmount, 1e18, _exchangeRate) calculation.

This allows the attacker to gain control of all vault assets with minimal investment while legitimate depositors receive 0 vHYPE shares despite depositing significant amounts, resulting in a complete loss of funds for victims and making the vault unusable for legitimate users.

Recommendation

Use internal accounting to keep track of all the HYPE in the vault with a storage variable and update it when the contract receives HYPE instead of using .balance.

Resolution

Fixed by 9007e61d16 and f5da5068c2

6.2 Not burning vHype on transfer

On depositing Hype tokens into the GenesisVaultManager contract, the tokens are moved into the StakingVault. At this time, the vHype tokens are minted for the accepted amount.

From the StakingVault, the manager can transfer Hype to any other account by calling the transferHype function, which will move the funds out of the protocol.

While Hype tokens are taken out of the protocol, the extent of vHype is not burnt.

While computing the exchange rate, the totalBalance for Hype comprises staking Balance + Spot Balance + funds in the StakingVault contract.

function exchangeRate() public view returns (uint256) {
==> uint256 balance = totalBalance();
==> uint256 totalSupply = vHYPE.totalSupply();
//....
return Math.mulDiv(balance, 1e18, totalSupply);
}

As the vHype was not burnt when taking out funds from StakingVault, it will directly impact the exchange rate.

Recommendation

Consider removing the transferHype function or burn equivalent vHYPE shares on transfer.

Resolution

Removed the transferHype function a02ad2aa62

6.3 Decimal Conversion Loss for Deposited Amounts

FileFix commitTypeStatus
src/GenesisVaultManager.sol9007e61MediumFixed

The system suffers from decimal conversion loss when handling HYPE deposits due to the conversion between 18 decimals (EVM standard) and 8 decimals (HyperCore standard).

The Converters.to8Decimals() function performs a division by 1e10 to convert from 18 decimals to 8 decimals: This division operation truncates any remainder less than 1e10 wei, resulting in permanent loss of precision. For example:

  • A deposit of 1.0000000001 HYPE (1e18 + 100 wei) would be converted to 1 HYPE (1e8) on HyperCore;
  • The 100 wei remainder is permanently lost;
  • When converted back to 18 decimals, it becomes 1.0000000000 HYPE (1e18), losing the original 100 wei;

Recommendation

Enforce minimum deposit granularity by a validation in the deposit() function to ensure only multiples of the minimum precision unit can be deposited (amountToDeposit % 1e10 == 0). Besides, add a minimum deposit amount check to prevent dust deposits that would result in significant relative precision loss.

Resolution

Added minimum deposit constraint 9007e61d1

6.4 Manager role to be restricted to GenesisVaultManager

In the StakingVault there are functions that interact with HyperCore. For the actions performed on HyperCore, the updated state does not reflect during reads via pre-compiled contracts until the next block. Hence, any read before the formation of the next block could result in incorrect values.

Currently, in order to prevent such a read, the tracking variable for the last action is in the GenesisVaultManager contract. If the manager role is configured for GenesisVaultManager and another EOA account, there is a possibility for EOA to interact with HyperCore in the StakingVault, resulting in changes to HyperCore state in transit.

For example, considering HYPE can be deposited directly into the StakingVault, if the intent is to send those funds to any of the below actions interacting with HyperCore, it will impact the working of the protocol.

function tokenDelegate(address validator, uint64 weiAmount, bool isUndelegate) external onlyManager whenNotPaused {
CoreWriterLibrary.tokenDelegate(validator, weiAmount, isUndelegate);
}


/// @inheritdoc IStakingVault
function spotSend(address destination, uint64 token, uint64 weiAmount) external onlyManager whenNotPaused {
CoreWriterLibrary.spotSend(destination, token, weiAmount);
}


/// @inheritdoc IStakingVault
function transferHypeToCore(uint256 amount) external onlyManager whenNotPaused {
_transfer(payable(HYPE_SYSTEM_ADDRESS), amount);
}

As the protection implemented in GenesisVaultManager is not effective in StakingVault, it could result in incorrect exchange rates.

Recommendation

Consider enforcing the Manager role to GenesisVaultManager during the genesis phase which is the intent. An improvement to consider is to move the _transferToCoreAndDelegate internal function into StakingVault so that interactions between stakingVault and HyperCore will be effective even after the genesis phase.

Resolution

As discussed, moved the lastEvmToCoreTransferBlockNumber logic to StakingVault so that the one-block delay is enforced in the StakingVault. 6c4f718eff

6.5 Hype tokens deposited directly into GenesisVault Manager are stuck

FileFix commitTypeStatus
src/GenesisVaultManager.sol0d6903eLowFixed

GenesisVaultManager can accept Hype tokens directly as the contract implements receive and fallback functions. While the Hype accepted via deposit function, equivalent vHype is minted and tokens are moved to the stakingVault, the directly deposited will remain in the GenesisVaultManager contract.

As there is no function to withdraw the hype tokens from GenesisVaultManager contract, any deposits directly sent to the contract will be stuck and locked forever.

Recommendation

Consider removing the receive and fallback functions

Resolution

Removed the receive and fallback functions 0d6903e09914

6.6 Malicious user can inflate the price of vHype

FileFix commitTypeStatus
src/GenesisVaultManager.sol
src/VHYPE.sol
5d78a55LowFixed

User can deposit Hype tokens into GenesisVaultManager contract, against which he will receive VHYPE tokens. The user can burn his vHYPE tokens at his discretion by calling burn on the Token contract.

Burning the token will impact the totalSupply as the supply will reduce to the extent of tokens burnt. This will directly inflate the value of VHYPE tokens.

Recommendation

Consider removing the burn function.

Resolution

Not a bug per-se, since if a user intentionally burned their tokens, the exchange rate *should* get inflated. There isn't a way for an attacker to exploit this, since they would just end up losing their claim on the underlying HYPE in the net.

However, we discussed removing this burn function to avoid accidental burns and reduce the surface area of bugs. Removed in this PR 5d78a553ac7

6.7 _mint() can be called when the contract is paused

FileFix commitTypeStatus
src/VHYPE.sol5d4a5c7LowFixed

The Base contract has a whenNotPaused modifier that is used to prevent certain actions when the contract is paused. The VHYPE contract inherits from Base and has a mint function. The mint function does not have whenNotPaused and hence VHYPE tokens can be minted even when VHYPE contract is paused.

function mint(address to, uint256 amount) public onlyManager {
_mint(to, amount);
}

Recommendation

Consider adding the whenNotPaused modifier to the mint function. This would prevent new tokens from being minted when the contract is paused, ensuring consistent behavior across both public and internal minting methods.

Resolution

Added whenNotPaused to mint() 5d4a5c7e88d

6.8 Event log can be cluttered with 0 deposits

FileFix commitTypeStatus
src/GenesisVaultManager.sol9007e61InfoFixed

The deposit function can be called with 0 HYPE. As the deposit function does not check for msg.value to be greater than 0, the function will execute and generate a deposit event while consuming gas.

function deposit() public payable canDeposit whenNotPaused {
uint256 requestedDepositAmount = msg.value;// if msg.value is 0
//...


==> emit Deposit(
msg.sender, /* depositor */
amountToMint, /* minted */
amountToDeposit, /* deposited */
requestedDepositAmount - amountToDeposit /* refunded */
);
}

Recommendation

Consider adding a validation for msg.value to be greater than 0 or preferably a minimum deposit amount.

Resolution

Fixed by minimum deposit limits 9007e61d162

6.9 Inconsistent comment for weiAmount conversion in CoreWriterLibrary

FileFix commitTypeStatus
src/lib/CoreWriterLibrary.sol608e7fdInfoFixed

The CoreWriterLibrary contract contains an inconsistent comment that states that weiAmount should be converted to 6 decimals before being passed to its functions. However, the rest of the protocol, including the GenesisVaultManager contract and the function-level Natspec comments, consistently uses 8 decimals for HYPE amounts sent to HyperCore. This discrepancy can lead to confusion.

The misleading comment is found in the contract-level NatSpec as below.

/// All weiAmounts must be converted to 6 decimals before calling these functions.
// ...
library CoreWriterLibrary {
// ...
}

Recommendation

Consider updating the CoreWriterLibrary contract's NatSpec comment to reflect the correct decimal conversion

Resolution

Fixed 608e7fd69e4

6.10 emergencyStakingWithdraw may fail

FileFix commitTypeStatus
src/GenesisVaultManager.solf045057InfoFixed

In the GenesisVaultManager contract, the funds moved to the HyperCore are delegated to a defaultVault.

The delegations to a particular validator have a lockup period of 1 day. Only after this lockup, delegations may be partially or fully undelegated at any time.

In the current implementation, the funds are delegated to the defaultVault.

If there arises a scenario where there is a need for emergencyStakingWithdraw, the earliest undelegate request can be submitted only after passing of 1 day from the last deposit.

function emergencyStakingWithdraw(uint256 amount, string calldata purpose) external onlyOwner {
//...


==> // Immediately undelegate HYPE
// @audit, the undelagate can be called 1 day after
// the last deposit.
stakingVault.tokenDelegate(defaultValidator, amount.to8Decimals(), true);


//...
}

Recommendation

Consider error handling in case emergencyStakingWithdraw does not work due to recent delegation to the validator.

Resolution

Addressed in this f045057de3

6.11 whitelistDepositLimits should be greater than current defaultDepositLimit

FileFix commitTypeStatus
src/GenesisVaultManager.sol19c95b5InfoFixed

When setting the whitelistDepositLimit for an address, the limit should be greater than the current defaultDepositLimit.

function setDefaultDepositLimit(uint256 _defaultDepositLimit) public onlyOwner {
defaultDepositLimit = _defaultDepositLimit;
}


function setWhitelistDepositLimit(address depositor, uint256 limit) public onlyOwner {
whitelistDepositLimits[depositor] = limit;
}

Recommendation

Consider adding a validation to check the limit to be greater than defaultDepositLimit.

Resolution

Going to leave behavior as-is, but updated the comment to avoid confusion 19c95b5798

6.12 Upgradeable contract lacks storage gaps

FileFix commitTypeStatus
src/Base.sol
*
0231aeeBest PracticesFixed

The protocol implements a Base contract using UUPS upgradeable proxy pattern. The common functionality is implemented in the Base contract, which is extended by other contracts in the protocol.

As the protocol follows the traditional storage for state, the storage layout is defined. In the future, if the Base contract needs to be extended to support more common features with state, it will not be possible. The absence of storage gaps removes the flexibility to insert variables in future upgrades without causing storage collisions.

Recommendation

Consider adding storage gaps to provision future extension in functionality.

Resolution

Fixed 0231aee978de

Issues in the new version

6.13 Withdrawals can be claimed before finalisation

FileFix commitTypeStatus
StakingVaultManager.sol—CriticalFixed

The function claimWithdraw() performs following checks:

function claimWithdraw(uint256 withdrawId, address destination) public whenNotPaused {
Withdraw storage withdraw = withdraws[withdrawId];
require(msg.sender == withdraw.account, NotAuthorized());
require(!withdraw.cancelled, WithdrawCancelled());
require(!withdraw.claimed, WithdrawClaimed());

Batch memory batch = batches[withdraw.batchIndex];
require(block.timestamp > batch.finalizedAt + 7 days, WithdrawUnclaimable()); // TODO: Should we add a buffer?

uint256 withdrawExchangeRate = batch.slashed ? batch.slashedExchangeRate : batch.snapshotExchangeRate;
uint256 hypeAmount = _vHYPEtoHYPE(withdraw.vhypeAmount, withdrawExchangeRate);
...
}

The function, however, does not check if the withdraw.batchIndex is not type(uint256).max. Also, the requirement block.timestamp > batch.finalizedAt + 7 days does not take into consideration that batch.finalizedAt with value 0 would pass. When the request is processed but the batch is not finalised, the request would contain the current batchIndex and the batch would contain snapshotExchangeRate, but the finalizedAt would be 0. This allows users to claim funds at the snapshotExchangeRate without the 7-day waiting period. In consequence, the calculations in the protocol would be incorrect, leading to loss of funds.

Recommendation

Consider preventing claiming in waiting period by checking if the withdraw.batchIndex is not type(uint256).max and that batch.finalizedAt is not 0.

Resolution

Fixed 8def0bb

6.14 Lack of pause for burnFrom()

FileFix commitTypeStatus
VHYPE.sol—MediumFixed

The burnFrom() function in VHYPE does not contain the pause check.

Recommendation

Consider introducing the pause check to VHYPE.burnFrom().

Resolution

Fixed 92b7aeb

6.15 Lack of withdrawal request minimum amount can lead to DOS

FileFix commitTypeStatus
StakingVaultManager.sol—MediumFixed

The function queueWithdraw() allows user to request withdrawal of any amount of vHYP greater than 0. A malicious user can create requests with a small amount of vHYPE to temporarily DOS the batch finalisation. The finalisation can only be done if there are no withdrawal requests in the queue that could be fulfilled with available funds, which is checked in _canFinalizeBatch(). A malicious user could create a withdrawal request with a small amount before the finalisation, to add another request that can be fulfilled, effectively stopping the finalisation. Moreover, users could add many small-value requests to disincentivise calling processBatch() due to high gas cost.

Recommendation

Consider defining minimum withdrawal request value.

Resolution

Fixed 10e585a and 0847334

6.16 Missing Validator address validation

FileFix commitTypeStatus
StakingVault.sol
StakingVaultManager.sol
—MediumFixed

The stake and tokenRedelegate functions do not validate validator addresses. They will not fail with validator == address(0) or any invalid address, which could lead to silent errors since the functions will not revert but may fail silently during delegation operations.

Recommendation

Add validation to ensure validator addresses are not zero addresses and consider implementing a whitelist of allowed validators.

Resolution

Fixed 39e9bc8

6.17 No limit on withdrawal request

FileFix commitTypeStatus
StakingVaultManager.sol—MediumFixed

Users can request withdrawal of any amount. The requests are processed in a queue. Withdrawn amount is limited by the HyperCore staking restriction, at least 500k HYPE tokens (initially) must be staked to maintain the perpetuals DEX deployment. This is checked in StakingVaultManager with minimumStakeBalance in the processBatch() function. However, there is a scenario where a single user requests a huge amount of tokens to withdraw. In such a case, there must be enough funds available to process the request of that user and all the subsequent requests. If that amount is really high, the withdrawals may be blocked for a substantial amount of time.

Recommendation

Consider introducing maximum limit for withdrawal request. This would incentivise users to withdraw in smaller portions and allow continuous withdrawals.

Resolution

Fixed 582641b. The fix is implemented such that we will automatically split a large withdraw request into smaller withdraws

6.18 Problems with applySlash() after claiming withdrawals

FileFix commitTypeStatus
StakingVaultManager.sol—MediumFixed

When the slashing happens, admins of the vHype protocol call the applySlash(). However, if this function is applied too late, users can claim the withdrawal funds from the affected batches. This may result in two problems:

  • users can claim their withdrawals at the original (higher) exchange rate before slashing is applied, allowing them to circumvent slashing penalties;
  • during the claiming the totalHypeClaimed is increased, but the applySlash() adjusts only the totalHypeProcessed according to the new slashedExchangeRate. This may result in totalHypeClaimed > totalHypeProcessed which would cause revery in totalBalance() since reservedHypeForWithdraws = totalHypeProcessed - totalHypeClaimed would result in underflow;

Note that the expected scenario is that the admins would react immediately to the slash event and call applySlash() only on the affected batches.

Recommendation(s): To ensure that no claiming happens between the actual slash event and calling applySlash(), consider introducing a two-step mechanism, which would first apply the slash or not, and only after allow claiming.

Resolution

Fixed ee2d169

6.19 Truncation during HyperEVM to HyperCore transfer

FileFix commitTypeStatus
StakingVaultManager.sol—MediumFixed

The truncation is not applied during the transfer of funds to the spot account in finaliseBatch(). This results in potential loss of 1e10 wei during every deposit.

function finalizeBatch() external whenNotPaused whenBatchProcessingNotPaused {
...

// Always transfer the full deposit amount to HyperCore spot
if (depositsInBatch > 0) {
stakingVault.transferHypeToCore(depositsInBatch);
}
...
}

The depositsInBatch will be truncated during transfer to HyperCore, but the whole un-truncated amount will be deducted from the StakingVault.

Recommendation

Consider applying restriction of precision less or equal to 1e8. This can be applied by adding a check to enforce amount % 1e10 == 0.

Resolution

Fixed 66961ad

6.20 Unclear error message when claiming unprocessed withdraw

FileFix commitTypeStatus
StakingVaultManager.sol—LowFixed

When claimWithdraw is called for a withdrawal that hasn't been processed yet (batchIndex is still type(uint256).max), the function doesn't fail early with a clear error message. Instead, it proceeds through all validation checks and only fails when reaching spotSend() because the balance will be 0, resulting in an unclear error message that doesn't indicate the withdrawal hasn't been processed yet.

Recommendation

Add explicit validation to check that withdraw.batchIndex != type(uint256).max in claimWithdraw and provide a clear error message if the withdrawal hasn't been processed yet.

Resolution

Fixed 8def0bb

6.21 Validation logic should be moved to StakingVault

FileFix commitTypeStatus
StakingVaultManager.sol—LowFixed

The validation checks for Core user existence and spot balance are performed in StakingVaultManager::claimWithdraw() before calling StakingVault::spotSend(). These checks should be moved to the StakingVault::spotSend() function to ensure that if/when StakingVaultManager is upgraded, the essential validation checks are still in place since they are critical for StakingVault functionality.

Recommendation

Move the Core user existence and spot balance validation checks to the StakingVault::spotSend() function to centralize validation logic.

Resolution

Fixed 9b9cd8a and 3bc3b57

6.22 Unclear timing logic in delegation reading

FileFix commitTypeStatus
StakingVault.sol—InfoFixed

The timing constraint logic uses block.number > lastDelegationChangeBlockNumber[_validator] + 1 in the block number comparison, but it's unclear why this additional block is required. This creates inconsistency with other timing checks in the contract that don't use + 1 like in the deposit function, where we have block.number > lastEvmToCoreTransferBlockNumber.

Recommendation

Clarify the reasoning for the + 1 requirement in the documentation or remove it for consistency.

Resolution

Fixed b59f8a9

6.23 minimumStakeBalance changes can temporarily block batch finalization

FileFix commitTypeStatus
StakingVaultManager.sol—InfoFixed

Increasing minimumStakeBalance after processing a batch but before finalization can temporarily prevent batch finalization if the new minimum makes balance < minimumStakeBalance + hypeProcessed. However, this can be resolved by lowering minimumStakeBalance again or using the resetBatch function.

Recommendation

Consider adding a check in the setMinimumStakeBalance function to ensure that the new minimum is not greater than the current balance if a batch is already processing.

Resolution

Fixed 65102fd

6.24 Increase timing checks

FileFix commitTypeStatus
StakingVaultManager.sol—Best PracticesFixed

There are two time checks in the manager: (1) a 7-day withdrawal queue and (2) a 1-day unstake lockup after stake. Those checks mirror the mechanisms on HyperCore. It is recommended to increase those checks slightly to ensure that the calls won't result in silent failures, which may cause loss of funds.

Recommendation

Consider increasing time checks to ensure correct behaviour. Additionally, consider increasing the block separation time to be greater than 1.

Resolution

Fixed d7fea7e

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 Ventuals protocol documentation provides a comprehensive overview of the liquid staking token (LST) system built on Hyperliquid. The documentation effectively covers both the current implementation (Phase 1) and future planned features (Phase 2), providing appropriate context for the current audit. The team provided an overview of the main system components during the kick-off call with a detailed explanation of the intended functionalities and was responsive to questions during the review.

All the relevant issues raised by the AuditAgent have been incorporated into this report. 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
234Tests 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.