Skip to content

Manage 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.

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 Scripts (Recommended)

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