Configure Vault V2 Roles

This tutorial guides you through setting up and managing roles for a Morpho Vault V2. A clear understanding and proper assignment of roles are critical for the vault's security, governance, and operational success.

Separation of Concerns is Key

The Vault V2 role system is designed with a strict separation of powers. The Owner manages permissions, the Curator sets strategy, the Allocator executes it, and the Sentinel ensures safety. Unlike V1, roles do not inherit permissions from each other. For a detailed breakdown, please refer to the Vaults V2 Roles concept page.

Handle Private Keys with Extreme Care

The Owner and Curator roles hold significant power. It is highly recommended to secure these roles with a multisig or an institutional-grade MPC wallet. Never use a single EOA (hot wallet) for these roles in a production environment. Always verify addresses before executing transactions.

The Role Setup Workflow

Setting up roles for a new Vault V2 follows a specific sequence. The Owner first assigns the Curator and Sentinels (actions which are immediate). Then, the Curator proposes strategic changes, such as appointing an Allocator.

Crucially, most of the Curator's actions are timelocked. The process for these actions depends on whether a timelock duration is set.

Managing Roles via the Curator App

The Curator app exposes a UI for Vault V2 role setup and role management. Use it when you prefer a guided flow; the script and Etherscan flows below remain useful for repeatable deployments and low-level verification.

  1. Open the app and select your vault.
  2. Connect the wallet that controls the required role:
    • Owner for appointing the Curator or adding and removing Sentinels.
    • Curator for submitting timelocked role changes such as adding or removing Allocators.
    • Sentinel for revoking pending actions from the Sentinel tab.
    • Any address for executing an already-submitted action after its timelock expires.
  3. Open the Roles tab and review the current Owner, Curator, Allocators, and Sentinels.
  4. For immediate Owner actions, fill the new address, verify it carefully, and sign the transaction.
  5. For timelocked Curator actions, submit the proposal, wait until it becomes executable, then return to execute it. A Curator or Sentinel can revoke the pending proposal before execution.

The Curator app does not bypass protocol permissions or timelocks. Always verify the target vault, role address, and action before signing.

Role setup on the curator app

Managing Roles via Scripts

The official deployment repository handles role management efficiently: https://github.com/morpho-org/vault-v2-deployment

Scenario 1: Initial Vault Setup (Zero Timelock)

The DeployVaultV2.s.sol script handles the complete initial setup. Here's how it manages roles:

// The script sets the broadcaster as temporary owner and curator
VaultV2 vaultV2 = new VaultV2(broadcaster, vaultV1.asset());
vaultV2.setCurator(broadcaster);

// Submit allocator role assignments
vaultV2.submit(abi.encodeCall(vaultV2.setIsAllocator, (broadcaster, true)));
vaultV2.submit(abi.encodeCall(vaultV2.setIsAllocator, (allocator, true)));

// Execute immediately (timelock is 0 by default)
vaultV2.setIsAllocator(broadcaster, true);
vaultV2.setIsAllocator(allocator, true);

// After configuration, transfer roles to final addresses
vaultV2.setCurator(curator);
if (sentinel != address(0)) {
    vaultV2.setIsSentinel(sentinel, true);
}
vaultV2.setOwner(owner);

Scenario 2: Production Management (Non-Zero Timelock)

Once a vault has non-zero timelocks, role changes require a two-step process:

Step 1: Submitting a Role Change

Create a script to submit the proposal:

script/SubmitRoleChange.s.sol

pragma solidity 0.8.28;

import {Script} from "forge-std/Script.sol";
import {IVaultV2} from "vault-v2/interfaces/IVaultV2.sol";

contract SubmitRoleChange is Script {
    function run() external {
        address vaultAddress = vm.envAddress("VAULT_V2_ADDRESS");
        address newAllocator = vm.envAddress("NEW_ALLOCATOR_ADDRESS");
        
        IVaultV2 vault = IVaultV2(vaultAddress);
        
        vm.startBroadcast();
        
        // Submit the proposal
        bytes memory data = abi.encodeCall(IVaultV2.setIsAllocator, (newAllocator, true));
        vault.submit(data);
        
        vm.stopBroadcast();
        
        uint256 timelock = vault.timelock(IVaultV2.setIsAllocator.selector);
        console.log("Proposal submitted. Execute after", timelock, "seconds");
    }
}

Step 2: Executing the Role Change

After the timelock expires, execute the change:

script/ExecuteRoleChange.s.sol

pragma solidity 0.8.28;

import {Script} from "forge-std/Script.sol";
import {IVaultV2} from "vault-v2/interfaces/IVaultV2.sol";

contract ExecuteRoleChange is Script {
    function run() external {
        address vaultAddress = vm.envAddress("VAULT_V2_ADDRESS");
        address newAllocator = vm.envAddress("NEW_ALLOCATOR_ADDRESS");
        
        IVaultV2 vault = IVaultV2(vaultAddress);
        
        vm.startBroadcast();
        
        // Execute the timelocked action
        vault.setIsAllocator(newAllocator, true);
        
        vm.stopBroadcast();
        
        console.log("Allocator role assigned successfully");
    }
}

Revoking Proposals

Curators and Sentinels can revoke pending proposals:

// To revoke a pending proposal
bytes memory dataToRevoke = abi.encodeCall(IVaultV2.setIsAllocator, (address, bool));
vault.revoke(dataToRevoke);

Managing Roles via Etherscan

For manual management, the process mirrors the scripted approach.

Owner Actions (Immediate)

As the Owner, connect your wallet to the Vault V2 contract on Etherscan and use the Write Contract tab to:

  • Execute setCurator(address newCurator) to appoint the Curator.
  • Execute setIsSentinel(address account, bool newIsSentinel) to add or remove Sentinels.
  • Execute setOwner(address newOwner) to transfer ownership.

Curator Actions (Timelocked)

As the Curator, connect your wallet to:

  1. Submit the Proposal:
    • Use a tool like Foundry's cast calldata to ABI-encode your intended function call (e.g., setIsAllocator(0x..., true)).
    • Call the submit(bytes) function with the encoded data.
  2. Wait for the Timelock: Check executableAt(bytes data) to see when execution is allowed.
  3. Execute the Proposal: After the timelock, anyone can call the original function to finalize the change.

Checking Role Status

Use the Read Contract tab to verify current roles:

  • owner(): Returns the current owner
  • curator(): Returns the current curator
  • isAllocator(address): Check if an address is an allocator
  • isSentinel(address): Check if an address is a sentinel

On this page