Skip to content

Architecture Overview

King's Vault V2 is an ERC-4626-based DeFi asset management system. Investors deposit assets into a Vault to receive vault shares; the operations team uses the Controller to manage vaults, strategies, NAV settlement, and configurable performance fee accounting; Keepers deploy idle assets according to strategies into Aave, ERC-4626 vaults, Morpho, or HyperEVM / HyperCore. The current performance fee rate for Ethereum Network Lending, AAVE GHO Saving, and Hyperliquidity Provider (HLP)—the V2 portals documented here—is 0%.

Lending and GHO Saving use separate KingsVaultV2 instances; HLP uses KingsVaultV2Async. The system is divided into six main layers:

Layer Contract Purpose
Vault KingsVaultV2 Synchronous ERC-4626 vault supporting deposits, withdrawals, dual-pricing conversion, liquidity waterfall replenishment, and emergency redemption.
Async Vault KingsVaultV2Async Asynchronous redemption version. Escrows shares during withdrawal, which are batch executed by a Keeper before the Investor can claim assets.
Controller Controller UUPS upgradeable central coordinator managing vault registration, strategy registry, fund routing, NAV propose/confirm, and conditional fee-share minting. Fee-share minting is inactive at the current 0% V2 portal fee rate.
Strategy Adapters AaveV3Strategy, ERC4626Strategy, MorphoStrategy; GHO Saving integration Deploys vault assets to lending and savings protocols on Ethereum.
Cross-chain Strategy HyperStrategy Ethereum-side CCTP V2 strategy responsible for cross-chain burn/mint, remote state synchronization, and asynchronous divest requests.
HyperEVM Execution HyperCoreAllocator, HyperCoreRouter HyperEVM side receives assets, mints internal shares, allocates to HyperCore/HLP, and handles cross-chain redemptions.

GHO Saving routes USDC → GHO → sGHO and reverses that route when underlying assets are needed for a withdrawal. Users hold King's Vault shares, while the strategy accounts for the net USDC recoverable from its sGHO position. This is an Aave savings integration, not an Umbrella staking position.

Roles

The documented V2 custom roles are defined in the Roles library in src/libraries/Constants.sol. The system utilizes OpenZeppelin's AccessControl or AccessControlUpgradeable. The following is an interface-level role summary, not proof of each deployed contract's current grants or upgrade authority; resolve any conflicting descriptions against the verified deployment before use.

Role Constant Scope Responsibility
Admin DEFAULT_ADMIN_ROLE Controller, Vault, Strategy, Allocator, Router Authorizes other roles, sets manager, confirms NAV, and manages role grants. Effective UUPS upgrade authority must be verified per deployment.
Developer Roles.DEVELOPER Controller, Allocator Registers vaults, adds/removes strategies, adds HyperCore routers.
Keeper Roles.KEEPER Controller, Async Vault, Strategy, Allocator, Router Executes operations: invest, divest, NAV propose, fee rate adjustments, CCTP relay, async executeRedeem, and HyperCore allocations. The current V2 portal fee rate is 0%.
Guardian Roles.GUARDIAN Vault, Controller Emergency operations: pause, unpause, permanent shutdown, and invocation of strategy exit paths; these do not guarantee remote asset recovery.
Investor Roles.INVESTOR Vault, Async Vault User operations: deposit, mint, withdraw, redeem, claim, emergencyRedeem.

Role Assignment Notes

Contract Initial Roles
Controller.initialize(owner, manager) owner receives DEFAULT_ADMIN_ROLE; manager is only the fee receiver, not automatically a role holder.
KingsVaultV2 constructor owner_ receives DEFAULT_ADMIN_ROLE and Roles.GUARDIAN; vault starts paused.
KingsVaultV2Async constructor Same as KingsVaultV2. Keeper role must be granted on the async vault for executeRedeem().
AaveV3Strategy, ERC4626Strategy, MorphoStrategy, HyperStrategy, HyperCoreRouter constructors owner_ receives DEFAULT_ADMIN_ROLE.
HyperCoreAllocator.initialize(...) owner_ receives DEFAULT_ADMIN_ROLE.

Important implementation detail: KingsVaultV2 does not use a Roles.CONTROLLER constant. Controller-only functions check msg.sender == CONTROLLER through the immutable CONTROLLER address.

For strategy execution through Controller.fundInvest(), Controller.fundDivest(), Controller.fundReplenishOnlyForVault(), and Controller.strategyRevoke(), the strategy sees msg.sender as the Controller contract. Therefore, the Controller proxy must be granted Roles.KEEPER on each concrete strategy that enforces onlyRole(Roles.KEEPER).

Architecture

flowchart TD
    Investor["Investor"] --> Vault["KingsVaultV2 / KingsVaultV2Async"]
    Admin["Admin"] --> Controller["Controller"]
    Developer["Developer"] --> Controller
    Keeper["Keeper"] --> Controller
    Guardian["Guardian"] --> Vault
    Guardian --> Controller
    Controller --> Vault
    Controller --> Strategies["Strategies"]
    Strategies --> Aave["Aave V3"]
    Strategies --> ERC4626["External ERC4626 Vault"]
    Strategies --> Saving["GHO Saving: USDC → GHO → sGHO"]
    Strategies --> HyperStrategy["HyperStrategy"]
    HyperStrategy --> CCTP["CCTP V2"]
    CCTP --> Allocator["HyperCoreAllocator"]
    Allocator --> Router["HyperCoreRouter"]
    Router --> HyperCore["HyperCore / HLP"]

Asset Accounting

KingsVaultV2.totalAssets() returns:

idleAssets() + Controller.totalStrategyAssets(address(this))

For the synchronous vault, idleAssets() means tokens physically held by the vault contract. The async variant excludes assets reserved for executed claims. Strategy assets are counted through each registered strategy's totalAssets(); for HyperStrategy this includes cached remote state, while Saving requires net USDC-recoverable valuation after applicable route costs. Reported NAV is not the same as immediately available withdrawal liquidity.

Dual Pricing

Vault share conversion compares the current ERC-4626 spot value, derived from strategy-reported assets, with the confirmed Controller mark-to-market values. "Spot" does not imply a fresh cross-chain read or a guaranteed executable exit quote.

Direction Function Path Code Behavior
Assets to shares _convertToShares(assets, rounding) Computes both spot shares and marked shares. If rounding is Ceil, returns the larger value. Otherwise returns the smaller value.
Shares to assets _convertToAssets(shares, rounding) Computes both spot assets and marked assets. If rounding is Ceil, returns the larger value. Otherwise returns the smaller value.

Because OpenZeppelin ERC-4626 uses different rounding modes for deposit, mint, withdraw, and redeem, the effective result depends on the user action:

User Action ERC-4626 Preview Effect
deposit(assets) assets to shares, floor mints the lower of spot/marked shares.
mint(shares) shares to assets, ceil requires the higher of spot/marked assets.
withdraw(assets) assets to shares, ceil burns the higher of spot/marked shares.
redeem(shares) shares to assets, floor pays the lower of spot/marked assets.