# MetaVaultWrapper



The MetaVaultWrapper is the user-facing contract for MetaVaults. It implements [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) (asynchronous deposit/redeem), [ERC-7575](https://eips.ethereum.org/EIPS/eip-7575), and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). Wrapper shares are ERC-20 tokens representing the depositor's pro-rata claim on the underlying infrastructure vault.

The wrapper delegates asset custody to an infrastructure vault (AsyncVault) and maintains its own epoch tracking to correctly account for per-user request balances.

The implementation is maintained in Spectra's private MetaVault contracts repository.

## Methods [#methods]

### requestDeposit [#requestdeposit]

```solidity
function requestDeposit(
    uint256 assets,
    address controller,
    address owner
) external nonReentrant whenNotPaused returns (uint256 requestId)
```

Requests a deposit of `assets` (underlying token) into the MetaVault. Assets are transferred from `owner` to the wrapper and forwarded to the infrastructure vault. The request is recorded for the current epoch.

If the controller has a claimable deposit from a previous epoch, it is automatically claimed first.

<Callout type="warn">
  `msg.sender` must be `owner` or an approved operator of `owner`. The owner must approve the wrapper for the underlying asset before calling this function.
</Callout>

| Input Parameter | Type    | Description                                          |
| --------------- | ------- | ---------------------------------------------------- |
| `assets`        | uint256 | Amount of underlying assets to deposit. Must be > 0. |
| `controller`    | address | Address that the deposit request is recorded for     |
| `owner`         | address | Address that the assets are transferred from         |

| Return Parameter | Type    | Description                                                      |
| ---------------- | ------- | ---------------------------------------------------------------- |
| `requestId`      | uint256 | Always returns 0 (request ID is not used in this implementation) |

### decreaseDepositRequest [#decreasedepositrequest]

```solidity
function decreaseDepositRequest(uint256 assets) external nonReentrant whenNotPaused
```

Decreases the caller's pending deposit request by `assets`. The assets are refunded to `msg.sender`.

| Input Parameter | Type    | Description                                                                             |
| --------------- | ------- | --------------------------------------------------------------------------------------- |
| `assets`        | uint256 | Amount of assets to remove from the pending request. Must be > 0 and ≤ pending balance. |

### deposit [#deposit]

```solidity
function deposit(
    uint256 assets,
    address receiver,
    address controller
) external nonReentrant whenNotPaused returns (uint256 shares)
```

Claims all claimable shares for the `controller` from a previously settled deposit request. Wrapper shares are minted to `receiver`.

<Callout type="info">
  The `assets` parameter is not used — all claimable shares are always claimed in full.
</Callout>

| Input Parameter | Type    | Description                                           |
| --------------- | ------- | ----------------------------------------------------- |
| `assets`        | uint256 | Not used (included for ERC-7540 interface compliance) |
| `receiver`      | address | Address that will receive the minted wrapper shares   |
| `controller`    | address | Address whose claimable deposit is being claimed      |

| Return Parameter | Type    | Description                                   |
| ---------------- | ------- | --------------------------------------------- |
| `shares`         | uint256 | Amount of wrapper shares minted to `receiver` |

### requestRedeem [#requestredeem]

```solidity
function requestRedeem(
    uint256 shares,
    address controller,
    address owner
) external nonReentrant whenNotPaused returns (uint256 requestId)
```

Requests a redemption of `shares` (wrapper shares). Shares are burned from `owner` and the equivalent infrastructure vault shares are queued for redemption.

If the controller has a claimable redeem from a previous epoch, it is automatically claimed first.

| Input Parameter | Type    | Description                                      |
| --------------- | ------- | ------------------------------------------------ |
| `shares`        | uint256 | Amount of wrapper shares to redeem. Must be > 0. |
| `controller`    | address | Address that the redeem request is recorded for  |
| `owner`         | address | Address that the shares are burned from          |

| Return Parameter | Type    | Description      |
| ---------------- | ------- | ---------------- |
| `requestId`      | uint256 | Always returns 0 |

### decreaseRedeemRequest [#decreaseredeemrequest]

```solidity
function decreaseRedeemRequest(uint256 shares) external nonReentrant whenNotPaused
```

Decreases the caller's pending redeem request by `shares`. The shares are minted back to `msg.sender`.

| Input Parameter | Type    | Description                                                                             |
| --------------- | ------- | --------------------------------------------------------------------------------------- |
| `shares`        | uint256 | Amount of shares to remove from the pending request. Must be > 0 and ≤ pending balance. |

### redeem [#redeem]

```solidity
function redeem(
    uint256 shares,
    address receiver,
    address controller
) public nonReentrant whenNotPaused returns (uint256 assets)
```

Claims all claimable assets for the `controller` from a previously settled redeem request. Assets are transferred to `receiver`.

<Callout type="info">
  The `shares` parameter is not used — all claimable assets are always claimed in full.
</Callout>

| Input Parameter | Type    | Description                                           |
| --------------- | ------- | ----------------------------------------------------- |
| `shares`        | uint256 | Not used (included for ERC-7540 interface compliance) |
| `receiver`      | address | Address that will receive the underlying assets       |
| `controller`    | address | Address whose claimable redeem is being claimed       |

| Return Parameter | Type    | Description                                           |
| ---------------- | ------- | ----------------------------------------------------- |
| `assets`         | uint256 | Amount of underlying assets transferred to `receiver` |

### setOperator [#setoperator]

```solidity
function setOperator(address operator, bool approved) external returns (bool)
```

Approves or revokes `operator` to act on behalf of `msg.sender` for deposit/redeem operations.

| Input Parameter | Type    | Description                           |
| --------------- | ------- | ------------------------------------- |
| `operator`      | address | Address to approve/revoke as operator |
| `approved`      | bool    | `true` to approve, `false` to revoke  |

### updateEpochID [#updateepochid]

```solidity
function updateEpochID() external
```

Synchronizes the wrapper's internal epoch ID with the infrastructure vault's current epoch. Called automatically by `requestDeposit`, `deposit`, `requestRedeem`, and `redeem`, but can also be called manually.

## View Methods [#view-methods]

### getInfraVault [#getinfravault]

```solidity
function getInfraVault() public view returns (address)
```

Returns the address of the underlying infrastructure vault.

### isOperator [#isoperator]

```solidity
function isOperator(address controller, address operator) external view returns (bool status)
```

Returns whether `operator` is approved to act on behalf of `controller`.

### totalVaultShares [#totalvaultshares]

```solidity
function totalVaultShares() public view returns (uint256)
```

Returns the total infrastructure vault shares held by the wrapper.

### epochId [#epochid]

```solidity
function epochId() external view returns (uint256)
```

Returns the current epoch ID from the infrastructure vault.

### lastUserDepositRequestEpoch [#lastuserdepositrequestepoch]

```solidity
function lastUserDepositRequestEpoch(address user) external view returns (uint256)
```

Returns the epoch ID of the user's last deposit request.

### lastUserRedeemRequestEpoch [#lastuserredeemrequestepoch]

```solidity
function lastUserRedeemRequestEpoch(address user) external view returns (uint256)
```

Returns the epoch ID of the user's last redeem request.

### convertToShares [#converttoshares]

```solidity
function convertToShares(uint256 assets) public view returns (uint256 shares)
```

Converts `assets` to wrapper shares using the **last settled epoch** rate.

### convertToShares [#converttoshares-1]

```solidity
function convertToShares(uint256 assets, uint256 requestID) external view returns (uint256 shares)
```

Converts `assets` to wrapper shares using a **specific epoch** rate.

### convertToAssets [#converttoassets]

```solidity
function convertToAssets(uint256 shares) public view returns (uint256 assets)
```

Converts wrapper `shares` to assets using the **last settled epoch** rate.

### convertToAssets [#converttoassets-1]

```solidity
function convertToAssets(uint256 shares, uint256 requestID) external view returns (uint256 assets)
```

Converts wrapper `shares` to assets using a **specific epoch** rate.

### maxDeposit [#maxdeposit]

```solidity
function maxDeposit(address receiver) public view returns (uint256 maxAssets)
```

Returns the maximum amount the `receiver` can claim via `deposit`. Returns 0 if paused or if the user's last deposit request is for the current (unsettled) epoch.

### maxRedeem [#maxredeem]

```solidity
function maxRedeem(address owner) public view returns (uint256 maxShares)
```

Returns the maximum amount the `owner` can claim via `redeem`. Returns 0 if paused or if the user's last redeem request is for the current (unsettled) epoch.

### pendingDepositRequest [#pendingdepositrequest]

```solidity
function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256 assets)
```

Returns the amount of assets the `controller` has pending in the current epoch's deposit queue.

### claimableDepositRequest [#claimabledepositrequest]

```solidity
function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256 assets)
```

Returns the amount of assets the `controller` can claim from a settled deposit request.

### pendingRedeemRequest [#pendingredeemrequest]

```solidity
function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256 shares)
```

Returns the amount of shares the `controller` has pending in the current epoch's redeem queue.

### claimableRedeemRequest [#claimableredeemrequest]

```solidity
function claimableRedeemRequest(uint256 requestID, address controller) external view returns (uint256 shares)
```

Returns the amount of shares the `controller` can claim from a settled redeem request.

### totalAssets [#totalassets]

```solidity
function totalAssets() public view returns (uint256)
```

Returns an estimate of the wrapper's total assets using the last settled epoch rate: `convertToAssets(totalSupply())`.

### decimals [#decimals]

```solidity
function decimals() public view returns (uint8)
```

Returns the decimals of the underlying asset.

### share [#share]

```solidity
function share() external view returns (address)
```

Returns the address of the share token (the wrapper itself). ERC-7575 compliance.

### supportsInterface [#supportsinterface]

```solidity
function supportsInterface(bytes4 interfaceId) public view returns (bool)
```

Returns `true` for IERC165, IERC7540, IERC7575, IERC7540Deposit, IERC7540Redeem, and IERC7540Operator.

<Callout type="warn">
  `previewDeposit`, `previewRedeem`, `previewWithdraw`, and `previewMint` are not implemented and will revert, as specified by ERC-7540 for asynchronous vaults.
</Callout>

## Events [#events]

### DepositRequest [#depositrequest]

```solidity
event DepositRequest(
    address indexed controller,
    address indexed owner,
    uint256 indexed requestId,
    address sender,
    uint256 assets
)
```

Emitted when a deposit request is created.

### RedeemRequest [#redeemrequest]

```solidity
event RedeemRequest(
    address indexed controller,
    address indexed owner,
    uint256 indexed requestId,
    address sender,
    uint256 shares
)
```

Emitted when a redeem request is created.

### OperatorSet [#operatorset]

```solidity
event OperatorSet(address indexed controller, address indexed operator, bool approved)
```

Emitted when an operator approval is set or revoked.

### DecreaseDepositRequest [#decreasedepositrequest-1]

```solidity
event DecreaseDepositRequest(
    uint256 indexed epochId,
    address indexed owner,
    uint256 indexed previousRequestedAssets,
    uint256 newRequestedAssets
)
```

Emitted when a deposit request is decreased.

### DecreaseRedeemRequest [#decreaseredeemrequest-1]

```solidity
event DecreaseRedeemRequest(
    uint256 indexed epochId,
    address indexed owner,
    uint256 indexed previousRequestedShares,
    uint256 newRequestedShares
)
```

Emitted when a redeem request is decreased.

### ClaimPendingDeposit [#claimpendingdeposit]

```solidity
event ClaimPendingDeposit(
    uint256 indexed epochId,
    uint256 indexed assetsClaimed,
    uint256 indexed wrapperSharesReceived
)
```

Emitted when the wrapper claims pending deposits from the infrastructure vault.

### ClaimPendingRedeem [#claimpendingredeem]

```solidity
event ClaimPendingRedeem(
    uint256 indexed epochId,
    uint256 indexed wrapperSharesClaimed,
    uint256 indexed assetsReceived
)
```

Emitted when the wrapper claims pending redeems from the infrastructure vault.

### MetaVaultWrapperInitialized [#metavaultwrapperinitialized]

```solidity
event MetaVaultWrapperInitialized(
    address indexed owner,
    address indexed infraVault,
    address indexed wrapper
)
```

Emitted when the wrapper is initialized.

## Errors [#errors]

| Error                                                                         | Description                                                      |
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `ZeroAddress()`                                                               | An address parameter is `address(0)`                             |
| `ZeroAssets()`                                                                | Assets amount is 0                                               |
| `ZeroShares()`                                                                | Shares amount is 0                                               |
| `ERC7540InvalidOperator()`                                                    | Caller is not the owner or an approved operator                  |
| `ZeroDecreaseAmount()`                                                        | Decrease amount is 0                                             |
| `DecreaseAmountExceedsPending(uint256 pendingAmount, uint256 decreaseAmount)` | Decrease amount exceeds the pending balance                      |
| `InvalidUnderlying()`                                                         | Underlying asset does not match the infrastructure vault's asset |
| `NoClaimAvailable(address owner)`                                             | No settled request available to claim                            |
