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

Code for MetaVaultWrapper.sol can be found on [GitHub](https://github.com/spectra-finance/metavaults-V1/blob/main/src/MetaVaultWrapper.sol).

## Methods

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

{% hint style="warning" %}
`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.
{% endhint %}

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>Amount of underlying assets to deposit. Must be > 0.</td></tr><tr><td><code>controller</code></td><td>address</td><td>Address that the deposit request is recorded for</td></tr><tr><td><code>owner</code></td><td>address</td><td>Address that the assets are transferred from</td></tr></tbody></table>

<table><thead><tr><th width="190">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>requestId</code></td><td>uint256</td><td>Always returns 0 (request ID is not used in this implementation)</td></tr></tbody></table>

### 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`.

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>Amount of assets to remove from the pending request. Must be > 0 and ≤ pending balance.</td></tr></tbody></table>

### 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`.

{% hint style="info" %}
The `assets` parameter is not used — all claimable shares are always claimed in full.
{% endhint %}

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>Not used (included for ERC-7540 interface compliance)</td></tr><tr><td><code>receiver</code></td><td>address</td><td>Address that will receive the minted wrapper shares</td></tr><tr><td><code>controller</code></td><td>address</td><td>Address whose claimable deposit is being claimed</td></tr></tbody></table>

<table><thead><tr><th width="190">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>Amount of wrapper shares minted to <code>receiver</code></td></tr></tbody></table>

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

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>Amount of wrapper shares to redeem. Must be > 0.</td></tr><tr><td><code>controller</code></td><td>address</td><td>Address that the redeem request is recorded for</td></tr><tr><td><code>owner</code></td><td>address</td><td>Address that the shares are burned from</td></tr></tbody></table>

<table><thead><tr><th width="190">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>requestId</code></td><td>uint256</td><td>Always returns 0</td></tr></tbody></table>

### 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`.

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>Amount of shares to remove from the pending request. Must be > 0 and ≤ pending balance.</td></tr></tbody></table>

### 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`.

{% hint style="info" %}
The `shares` parameter is not used — all claimable assets are always claimed in full.
{% endhint %}

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>Not used (included for ERC-7540 interface compliance)</td></tr><tr><td><code>receiver</code></td><td>address</td><td>Address that will receive the underlying assets</td></tr><tr><td><code>controller</code></td><td>address</td><td>Address whose claimable redeem is being claimed</td></tr></tbody></table>

<table><thead><tr><th width="190">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>Amount of underlying assets transferred to <code>receiver</code></td></tr></tbody></table>

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

<table><thead><tr><th width="190">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>operator</code></td><td>address</td><td>Address to approve/revoke as operator</td></tr><tr><td><code>approved</code></td><td>bool</td><td><code>true</code> to approve, <code>false</code> to revoke</td></tr></tbody></table>

### 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

### getInfraVault

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

Returns the address of the underlying infrastructure vault.

### 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

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

Returns the total infrastructure vault shares held by the wrapper.

### epochId

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

Returns the current epoch ID from the infrastructure vault.

### lastUserDepositRequestEpoch

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

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

### lastUserRedeemRequestEpoch

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

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

### convertToShares

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

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

### convertToShares

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

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

### convertToAssets

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

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

### convertToAssets

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

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

### 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

```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

```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

```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

```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

```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

```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

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

Returns the decimals of the underlying asset.

### share

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

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

### supportsInterface

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

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

{% hint style="warning" %}
`previewDeposit`, `previewRedeem`, `previewWithdraw`, and `previewMint` are not implemented and will revert, as specified by ERC-7540 for asynchronous vaults.
{% endhint %}

## Events

### 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

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

Emitted when a redeem request is created.

### OperatorSet

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

Emitted when an operator approval is set or revoked.

### DecreaseDepositRequest

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

Emitted when a deposit request is decreased.

### DecreaseRedeemRequest

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

Emitted when a redeem request is decreased.

### 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

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

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

### MetaVaultWrapperInitialized

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

Emitted when the wrapper is initialized.

## 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                            |
