Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · October 2025

Protocol Review

for Nunchi

Guardian's review of Protocol Review for Nunchi, published October 2025. The report records 27 findings across 2 review rounds, including 4 high and 7 medium.

Published
Review window
September 17 to October 7, 2025
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Hyperliquid
Sector
Yield and vaults, Perpetuals
  • 0 Critical
  • 4 High
  • 7 Medium
  • 6 Low
  • 10 Informational

17 resolved · 10 acknowledged

Scope

9 files in scope · 691 nSLOC
FilenSLOCLines
src/tokens/GenesisVaultSYToken.sol154269
src/storage/GlobalStorage.sol66129
src/storage/Vault.sol212390
src/strategies/BaseGenesisStrategy.sol1943
src/strategies/NoYieldStrategy.sol4768
src/modules/ConfigurationModule.sol68145
src/modules/StrategyModule.sol3459
src/modules/VaultModule.sol61103
src/libraries/Errors.sol3046

Findings 27

Main Review

18 findings · September 17 to 22, 2025
  1. H-01 High Global Withdrawal Delay For SY Holders Logical Error Resolved
    Location
    src/storage/Vault.sol:112
    Round
    Main Review

    Description

    Users deposit underlying tokens through the SY wrapper, GenesisVaultSYToken. The wrapper calls deposit function on Vault module, where the logic records a cooldown timestamp for the caller address.

            // Update vault state
            vault.sharesOf[user] += shares;
            vault.totalShares += shares;
            vault.lastDepositTime[user] = block.timestamp;
    

    When the SY wrapper deposits, msg.sender is the SY contract address, so the vault stores one lastDepositTime for the entire wrapper. When anyone later redeems via the SY wrapper, the wrapper calls withdraw on VaultModule contract. That path enforces the withdrawal delay against the caller address.

            // Check withdrawal delay
            uint256 nextWithdrawalTime = vault.lastDepositTime[user] +
                vault.withdrawalDelay;
    
            if (vault.withdrawalDelay > 0) {
                if (nextWithdrawalTime > block.timestamp) {
                    revert Errors.WithdrawalDelayNotMet(
                        nextWithdrawalTime,
                        block.timestamp
                    );
                }
            }
    

    Because the cooldown is tracked once for the SY address (not per end‑user), any deposit via the SY wrapper resets lastDepositTime and pushes out the withdrawal window for every SY holder. An attacker can grief all SY redemptions by repeatedly depositing the minimum allowed amount just before the delay expires, creating a redemption DoS for all users who hold SY for that vault.

    Recommendation

    Move the cooldown enforcement to the SY wrapper on a per‑user basis. Track lastDepositTime[user] inside the SY contract and enforce the delay on redeem. Skip the further withdrawal delay validation for registered SY wrappers.

  2. H-02 High Share Price Inflation Attack Rounding Acknowledged
    Location
    src/modules/VaultModule.sol:23-49
    Round
    Main Review

    Description

    The protocol performs a check that the given user did not receive 0 shares and they have a configurable parameter to set a minDeposit amount for vaults. These security checks will decrease impact and likelihood but depending on the configs and the given situation a share price inflation attack may still be possible.

    Recommendation

    Be aware and consider to further mitigate against share price inflation attacks by implementing virtual shares or minting shares directly in the init flow of a new vault.

  3. H-03 High Cross Vault Drains In No-Strategy Vaults Logical Error Resolved
    Location
    src/storage/Vault.sol:301
    Round
    Main Review

    Description

    Vaults without a strategy route deposits to the Genesis Factory and price shares using getTotalAssets, which, in no‑strategy mode, returns the Factory’s entire balance of the vault’s liquidityToken.

        function getTotalAssets(
            Data storage vault
        ) internal view returns (uint256) {
            if (address(vault.strategy) == address(0)) {
                return IERC20(vault.liquidityToken).balanceOf(address(this));
            }
    
            return IGenesisStrategy(vault.strategy).totalAssets();
        }
    

    Because this balance is shared across all no‑strategy vaults that use the same token, a deposit into Vault B increases the Factory’s balance and immediately inflates getTotalAssets for Vault A. Conversions then use contaminated AUM value, letting redemptions in one vault withdraw assets funded by another, and mispricing new mints.

    Recommendation

    Use per‑vault custody. Track each vault assets and use it in getTotalAssets instead of the Factory balance.

  4. H-04 High Cross Vault Drains If Strategy Is Shared Logical Error Resolved
    Location
    Global
    Round
    Main Review

    Description

    Users are able to drain the factory if multiple vaults share the same strategy. The reason for this is that they both use the same summed up total amount for the share price calculation but a different totalShare value.

    For example:

    • Vault A is connected to Strategy A and 100 WETH is deposited
    • Vault B is created and connected to Strategy A
    • Both vaults share the same totalAssets amount (balanceOf the strategy contract)
    • Eve deposits 1 WETH into Vault B and therefore owns 100% of the shares of Vault B
    • Eve withdraws all of her shares and by doing so receives: assetsOut = vaultBSharesIn * summedUpAssets / totalVaultBShares = 1e18 * 101e18 / 1e18 = 101e18

    Recommendation

    Consider to enforce a 1 to 1 relationship between vaults and strategies.

  5. M-01 Medium emergencyRecover Function Is Unreachable Access Control Resolved
    Location
    src/tokens/GenesisVaultSYToken.sol:287-300
    Round
    Main Review

    Description

    The emergencyRecover function in the GenesisVaultSYToken contract is unreachable as it has a onlyFactory modifier. But there is no contract in the factory to call it.

    Recommendation

    Consider adding a function to the factor to be able to call it.

  6. M-02 Medium Missing Withdrawal Limit Validation Resolved
    Location
    src/storage/Vault.sol:100-125
    Round
    Main Review

    Description

    The AUDIT_SCOPE.md document talks about a Configurable deposit/withdrawal limits in the Key Features of the vault. However only a configurable deposit limit was implemented.

    Recommendation

    Consider to implement a configurable withdrawal limit too or adjust the docs accordingly.

  7. M-03 Medium SY Exchange Rate Mispricing Logical Error Resolved
    Location
    src/tokens/GenesisVaultSYToken.sol:91-99
    Round
    Main Review

    Description

    Users can deposit directly into the Genesis vault (VaultModule.deposit) or via the SY wrapper (GenesisVaultSYToken.deposit). When depositing via SY, the Genesis factory mints vault shares to the SY contract, and the SY contract mints an equal amount of ERC-20 tokens to the user. The SY token exposes an exchangeRate function that indicates the number of underlying assets per SY token.

        function totalAssets() public view returns (uint256) {
            return IVaultModule(factory).totalAssets(vaultId);
        }
    
        /// @notice Get the current exchange rate (assets per SY token)
        /// @return rate Exchange rate scaled by 1e18
        function exchangeRate() public view override returns (uint256 rate) {
            uint256 _totalSupply = totalSupply();
            if (_totalSupply == 0) {
                return 1e18; // 1:1 for empty vault
            }
    
            uint256 _totalAssets = totalAssets();
            return (_totalAssets * 1e18) / _totalSupply;
        }
    

    Because the vault allows direct deposits (EOAs mint vault shares that are not represented by SY supply), totalAssets includes all assets, while SY.totalSupply includes only SY-backed shares. As soon as non-SY shares are outstanding, the exchangeRate becomes inflated. Any off-chain valuation, UI or DEX logic that uses exchangeRate as a price proxy will be incorrect, which may open further attack scenarios.

    Recommendation

    Compute the SY exchange rate as the price per share, using the vault’s convertToAssets, so it reflects all outstanding shares (SY + direct holders).

    function exchangeRate() public view override returns (uint256) {
        uint256 rate = IVaultModule(factory).convertToAssets(vaultId, 1e18);
        return rate == 0 ? 1e18 : rate;
    }
    
  8. L-01 Low Strategy Yield Distribution Sandwich Attack MEV Acknowledged
    Location
    src/modules/VaultModule.sol:23-76
    Round
    Main Review

    Description

    A withdraw delay can be configured which reduces the likelihood but it may still be possible and profitable to front a yield distribution in the underlying vault of a strategy and deposit a lot of funds into the vault to gain most of the yield and exit right after the withdraw delay passed. So their may still be enough incentive for MEV here.

    Recommendation

    Be aware and consider one of these options:

    • Always configure the withdraw delay accordingly
    • Only pick vaults which do not have step-wise jumps in their share price value
    • Implement a mechanism that distributes yield over time
  9. L-02 Low Missing Event For Critical State Change Best Practices Resolved
    Location
    Global
    Round
    Main Review

    Description

    Multiple functions which perform critical state changes do no emit an event:

    • ConfigurationModule::transferOwnership
    • StrategyModule::setPerformanceFee

    Recommendation

    Consider emitting events in all functions which perform critical state changes to follow best practices.

  10. L-03 Low Harvest Flow May Be Capital Inefficient Rewards Acknowledged
    Location
    src/storage/Vault.sol:494-515
    Round
    Main Review

    Description

    The harvest flow is the following:

    • All of the accrued yield is withdrawn
    • The performance fee is collected
    • The rest of the yield is deposited back into the vault

    This is very capital inefficient if there is a withdraw and or deposit fee in the given vault.

    Recommendation

    Be aware and consider to not use vaults with deposit or withdraw fees or rewrite the logic in that case to only withdraw the performance fee.

  11. L-04 Low Missing Check In validateStrategy Validation Acknowledged
    Location
    src/storage/Vault.sol:131-142
    Round
    Main Review

    Description

    The validateStrategy function does not check if the given strategy contract uses address(this) as factory contract. Using a strategy with a different address set as factory could lead to DoS or other major issues.

    Recommendation

    Consider implementing this check.

  12. L-05 Low Wrong Events In SY Token Best Practices Resolved
    Location
    src/tokens/GenesisVaultSYToken.sol
    Round
    Main Review

    Description

    The GenesisVaultSYToken emits events which do not strictly follow the ERC-5115 SY token standard: https://eips.ethereum.org/EIPS/eip-5115

    Recommendation

    Consider to follow best practices and implement the same events as specified in the ERC-5115 SY Token standard.

  13. I-01 Informational Unsafe Operations Best Practices Resolved
    Location
    src/storage/Vault.sol:485
    Round
    Main Review

    Description

    There is a unsafe transfer call in the collectPerformanceFee function as well as unsafe approve operations globally.

    Recommendation

    Consider always using safeTransfer and safeApprove functions to follow best practices.

  14. I-02 Informational Unused Code Superfluous Code Resolved
    Location
    Global
    Round
    Main Review

    Description

    There is unused code in multiple parts of the system.

    Unused Errors:

    • NoFeeRecipient
    • NoFeeConfigured
    • OnlyRouter
    • InvalidAsset
    • InvalidRouter
    • InsufficientBalance

    Unused Imports:

    • IGenesisStrategy in the VaultModule contract
    • Errors in the GenesisVaultSYToken contract

    Recommendation

    Consider to remove unused code.

  15. I-03 Informational Variables Not Marked As Immutable Gas Optimization Resolved
    Location
    src/tokens/GenesisVaultSYToken.sol:22-28
    Round
    Main Review

    Description

    In the GenesisVaultSYToken contract, the vaultId, factory, and asset variables assigned in the constructor are never modified afterward. There are no setter functions or internal assignments that change these values post-deployment. Keeping them as regular public variables causes unnecessary storage reads at runtime, which consume gas, whereas declaring them immutable embeds the values in bytecode at deployment, eliminating storage access costs.

    Recommendation

    Declare the mentioned fields as immutable.

  16. I-04 Informational ConfigurationModule Initialization Frontrunning Acknowledged
    Location
    src/modules/ConfigurationModule.sol:20
    Round
    Main Review

    Description

    The initialize function, implemented in ConfigurationModule is external and lacks access control. The first caller permanently sets GlobalStorage.owner, after which further calls revert. If deployment and initialization are not atomic, an attacker can front-run the initialization and set themselves as owner, forcing redeployment.

    Recommendation

    Initialize the ConfigurationModule in the Genesis Factory’s constructor during deployment to prevent front-running.

  17. I-05 Informational Overly permissive Fee Bounds Best Practices Acknowledged
    Location
    src/storage/Vault.sol:157
    Round
    Main Review

    Description

    The validatePerformanceFee function only rejects rate greater than 10000 BPS, so a 100% performance fee is currently permitted.

    This is an overly permissive configuration that poses unexpected risk if set accidentally.

    Recommendation

    Enforce a sensible hard cap below 100% (e.g. 2000–3000 bps), and consider applying a time lock for fee increases.

  18. I-06 Informational Incompatibility With Non-Standard ERC20s Best Practices Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    The vault assumes standard ERC-20 behaviour - exact amount is received on transfer, balances change only by that amount and math can safely rely on the requested value. If the underlying token deviates from these assumptions, internal accounting can drift and downstream calls may operate on incorrect balances. Examples include fee-on-transfer or deflationary tokens that deliver fewer tokens than requested, rebasing tokens whose balances change asynchronously or ERC-777 hooks that may introduce the reentrancy risk.

    Because the implementation mints shares from the requested amount instead of the observed balance delta, it can over-mint shares and attempt to deposit more into the strategy than it actually holds, breaking internal accounting.

    Recommendation

    Accept only standard ERC-20 tokens or update the vault logic to correctly handle non-standard behaviours. Note that even commonly used tokens such as USDT or USDC could enable transfer fees in the future.

