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 performance fees; Keepers deploy idle assets according to strategies into Aave, ERC-4626 vaults, Morpho, or HyperEVM / HyperCore.

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 fee shares minting.
Standard Strategies AaveV3Strategy, ERC4626Strategy, MorphoStrategy Deploys vault assets to external yield sources.
Cross-chain Strategy HyperStrategy Source chain HyperEVM CCTP V2 strategy responsible for cross-chain burn/mint, remote state synchronization, and divest requests.
HyperEVM Execution HyperCoreAllocator, HyperCoreRouter HyperEVM side receives assets, mints internal shares, allocates to HyperCore/HLP, and handles cross-chain redemptions.

Roles

All custom roles are defined in the Roles library in src/libraries/Constants.sol. The system utilizes OpenZeppelin's AccessControl or AccessControlUpgradeable.

Role Constant Scope Responsibility
Admin DEFAULT_ADMIN_ROLE Controller, Vault, Strategy, Allocator, Router Authorizes other roles, upgrades UUPS contracts, sets manager, confirms NAV, manages role grants for vault/strategy.
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.
Guardian Roles.GUARDIAN Vault, Controller Emergency operations: pause, unpause, permanent shutdown, and forced exit of all strategies.
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 --> 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))

idleAssets() means tokens physically held by the vault contract. Strategy assets are counted through each registered strategy's totalAssets().

Dual Pricing

Vault share conversion compares the live ERC-4626 spot value with the confirmed Controller mark-to-market values.

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.