Skip to main content
While SuperVault deployment is permissionless, only SuperVaults whose managers perform KYC and stake $UP in the Aggregator contract will be displayed on the Superform UI as valid vaults.
For a guided management experience, use the curator application at curators.superform.xyz. See the full Curator Documentation for the complete operational guide.
Once a manager becomes a validated curator, they gain full access to the tooling on the curators.superform.xyz site.

Creating and Setting Up a Vault

Vaults can be created in SuperVaultAggregator by calling createVault using the following parameters.
function createVault(VaultCreationParams calldata params) external
    returns (address superVault, address strategy, address escrow) {}

struct VaultCreationParams {
    address asset; // Address of the underlying asset
    string name; // Name of the vault token
    string symbol; // Symbol of the vault token
    address mainStrategist; // Address of the vault mainStrategist
    uint256 minUpdateInterval; // Minimum time interval between PPS updates
    uint256 maxStaleness; // Maximum time allowed between PPS updates before staleness
    FeeConfig feeConfig;
}

struct FeeConfig {
    uint256 managementFeeBps; // Management fee in basis points
    uint256 performanceFeeBps; // Performance fee in basis points
    address recipient; // Fee recipient address
}

Primary Managers

The primary manager has full control over strategy operations, including the list below as well as the functionality granted to secondary managers.
  • Create vaults
  • Update secondary managers
  • Pause and unpause strategies
  • Update PPS expiration thresholds
  • Add and remove authorized callers
  • Add hooks to the strategy hooks root
  • Update vault performance and management fees

Control of Primary Managers

If a primary manager is found to be malicious, they can be replaced by either the secondary managers or SUPER_GOVERNOR through the following functions of SuperVaultAggregator.
/// @notice Change the primary manager for a strategy
/// @dev Only SuperGovernor can call this function directly
/// @param strategy The strategy address
/// @param newManager The new primary manager address
/// @param feeRecipient The new fee recipient address
function changePrimaryManager(address strategy, address newManager, address feeRecipient) external;

/// @notice Proposes a change to the primary manager
/// @notice Caller can either be secondary or primary manager
/// @param strategy Address of the strategy
/// @param newManager Address of the proposed new primary manager
/// @param feeRecipient Address of the new fee recipient
function proposeChangePrimaryManager(address strategy, address newManager, address feeRecipient) external;

/// @notice Executes a previously proposed change to the primary manager after timelock
/// @param strategy Address of the strategy
function executeChangePrimaryManager(address strategy) external;

Secondary Managers

The primary manager can add multiple secondary managers as backup accounts who have partial control over strategy operations.
  • Skim performance fees
  • Whitelist yield sources
  • Update primary manager
  • Call executeHooks() to interact with underlying yield sources
  • Call executeHooks() to perform emergency withdrawals
  • Fulfill redeem requests
  • Fulfill cancel redeem requests
The list of secondary managers for a given strategy can be maintained as follows.
/// @notice Adds a secondary manager to a strategy
/// @notice Either primary or secondary managers can append secondary managers
/// @param strategy Address of the strategy
/// @param manager Address of the manager to add
function addSecondaryManager(address strategy, address manager) external;

/// @notice Removes a secondary manager from a strategy
/// @notice Only the primary manager can remove secondary managers
/// @param strategy Address of the strategy
/// @param manager Address of the manager to remove
function removeSecondaryManager(address strategy, address manager) external;

Upkeep Mechanism

Managers must deposit upkeep tokens to ensure that validators are compensated for the cost of updating onchain PPS for their vaults. This is done in SuperVaultAggregator. The upkeep token is configurable per chain ($UP on mainnet, WETH on L2s, etc.)
/// @notice Deposits upkeep tokens for strategy upkeep
/// @param strategy Address of the strategy to deposit for
/// @param amount Amount of upkeep tokens to deposit
function depositUpkeep(address strategy, uint256 amount) external;
Upkeep withdrawals are two-step and have a 24 hour timelock.
/// @notice Proposes withdrawal of upkeep tokens from strategy upkeep balance (starts 24h timelock)
/// @dev Only the main manager can propose. Withdraws full balance at time of proposal.
/// @param strategy Address of the strategy to withdraw from
function proposeWithdrawUpkeep(address strategy) external;

/// @notice Executes a pending upkeep withdrawal after 24h timelock
/// @dev Anyone can execute, but funds go to the main manager of the strategy
/// @param strategy Address of the strategy to withdraw from
function executeWithdrawUpkeep(address strategy) external;

Strategy Management