Remediation Review

9 findings · October 6 to 7, 2025
  1. M-01 Medium Strategy Switch Resets Performance Fee Rewards Acknowledged
    Location
    src/storage/Vault.sol:369
    Round
    Remediation Review

    Description

    The switchStrategy flow calls the unsetStrategy function to unset the old strategy. However this function also resets the performanceFeeRate and the feeRecipient.

    This may lead to lost revenue for the protocol.

    Recommendation

    Consider to not reset the performance fee configs or add a boolean if this is wished or not.

  2. M-02 Medium DoS Griefing Attack DoS Acknowledged
    Location
    src/tokens/GenesisVaultSYToken.sol:210
    Round
    Remediation Review

    Description

    There is a withdrawal delay based on when a user was the recipient of a deposit the last time. However an arbitrary address can be set as recipient of a deposit.

    This allows users to DoS other users from withdrawing their funds by depositing a small amount of funds and set the victim as recipient.

    Recommendation

    As long as the minDeposit value is high enough the chance that this happens is unlikely. But be aware and consider to make changes if necessary.

  3. M-03 Medium Withdraw Delay Can Be Avoided Validation Resolved
    Location
    src/tokens/GenesisVaultSYToken.sol
    Round
    Remediation Review

    Description

    The withdraw delay can be avoided by transferring the tokens to another EOA and withdrawing with this account instead as the check is performed for the last deposit of the given msg.sender.

    Recommendation

    Here are two possible solutions:

    • Make withdrawals a 2 step process with a delay in between
    • Increase the lastDepositTime of the recipient during a transfer and add a minTransfer amount to mitigate against another griefing DoS vector
  4. M-04 Medium Function Is Unreachable Access Control Resolved
    Location
    src/strategies/AAVEStrategy.sol:302-327
    Round
    Remediation Review

    Description

    The emergencyWithdraw function in the AAVEStrategy contract is unreachable as it has a onlyFactory modifier. But there is no contract in the factory to call it.

    Recommendation

    Consider adding a function to the factor to be able to call it.

  5. L-01 Low Mismatch In Migration Waiting Period Documentation Acknowledged
    Location
    src/modules/MigrationModule.sol:22
    Round
    Remediation Review

    Description

    The migration specification requires a 3-day waiting period before execution, but the implementation sets MIN_MIGRATION_DELAY = 1 days and applies migrationDelay == 0 ? MIN_MIGRATION_DELAY : migrationDelay. Additionally, GlobalStorage comments state the default is 3 days. This inconsistency can allow migrations to execute earlier than stakeholders expect, reducing the intended review/exit window.

    Recommendation

    Align code and documentation by setting MIN_MIGRATION_DELAY to 3 days.

  6. I-01 Informational Misleading Event In claimStrategyIncentives Events Resolved
    Location
    src/modules/StrategyModule.sol:136
    Round
    Remediation Review

    Description

    The claimStrategyIncentives function will pass and emit an StrategyIncentivesClaimed event even if no tokens were claimed.

    Recommendation

    Consider to revert in that case.

  7. I-02 Informational Unused Code Superfluous Code Resolved
    Location
    Global
    Round
    Remediation Review

    Description

    There is unused code in multiple parts of the system:

    • IERC165 import in the ConfigurationModule
    • validateVaultInteraction function in the GlobalStorage library
    • InsufficientBalance error in the AAVEStrategy contract
    • safeTransfer in the catch flow of the AAVEStrategy deposit function is redundant as the state will reset anyway on a revert

    Recommendation

    Consider to remove redundant code.

  8. I-03 Informational Unnecessary Try/Catch Blocks Superfluous Code Resolved
    Location
    AAVEStrategy.sol
    Round
    Remediation Review

    Description

    AAVEStrategy contract uses try/catch around Aave calls in deposit, withdraw, and withdrawAll functions, where failures should revert the whole transaction. This masks root causes by replacing Aave’s revert data with generic errors and adds bytecode size and complexity with no functional benefit.

    Recommendation

    Consider removing try/catch pattern from deposit, withdraw and withdrawAll functions.

  9. I-04 Informational Redundant Token Transfer Best Practices Resolved
    Location
    src/strategies/AAVEStrategy.sol:109
    Round
    Remediation Review

    Description

    In AAVEStrategy.deposit, after pulling tokens from the factory, the catch block calls safeTransfer and then reverts. Since the revert rolls back all state changes in this transaction, the transfer is redundant and adds an unnecessary external call.

    Recommendation

    Remove the safeTransfer in the catch block and revert directly.

More from Nunchi

  1. Migration

    20 findings5 high 20 findings: 5 high, 5 medium, 6 low, 4 informational
  2. SY Genesis Vaults

    6 findings2 high 6 findings: 2 high, 4 low
  3. Genesis Vaults Updates

    18 findings2 high 18 findings: 2 high, 9 medium, 5 low, 2 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote