# Gates in Morpho Vaults

Source: https://docs.morpho.org/curate/concepts/gates



Morpho Vaults (V1 and V2) support different mechanisms to restrict access to deposits, withdrawals, and transfers. Vault V2 introduces **first-class gates** as external contracts with timelock and abdication guarantees, while Vault V1.1 achieves gating operationally by manipulating the `supplyQueue` and market caps.

## High-Level Comparison [#high-level-comparison]

| Aspect                 | Vault V1.1                                | Vault V2                                                         |
| ---------------------- | ----------------------------------------- | ---------------------------------------------------------------- |
| **Native Gates**       | ❌ None (must use supplyQueue + caps)      | ✅ Four explicit gates (receive/send shares, receive/send assets) |
| **Granularity**        | Coarse: deposits only (via `supplyQueue`) | Fine: deposits, withdrawals, transfers, fee accrual              |
| **Timelock**           | Caps increases are timelocked (24h–2w)    | All gate changes timelocked (configurable, e.g. up to 3 weeks)   |
| **Abdication**         | ❌ Not supported                           | ✅ Supported (permanent permissionless or permanently gated)      |
| **Atomic batching**    | ✅ `multicall` allows open–deposit–close   | ✅ `multicall` supported but rarely needed for gates              |
| **Intended use cases** | Simple whitelisting of suppliers          | Compliance, allowlists, permissioned transfers                   |

## Gates in Vault V2 [#gates-in-vault-v2]

### Executive Summary [#executive-summary]

Gates in Vault V2 are external smart contracts that provide **access control and compliance mechanisms** for vault operations. They enable selective permissions for vault receipt token transfers, asset deposits, and asset withdrawals while making it possible to maintain the vault's noncustodial guarantees. Gates are **optional** (if not set, operations are unrestricted) and can be **timelocked** for security.

### Core Architecture [#core-architecture]

#### Four Gate Types [#four-gate-types]

<figure>
  <ZoomableImage src="/img/curators/gates/gates-types.png" alt="Gates in Morpho Vault V2" />
</figure>

Vault V2 implements a **four-gate system** addressing different operational vectors:

#### 1. **Receive Shares Gate** (`receiveSharesGate`) [#1-receive-shares-gate-receivesharesgate]

* **Interface**: `IReceiveSharesGate` with `canReceiveShares(address)`
* **Controls**: Who can receive vault shares
* **Operations**:
  * `deposit()`, `mint()` → checks `canReceiveShares(onBehalf)`
  * `transfer()`, `transferFrom()` → checks `canReceiveShares(to)`
  * Performance/management fee accrual → checks `canReceiveShares(feeRecipient)`
* **⚠️ Critical Risk**: Can prevent depositors from getting back shares deposited on other contracts. Note that if the gate **reverts** instead of returning `false`, it can cause a &#x2A;*denial of service (DoS)** across core vault operations.

#### 2. **Send Shares Gate** (`sendSharesGate`) [#2-send-shares-gate-sendsharesgate]

* **Interface**: `ISendSharesGate` with `canSendShares(address)`
* **Controls**: Who can send vault shares
* **Operations**:
  * `withdraw()`, `redeem()` → checks `canSendShares(onBehalf)`
  * `transfer()`, `transferFrom()` → checks `canSendShares(from)`
* **⚠️ Critical Risk**: Can lock users out of exiting the vault or prevent depositors from getting back shares deposited on other contracts

#### 3. **Receive Assets Gate** (`receiveAssetsGate`) [#3-receive-assets-gate-receiveassetsgate]

* **Interface**: `IReceiveAssetsGate` with `canReceiveAssets(address)`
* **Controls**: Who can receive underlying assets from the vault
* **Operations**:
  * `withdraw()`, `redeem()` → checks `canReceiveAssets(receiver)`
* **Special Rule**: Vault itself (`address(this)`) always bypasses this gate
* **⚠️ Critical Risk**: Can prevent people from receiving their assets upon withdrawals

#### 4. **Send Assets Gate** (`sendAssetsGate`) [#4-send-assets-gate-sendassetsgate]

* **Interface**: `ISendAssetsGate` with `canSendAssets(address)`
* **Controls**: Who can deposit underlying assets into the vault
* **Operations**:
  * `deposit()`, `mint()` → checks `canSendAssets(msg.sender)`
* **Non-critical**: Cannot block users' assets, while still being able to gate supplies

### Technical Implementation Deep Dive [#technical-implementation-deep-dive]

Permissioned token functions (gates) can be found in the Vault V2 repository [here](https://github.com/morpho-org/vault-v2/blob/0888120e4c041690a040635dc4d448b7cb597965/src/VaultV2.sol#L910-L926) and IGate interfaces [here](https://github.com/morpho-org/vault-v2/blob/main/src/interfaces/IGate.sol).

#### Gate Checking Logic [#gate-checking-logic]

```solidity
// From VaultV2.sol - Gate validation functions
function canReceiveShares(address account) public view returns (bool) {
    return receiveSharesGate == address(0) ||
           IReceiveSharesGate(receiveSharesGate).canReceiveShares(account);
}

function canSendShares(address account) public view returns (bool) {
    return sendSharesGate == address(0) ||
           ISendSharesGate(sendSharesGate).canSendShares(account);
}

function canReceiveAssets(address account) public view returns (bool) {
    return account == address(this) ||
           receiveAssetsGate == address(0) ||
           IReceiveAssetsGate(receiveAssetsGate).canReceiveAssets(account);
}

function canSendAssets(address account) public view returns (bool) {
    return sendAssetsGate == address(0) ||
           ISendAssetsGate(sendAssetsGate).canSendAssets(account);
}

```

#### Critical Implementation Requirements [#critical-implementation-requirements]

Gates MUST be designed knowing it should never revert, returning always boolean values. Also gates should not use a malicious amount of gas (like something close to the block size).

### Governance and Security Model [#governance-and-security-model]

#### Timelock Protection [#timelock-protection]

Gates follow the vault's timelock system:

```solidity
// Curator functions for gate management (timelockable)
function setReceiveSharesGate(address newReceiveSharesGate) external {
    timelocked();
    receiveSharesGate = newReceiveSharesGate;
    emit EventsLib.SetReceiveSharesGate(newReceiveSharesGate);
}

function setSendSharesGate(address newSendSharesGate) external {
    timelocked();
    sendSharesGate = newSendSharesGate;
    emit EventsLib.SetSendSharesGate(newSendSharesGate);
}

function setReceiveAssetsGate(address newReceiveAssetsGate) external {
    timelocked();
    receiveAssetsGate = newReceiveAssetsGate;
    emit EventsLib.SetReceiveAssetsGate(newReceiveAssetsGate);
}

function setSendAssetsGate(address newSendAssetsGate) external {
    timelocked();
    sendAssetsGate = newSendAssetsGate;
    emit EventsLib.SetSendAssetsGate(newSendAssetsGate);
}

```

#### The Two Types of Permanent Guarantees with Gate Abdication [#the-two-types-of-permanent-guarantees-with-gate-abdication]

```solidity
// Example: Permanently disable receive shares gate changes
vault.abdicate(IVaultV2.setReceiveSharesGate.selector);

```

The `abdicate()` function allows a curator to make an irreversible commitment regarding a vault's configuration, providing users with permanent guarantees. When applied to gates, this creates two distinct and powerful outcomes: ensuring a vault is either permanently permissionless or permanently governed by a fixed set of rules:

#### Type 1. The Permanently Ungated Vault (Abdicating to Zero) [#type-1-the-permanently-ungated-vault-abdicating-to-zero]

This strategy provides the strongest guarantee of permissionless access.

* **Goal:** To ensure a vault can never have a specific type of gate imposed on it.
* **How it Works:** The curator first ensures the target gate address is set to address(0) (disabling it). They then call `abdicate` on that gate's setter function (e.g., setReceiveSharesGate). Note that `abdicate` is a timelocked function, so it must be submitted and then executed after the timelock period (or immediately if the timelock is 0).
* **Result:** This action permanently locks the gate address to address(0). No one can ever set a gate contract for that function in the future. This is a powerful tool for creating truly decentralized vaults where users are assured that access rules will never be introduced.

#### Type 2. The Permanently Gated Vault (Abdicating to a Non-Zero Gate) [#type-2-the-permanently-gated-vault-abdicating-to-a-non-zero-gate]

This strategy is designed for vaults that must adhere to a fixed, unchangeable set of compliance rules.

* **Goal:** To lock a vault into a specific, non-modifiable access control policy.
* **How it Works:** The curator sets the gate to the address of a deployed gate contract and then calls `abdicate` on the setter function.
* **Result:** The vault becomes permanently bound to that gate contract. The rules defined within that contract can never be altered by changing the gate address.

**Crucial Security Consideration:** This guarantee is only meaningful if the gate contract itself has **immutable behavior**. If the gate contract contains administrative functions that allow an owner to modify its rules (e.g., change a whitelist), then abdicating simply transfers the power to control access from the vault's curator to the gate's administrator. For a true permanent guarantee, the gate contract must be designed without such administrative controls, or its ownership must be renounced.

### Special Cases and Edge Conditions [#special-cases-and-edge-conditions]

#### Vault Self-Operations [#vault-self-operations]

The vault itself (`address(this)`) is **always allowed** to receive assets, regardless of the `receiveAssetsGate` configuration. This prevents the vault from blocking its own internal operations:

```solidity
function canReceiveAssets(address account) public view returns (bool) {
    return account == address(this) || // Vault always bypasses
           receiveAssetsGate == address(0) ||
           IReceiveAssetsGate(receiveAssetsGate).canReceiveAssets(account);
}

```

#### Fee Recipient Gate Checks [#fee-recipient-gate-checks]

**Important**: If a gate is set and reverts for fee recipients when fees are non-zero, interest accrual will revert. This makes proper gate configuration critical for vault operation.

### Impact on Non-Custodial Guarantees [#impact-on-non-custodial-guarantees]

#### Force Deallocate Mechanism [#force-deallocate-mechanism]

```solidity
function forceDeallocate(address adapter, bytes memory data, uint256 assets, address onBehalf)
    external returns (uint256)

```

This function allows users to perform in-kind redemptions by:

1. Flashloaning liquidity
2. Supplying to an adapter's market
3. Withdrawing liquidity through `forceDeallocate`
4. Repaying the flashloan

More details are provided [here](/learn/concepts/vault-v2/#are-morpho-vaults-v2-non-custodial).

#### Fee Wrappers on Top of Your Vault [#fee-wrappers-on-top-of-your-vault]

When a [Fee Wrapper](/developers/earn/concepts/fee-wrapper) allocates to your vault, its adapter is a depositor of your vault and your gates apply to the adapter address. A critical gate that blocks the adapter blocks withdrawals and `forceDeallocate` for every Fee Wrapper depositor, even if the Fee Wrapper has abdicated its own gates.

Your `sendAssetsGate` also matters here. When your vault is illiquid, Fee Wrapper depositors exit through in-kind redemption by depositing flash-loaned assets into your vault, so your `sendAssetsGate` can prevent the in-kind redemption flow.

A Fee Wrapper on top of your vault is only non-custodial if your critical gates and your `sendAssetsGate` are abdicated, or can never block its adapter or its depositors.

### Testing Considerations [#testing-considerations]

The codebase includes comprehensive gate testing:

* `GateExampleTest.sol` - Tests the reference implementation (Disclaimer: this should not be used in production as is).
* `GatingTest.sol` - Tests all gate integration points
* Tests verify proper error handling for each gate type
* Tests confirm vault self-operations bypass receive assets gate

## Gates in Vault V1 [#gates-in-vault-v1]

Vault V1.1 does not include first-class gates. Instead, access control can be emulated by **playing with the `supplyQueue` and market caps**, combined with `multicall`. This provides a practical gating mechanism for suppliers.

### Key Mechanics [#key-mechanics]

* **Deposits only work if the `supplyQueue` is non-empty**. If empty, `deposit` reverts and the vault is effectively closed.
* **Allocator role** can update the queue at any time (no timelock).
* A market must have a **non-zero cap** to appear in the queue.
* **Cap increases** are timelocked (24h–2w), but **cap decreases** are instant.
* The vault supports &#x2A;*`multicall`**, allowing atomic open–deposit–close flows to prevent front-running.

### Tutorial [#tutorial]

#### One-Time Setup [#one-time-setup]

1. **Deploy** the V1.1 vault (`MetaMorphoV1_1`).
2. **Assign roles**:
   * Owner → multisig
   * Curator → multisig (or another trusted address)
   * Allocator → hot wallet / automation bot  that will run the batch transaction
3. **Prepare the market**: ensure cap > 0 (requires timelock if raising).

#### Keep the Vault Closed by Default [#keep-the-vault-closed-by-default]

```solidity
// Curator or Allocator
vault.setSupplyQueue(new MarketParams); // []
```

No one can deposit, as the queue is empty.

#### Open Deposit Window (Privileged Supplier) [#open-deposit-window-privileged-supplier]

Perform an **atomic batch** via `multicall`:

1. Open queue (add target market).
2. Deposit assets.
3. Close queue (reset to empty).

#### Example in viem [#example-in-viem]

```ts
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { morphoVaultAbi } from "./abi";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(RPC_URL),
});

const VAULT_ADDRESS = "0x..."; 
const TARGET_MARKET = "0x..."; // market id
const AMOUNT = 1_000n * 10n ** 18n;

const openQueueCall  = {
  abi: morphoVaultAbi,
  functionName: "setSupplyQueue",
  args: [[TARGET_MARKET]],
};

const depositCall = {
  abi: morphoVaultAbi,
  functionName: "deposit",
  args: [AMOUNT, account.address],
};

const closeQueueCall = {
  abi: morphoVaultAbi,
  functionName: "setSupplyQueue",
  args: [[]],
};

await client.writeContract({
  address: VAULT_ADDRESS,
  abi: morphoVaultAbi,
  functionName: "multicall",
  args: [[
    client.encodeFunctionData(openQueueCall),
    client.encodeFunctionData(depositCall),
    client.encodeFunctionData(closeQueueCall),
  ]],
});
```

#### Operational Caveats [#operational-caveats]

* **Always batch** open–deposit–close; otherwise, others may front-run deposits.
* Keep caps at **0 by default** and raise only when scheduling controlled deposits.
* This mechanism gates **suppliers only**; withdrawals remain unaffected.

#### Final notes on Vault V1 Gating operation: [#final-notes-on-vault-v1-gating-operation]

* The **supplier must sign the batch** and be the allocator itself.
* If you use a Gnosis Safe, craft a Multisend with those three calls and have the Safe execute it.