Once vaults are created, managers automatically benefit from the list of hooks that are in the global hooks root. However, if more functionality is to be added, primary managers can propose a new strategy hooks root.
function proposeStrategyHooksRoot(address strategy, bytes32 newRoot) external validStrategy(strategy)
Once the hooks required to perform a given yield strategy are available in the hooks root, the managers can run them through executeHooks(). The curators dashboard supports hook execution simulations. It is advised to run these before actual execution.
/// @notice Structure for hook execution arguments
struct ExecuteArgs {
    /// @notice Array of hooks to execute
    address[] hooks;
    /// @notice Calldata for each hook (must match hooks array length)
    bytes[] hookCalldata;
    /// @notice Expected output amounts or output shares
    uint256[] expectedAssetsOrSharesOut;
    /// @notice Global Merkle proofs for hook validation (must match hooks array length)
    bytes32[][] globalProofs;
    /// @notice Strategy-specific Merkle proofs for hook validation (must match hooks array length)
    bytes32[][] strategyProofs;
}

/// @notice Execute hooks for general strategy management (rebalancing, etc.).
/// @notice Any manager can call this function.
/// @param args Execution arguments containing hooks, calldata, proofs, expectations.
function executeHooks(ExecuteArgs calldata args) external payable;

Strategy Pausing

Both primary and secondary managers can manually pause strategies in the event of an emergency.
/// @notice Manually pauses a strategy
/// @param strategy Address of the strategy to pause
/// @dev Only the main or secondary manager of the strategy can pause it
function pauseStrategy(address strategy) external validStrategy(strategy) {}

/// @notice Manually unpauses a strategy
/// @param strategy Address of the strategy to unpause
/// @dev unpausing marks PPS stale until a fresh oracle update
function unpauseStrategy(address strategy) external validStrategy(strategy) {}

Fee Mechanism

Fee Configuration

Managers can alter fee parameters through the following functions.
/// @notice Change the fee recipient when the primary manager is changed
/// @param newRecipient New fee recipient
function changeFeeRecipient(address newRecipient) external;

/// @notice Propose or execute a hook root update
/// @notice Propose changes to vault-specific fee configuration
/// @param performanceFeeBps New performance fee in basis points
/// @param managementFeeBps New management fee in basis points
/// @param recipient New fee recipient
/// @dev IMPORTANT: Before executing the proposed update (via executeVaultFeeConfigUpdate),
///      manager should call skimPerformanceFee() to collect performance fees on existing profits
///      under the current fee structure to avoid losing profit or incorrect fee calculations.
function proposeVaultFeeConfigUpdate(
    uint256 performanceFeeBps,
    uint256 managementFeeBps,
    address recipient
) external;

/// @notice Execute the proposed vault fee configuration update after timelock
/// @dev IMPORTANT: Manager should call skimPerformanceFee() before executing this update
///      to collect performance fees on existing profits under the current fee structure.
///      Otherwise, profit earned under the old fee percentage will be lost or incorrectly calculated.
/// @dev This function will reset the High Water Mark (vaultHwmPps) to the current PPS value
///      to avoid incorrect fee calculations with the new fee structure.
function executeVaultFeeConfigUpdate() external;

Fee Skimming

Performance fees on realized profit are obtained by periodically calling skimPerformanceFee().
/// @notice Skim performance fees based on per-share High Water Mark (PPS-based)
/// @dev Can be called by any manager when vault PPS has grown above HWM PPS
/// @dev Uses PPS growth to calculate profit: (currentPPS - hwmPPS) * totalSupply / PRECISION
/// @dev HWM is only updated during this function, not during deposits/redemptions
function skimPerformanceFee() external;
The ideal frequency at which to skim fees depends on the TVL and PPS of a vault, but generally 24 hours is optimal. More precise simulations can be found on the curators dashboard and here.

User Redemptions

User redemption and cancel redemption requests must be fulfilled manually by any authorized manager. It is advised to do so through the curators dashboard where there is tooling to aid controller sorting and totalAssetsOut calculations.
/// @notice Fulfills pending cancel redeem requests by making shares claimable
/// @dev Processes all controllers with pending cancellation flags
/// @dev Can only be called by authorized managers
/// @param controllers Array of controller addresses with pending cancel requests
function fulfillCancelRedeemRequests(address[] memory controllers) external;

/// @notice Fulfills pending redeem requests with exact total assets per controller (pre-fee).
/// @dev PRE: Off-chain sort/unique controllers. Call executeHooks(sum(totalAssetsOut)) first.
/// @dev Social: totalAssetsOut[i] = theoreticalGross[i] (full). Selective: totalAssetsOut[i] < theoreticalGross[i].
/// @dev NOTE: totalAssetsOut includes fees - actual net amount received is calculated internally after fee
/// deduction.
/// @param controllers Ordered/unique controllers with pending requests.
/// @param totalAssetsOut Total PRE-FEE assets available for each controller[i] (from executeHooks).
function fulfillRedeemRequests(address[] calldata controllers, uint256[] calldata totalAssetsOut) external;