# Overview

#### Introduction

Aera powers onchain vaults with off chain intelligence. Aera vaults can be deployed and configured to power various off chain strategies ranging from treasury management for DAOs to complex yield strategies, earn programs and other multi-depositor use cases.

Each vault has one or more guardians that take onchain actions while complying to specified constraints. Vaults immediately revert guardian transactions that call un-whitelisted contracts or don't comply with calldata constraints.

#### Key Features

* **Trustless use of guardians**. Aera guardians can optimize their execution off chain while the vault is being protected onchain. Efficient use of Merkle trees allows guardian transactions to be heavily constrained without impacting gas costs.
* **Access the entire universe of DeFi assets**. Aera's constraints are flexible enough to support interactions with a wide variety of DeFi (and even non-DeFi) protocols.
* **No-code configuration**. Vault owners can add custom integrations by configuring calldata constraints called hooks without the need to write any custom code.
* **Modularity**. Each Aera vault can upgrade parts of its functionality as its purpose evolves. This allows Aera to support adaptive strategies that take advantage of emerging market opportunities. For treasury managers, Aera can be tailored to offer the strongest possible protections for their assets.

#### Use Cases

* **Treasury Management**. Like V2, Aera V3 can be used to manage DAO treasuries without compromising on voter control or speed.
* **Yield**. Aera can be used to deploy purpose-built multi-depositor vaults to implement various DeFi yield-generating strategies or launch new reward programs.

#### Getting Started

The best way to get started is to explore the various solutions.

#### Need Help?

See the [Contact Us](/contact-us) section to talk to the team.


# The Aera Approach

Aera features a number of core design ideas that will be referenced throughout the documentation.

#### **A custom strategy for each use case**

Each Aera vault uses a custom strategy. Many existing solutions only pursue asset returns (or APY) through yield strategies. Aera is more flexible: multi-depositor vaults can support yield strategies but treasury management clients are in control of what strategies they want to use and how they want to combine them. This includes making contributor payments, managing the volatility of the treasury, providing liquidity in a governance token, diversifying and more.

Note: each strategy is currently not represented onchain in V3. All key metrics are transparently tracked in the Aera UI, but not explicitly required as part of the protocol.

#### **Vault guardian and operations**

Each Aera vault elects one or more guardians to submit operations. In the current iteration, the guardian (off-chain) has two responsibilities: allocation and execution. The allocation step decides on a target portfolio allocation based on the given strategy. The execution steps decides how to use available routing options to get to the target portfolio distribution. The guardian submits these recommended operations and they are checked by the vault to make sure all operations are compliant with the whitelist.

#### Non-custodial ownership

Aera V3 gives the vault owner full ownership and the ability to execute any action from the vault. Guardians can be appointed to allocate assets but they can also perform operational duties with treasury funds such as designing and implementing payment stream contracts for protocol contributors. Unlike other platforms Aera is not trying to move ownership of the funds to a third-party, instead Aera vaults allow treasuries to maintain ownership of their funds while also allowing guardians to take specific limited actions.

#### Adaptability

At its core, Aera is a layer that helps guardians act on a vault in a controlled way. The exact ways guardian actions are controlled by the Merkle tree and can vary based on each deployment. Vault owners are able and encouraged to pick the protocol integrations and constraints that can help them achieve their specific objectives. More advanced owners can adapt Aera to their needs through a variety of changes ranging from using a different oracle registry, inheriting from the vault contract to add additional functionality and more.


# Aera Protocol in One Page

Aera is a noncustodial trustless treasury management protocol. It helps capital allocators achieve various stated objectives by using special actors called *guardians* which have access to off chain intelligence. Aera allows these designated guardians to interact with a whitelisted set of DeFi protocols as chosen by the owner of each vault. Guardians operate in a principled way and are restricted with several constraints enforced onchain.

While Aera supports reasonable defaults for deployments, it's a flexible system that can be customized to integrate with any DeFi protocol, used to take treasury management actions such as paying contributors and so on.

<figure><img src="/files/zD8poCdwoy0vox9zOdH3" alt=""><figcaption></figcaption></figure>

#### Aera Vaults

Every Aera vault has an `owner` and one or more `guardians` among other roles.

The `owner` or specific roles it has assigned can:

* Call `setGuardianRoot` to change a given `guardian` and its Merkle tree root
* Call `removeGuardian` to remove a `guardian` from the vault
* Call `setSubmitHooks` to configure triggers before and after `guardian` transactions
* Call `pause` to pause all guardians
* Call `unpause` to resume vault operations

The guardian can:

* Call `submit` to take vault actions
* Call `pause` to interrupt all guardian operations (in the event of a possible security incident or another need to visibly terminate guardianship)

When using the `FeeVault` contract, in addition the vault owner can designate a fee recipient and a fee calculator contract. The fee recipient (which could be one of the guardians) can:

* Call `claimFees` to call accrued fees

#### Single Depositor Vaults

The simplest way to use Aera is by deploying a dedicated noncustodial vault to manage a set of assets (e.g., a treasury). These vaults give the owner additional permissions.

The `owner` or specific roles it has assigned can:

* Call `execute` to take arbitrary actions on the vault
* Call `deposit` to add assets to the vault
* Call `withdraw` to withdraw liquid assets from the vault

#### **Multi Depositor Vaults**

Aera also supports tokenized multiple depositor vaults that allow multiple depositors to select the same strategy. Multi depositor vaults can be used to share strategies across multiple single depositor Aera vaults or to create externally-facing tokenized strategies. An Aera Multi Depositor Vault allows multiple users to supply capital into a single vault and receive tokenized ERC20 vault units as a digital ledger representation of their supplied assets.

Users interact with a separate `Provisioner` contract and can:

* Call `deposit` or `mint` to directly deposit into the vault
* Call `withdraw` or `redeem` to directly withdraw from the vault (only when enabled)
* Call `requestDeposit` or `requestRedeem` to put in an order to deposit or redeem their units (fulfilled by a solver)
  * When a users requests a Deposit or Redeem, they will asynchronously receive their tokens directly to their wallet from the Provisioner address after their request gets solved.

#### How Aera Integrates DeFi Safely

The assets in each Aera vault are guarded by two constraints that are imposed on guardian actions:

* Guardians cannot leave outgoing nonzero approvals for ERC20 tokens
* Each guardian action has to comply with the Merkle tree

The Merkle tree is used to check:

* If the operation and target contract is whitelisted
* If the calldata provided for the call is valid (this is checked by calling a specific operation hook which may extract additional variables from the calldata that are compared against the Merkle tree leaf for that operation). **These are called operation pre-hooks**
* If any required cleanup step is successful. **These are called operation post-hooks**

#### Contract Overview

Here are the most important contracts and their functions in Aera:

| Contract                  | Scope                                   | Function                                                                                                                                                                                                                                                                            |
| ------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BaseVault`               | All Vaults                              | The base vault that encapsulates the functionality of having guardians. All vaults inherit from this contract.                                                                                                                                                                      |
| `FeeVault`                | All Vaults that inherit from `FeeVault` | The fee vault adds the ability for a fee recipient to claim reported fees. Intended to be used as a incentive for guardians and/or other vault operators.                                                                                                                           |
| `SingleDepositorVault`    | Single Depositor                        | This contract allows a treasury to use Aera. Each single depositor vault empowers the owner with the ability to deposit, withdraw and execute.                                                                                                                                      |
| `DelayedFeeCalculator`    | Single Depositor                        | The default fee calculator contract used in single depositor vaults. Allows an accountant to submit vault value over time and calculates fees. Fee accrual is delayed, allowing a vault owner behind a timelock to retire an accountant that is submitting fees incorrectly.        |
| `MultiDepositorVault`     | Multi Depositor                         | This contract allows multiple depositors to deposit into an Aera vault and receive receipt tokens that represent their units held.                                                                                                                                                  |
| `PriceAndFeeCalculatorV2` | Multi Depositor                         | The default fee calculator contract used in multi depositor vaults. Allows an accountant to submit the value of a single unit over time and calculates fees. Accountant snapshots are carefully managed to prevent sudden gains/losses in reported value from impacting depositors. |
| `ProvisionerV2`           | Multi Depositor                         | The default contract that depositors in a multi-depositor vault interact with to receive or burn their vault units.                                                                                                                                                                 |
| `OracleRegistry`          | All Vaults                              | A ERC7726 compatible registry of oracles that Aera uses for each base/quote token pair.                                                                                                                                                                                             |

**Note**: oracles in this registry are not enforced by default but are used:

1. In common hooks that support trading. For example, trustless swapping on Uniswap requires slippage bounds to be enforced onchain so the Uniswap slippage hook consults the oracle registry for asset prices.
2. To determine exchange rates for deposit assets in multi-depositor vaults. When multi-depositor vaults allow users to deposit in different assets, only one asset is used to price units and the rest are converted via oracle registry oracles.

#### Modifying Aera

There are several ways to modify Aera to suit different use cases. We will discuss them starting with the simplest options.

**Modifying hooks**

The default hooks for each protocol are added to cater for the vast majority of use cases. However, it's possible that your use case requires modifications to these hooks or even new hooks to support new types of protocol integrations. Modifying or adding new hooks is one of the easiest way of supporting more complex strategies in your Aera vault.

**Using a custom oracle registry**

Many of the common rebalancing pre-hooks (such as the Uniswap hooks) rely on the oracle registry to consult asset prices. Some vault owners may want direct control over what oracles they use for pricing, in that case they would need to deploy a custom oracle registry for the vault and use that in all the relevant hooks.

**Extending the single depositor vault**

Another option that teams have to update Aera contract functionality is to inherit directly from the single depositor vault. This could, for example, be used to make Aera compatible with future standards (e.g., account abstraction standards), to add new treasury-specific functionality (e.g., allow some access to treasury funds by a new role type), etc. The same is true for multi depositor vaults.

**Extending the base vault**

The single depositor vault inherits from `BaseVault` which provides most of the core Aera functionality. However, in itself it is not opinionated about questions such as how assets move in and out of the vault. More complex use cases like bridge funds or rollup contracts could extend from the Aera base vault and just leverage Aera to facilitate various strategies while maintaining fine-grained control over how assets move in and out of the Aera vault.


# Contact Us

Please e-mail the Aera team directly if you have interest in learning more about Aera at <info@aera.finance>.


# Aera for Treasury Management

### The Treasury Management Trilemma

With the success of DeFi, protocol treasuries continue to grow and now hold billions in value. As decision making moves to DAOs capital allocation becomes less efficient. Most treasuries today are not able to use this capital effectively. They have had to use one of three suboptimal approaches.

| Approach                          | Description                                                                                                                                                                                                                                                                                                                              |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One-time treasury diversification | A one-off approach to treasury management that covers immediate needs of the DAO but doesn’t address long-term treasury sustainability. This is a principled approach, but one that cannot adapt to evolving needs. Another problem with this approach is direct diversification from a DAO tends to be expensive due to slippage costs. |
| Centralized treasury management   | DAO elects a treasury manager which has a broad remit on what asset class choices and investment strategies they can employ. This breaks a lot of the properties that DAOs seek to achieve like credible neutrality, limiting single points of failure and others.                                                                       |
| Inaction by democracy             | DAOs who care about trustlessness may elect to make large reallocation decisions through votes. Unfortunately this is hindered by misaligned incentives from DAO members and apathetic/uninterested voters.                                                                                                                              |

Faced with three choices that are fundamentally unappealing, DAOs choose to keep their capital largely unallocated.

### Breaking the Treasury Management Trilemma

Aera is a treasury management platform that achieves all three desirable properties: trustless, principled & responsive treasury management.

<figure><img src="/files/W7jjFulC21vZRrUNHT1h" alt=""><figcaption></figcaption></figure>

Here’s how it works.

**Aera achieves responsiveness and efficacy by using off-chain guardians**

Guardians are able to build complicated data pipelines and strategies to ensure that treasuries are managed using best available market instruments, are risk-aware, liquidity-aware and otherwise responsive to market conditions. Allowing for off chain logic means that the strategy space is much wider and not limited to strategies that can only be computed onchain (which is often very small due to computational constraints).

**Aera achieves trustlessness by limiting what guardians can do at the protocol level**

Comprehensive onchain protections are used in each vault that limit the types of actions that a guardian can take and how they can impact the vault. Every action a guardian takes has to be whitelisted. Unlike other solutions for treasury management, Aera strikes a balance on trust and responsiveness by constraining the actions a guardian can take while still allowing them to effectively pursue and achieve treasury objectives.

**Aera achieves principled strategies through the use of a custom objective function for each treasury**

The stated objective function serves as a point of alignment between the DAOs objectives and the guardians’ actions. Guardians are incentivized to act only in ways that improve the objective function of the vault and not limited to simple strategies like seeking yield without consideration of insolvency or other risk vectors. Complex objectives necessitate using multiple strategies together, and Aera can decompose this into a set of strategies that work in tandem (see the Case Study below for an example of this in practice).

### How Treasuries Use Aera today

Over $100M of treasury capital has been allocated in Aera vaults and used to achieve DAO objectives. Aera is able to solve a wide variety of treasury needs in production:

* Portfolio Management including Yield, Execution and Diversification
* Protocol Owned Liquidity and Incentives
* Treasury Operations such as making contributor payments.

For more details and case studies of Aera, see the [Aera blog](https://aera.mirror.xyz/).

Read on if you want to learn how Aera supports a wide variety of use cases and strategies in a safe and principled way.


# BaseVault and core interactions

### What is the `BaseVault`

Every vault in Aera inherits from the `BaseVault` . It is a foundational smart contract in Aera V3 that allows guardians to take vault operations secured by a set of owner-approved hooks. It serves as an abstract base contract for both single depositor and multi-depositor Vaults incorporating features for guardians, vault pausing while being agnostic to how assets move in and out of the vault and how fees are charged.

At its core, the `BaseVault` implements:

* Guardian-based operation execution protected by Merkle proof verification
* Operation chaining & callback handling support
* Configurable pre- and post- operation hooks for extensible behavior
* Pausing functionality
* Mandatory whitelist integration for guardians

The protocol comes with two audited implementations, the `SingleDepositorVault` and `MultiDepositorVault` supporting vaults where a single owner retains custody at all times (treasury management) and vaults that allow multiple depositors to jointly participate in the same strategy (yield).

### Why it matters

`BaseVault` encapsulates “core” Aera functionality (off chain strategies) while retaining flexibility for implementers to define their own extensions. `BaseVaults` can also be used as sub vaults on the same or other chains when a direct deposit/withdraw facility isn't needed.

**Unified security model**:

* Provides a common security foundation for both permissioned (single-depositor) and permissionless (multi-depositor) vaults
* Guardian-based system with Merkle verification ensures only carefully curated operations can be executed on the vault

**Flexible architecture**:

* Submit operations support both simple and advanced execution patterns:
  * Basic operations for standard, zero-value calls
  * Advanced operations with native token transfers, chaining inputs and outputs and listening for callbacks
* Hook system enables vault-specific logic at multiple points:
  * Before/after entire submission batches
  * Before/after individual operations
  * Hooks can have custom code for operation-specific behavior or be configurable if operation calldata will be constrained in a simple way

**Built in vault management workflows**:

* Emergency pause functionality
* Whitelist restrictions on guardians

### How to use `BaseVault`

A `BaseVault` is rarely used directly as it is most often used as an abstract contract for other Aera vaults but the functions it defines are present in every Aera vault.

Core operations on `BaseVault` include:

* Administrative functions:
  * `setGuardianRoot` to elect a new guardian or update their hooks
  * `removeGuardian` to remove a guardian
  * `setSubmitHooks` to configure vault level `beforeSubmit` and `afterSubmit` hooks
  * `pause` to halt vault operations by guardians
  * `unpause` to resume operations
* Guardian functions:
  * `submit` vault operations
  * `pause` to halt vault operations by guardians
* Permissionless functions:
  * `checkGuardianWhitelist` allows anyone to validate whether a guardian is still whitelisted and remove them from vault management if the answer is no

### Caveats

* Guardians MUST be whitelisted through the whitelist contract
* Guardian roots should be managed with extreme care
* `BaseVault` uses the Auth.sol library and therefore retains flexibility on how roles are assigned. The trust model should be carefully considered especially for sensitive operations like unpausing


# Guardians and Strategies

### Who are vault guardians

Each vault has a set of one or more designated guardians elected by the vault owner to implement a specific strategy. Each guardian can submit operations and pause the vault (in the event of a security emergency).

Guardians are sophisticated actors who specialize in using off chain intelligence to achieve onchain objectives. They develop proprietary technology that allows them to effectively implement a wide variety of strategies. Each vault owner can pick the set of vault guardians that are best suited to achieve their objectives or even elect a vault guardians from within their own team.

A guardian **cannot** (among other things):

* Deposit or withdraw any funds/liquidity
* Change what actions they are able to perform by adding/removing oracles or whitelisting hooks
* Elect other guardians

### Why they matter

Guardians implement strategies and help customers get value from Aera vaults. A strategy aligns expectations between customers and guardians, allowing guardians to achieve customer objectives while operating in a predictable way.

Strategies may:

* Have diverse sets of objectives dependent on the goals of depositors
* Interact with different sets of external protocols onchain

See our use cases for examples of strategies.

### How to become a guardian/develop strategies

To become a guardian, please contact our team (contact us).

Strategies are typically developed by professional risk managers and their development is beyond the scope of this document. For the technical requirements of developing a working strategy, see the “For Guardians” section.

### Caveats

While guardians, their addresses and hooks are maintained onchain for each vault, strategies do not have any onchain representation or enforcement other than how individual guardian actions are constrained by the relevant hooks.


# Operations and submit()

### What are vault operations

Vault operations are a series of transaction sequences used by the guardian to rebalance the vault. These transaction sequences are specified in the relevant `submit` call and checked and executed by the vault.

### Why they matter

Operations are submitted to the vault using the `submit` function. The submit function gives guardians a lot of flexibility in how they want to execute operations:

* Any contract can be called and any valid calldata sequence can be an operation
* Each operation can refer to the return values of prior operations and use them to modify calldata using the built-in input/output chaining functionality
* Operations can be read-only (used to read values and use them as input for other operations) or read-write (normal transaction type actions)
* Each operation requires the guardian to submit a Merkle proof proving that the operation complies with its associated hook

But not every calldata sequence is a valid operation. Vault operations are constrained onchain for each guardian. Each vault:

* Prevents any outgoing allowances for ERC20 tokens from being created as a result of an operation
* Restricts the types of operations and contracts that are called in those operations via a Merkle tree for each guardian

### How to create operations

For efficiency reasons, operations are packed in bytes.

The simplest operations will only contain:

* A `target` contract
* Call `data` to call
* Signifier of whether the call is static `isStaticCall`
* A Merkle `proof` to verify that the operation is whitelisted for the active guardian

Most operations will also contain:

* A set of `configurableHookOffsets` to extract variables of interest. This could, for example, be used to constrain what pools on Uniswap the guardian is allowed to swap on

In some cases, operations may contain:

* A set of `clipboards` if outputs from prior operations should be used as inputs for this operation and spliced into the calldata
* `callbackData` if a handler was used, which signifies that an operation expects a callback
* A custom `hook` to support more sophisticated call validation logic
* A `value` if the chain's native token (e.g., ether) should be passed with the submission

### Caveats

The Aera team has developed SDKs in various languages to facilitate the safe creation of operations so please get in touch if you are interested in exploring becoming a guardian further.


# Entry/Exit with Provisioner

### What is the Provisioner?

***NOTE**: These details are only important if you want to directly interface with the Provisioner contract directly. The simplest way to deposit/withdraw is to use the app.*

The Provisioner is the contract that depositors interface to mint or burn vault units in multi-depositor vaults. More technically, it is the only contract that is authorized to mint and burn shares for a given `MultiDepositorVault`.

The Provisioner has the following features:

1. **Synchronous deposits**. Direct deposits allow users to deposit in line with the current vault value. Units are initially locked for a period of time to prevent arbitrage attacks. In extreme cases, the authorized operator can call `refundDeposit` to refund a user's deposit while the units are locked.
2. **Asynchronous deposits/redemptions**. The most general way to enter/exit the vault is by creating a deposit/redemption request. Requests can be filled by solvers at a price that is valid at the time of solving (but still respecting any constraints imposed by the depositor).
3. **Batch solving**. Solvers can solve many orders atomically in a batch.

### Why it Matters

The Provisioner allows users to choose the deposit/redemption policy that expresses their requirements in the best way. Some users want to prioritize rapid entry, others want to place a limit order. Others yet will trust the pricing on the vault but may want to configure the specific solving fee that should go to the solver.

Having the deposit/withdraw logic in a separate contract allows Aera vaults to upgrade their provisioner contracts in the future to support additional mechanisms for entering/exiting the vault.

### How to Use the Provisioner

**`deposit`**

Deposit requires you to specify `token` (the deposit token), `tokensIn` (the amount to deposit) and `minUnitsOut` (a minimum amount of units you expect to receive).

If the deposit succeeds, you'll receive the units but they will be locked for the duration of the deposit refund period to protect other depositors against arbitrage attacks.

The deposit can fail for various reasons:

* Not enough tokens have been provided to mint any units
* `minUnitsOut` is not met at current unit price
* `token` is not an approved deposit token
* synchronous deposits are not enabled for the vault
* the deposit cap would be exceeded

*NOTE: Mint works analogously.*

**`requestDeposit`**

There are two types of asynchronous deposit orders so each argument has slightly different context depending on which order is considered. The order type is specified by setting `isFixedPrice`.

| Argument      | Meaning for fixed price orders                                                                                      | Meaning for auto priced orders |
| ------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `token`       | The token to deposit                                                                                                | Same                           |
| `tokensIn`    | The amount of tokens to deposit                                                                                     | Same                           |
| `minUnitsOut` | Minimum amount of units expected                                                                                    | Same                           |
| `solverTip`   | Needs to be 0. Solver receives difference between tokens needed to deliver `minUnitsOut` and actual units delivered | Tip to offer solver            |
| `deadline`    | Timestamp until which the request is valid                                                                          | Same                           |
| `maxPriceAge` | Maximum age of price data that solver can use                                                                       | Same                           |
| `receiver`    | Address that should receive units on settlement                                                                     | Same                           |

When a request deposit order is made a solver may fill it until the deadline. If the order is a fixed price order, the fill price will be `minUnitsOut` but if the order is an auto price order then the active unit price will be used and the `solverTip` will be calculated and sent to the solver.

If the deadline passes and the order wasn't filled, anyone can call `refundRequest` to refund the order.

*NOTE: Request redeem works analogously.*

**cancelRequest**

For vaults that support cancellations, orders can be cancelled early using `cancelRequest`. A cancellation fee may be applied if this feature is exploited to impact the operations of the guardian and/or solver.

**refundRequest**

Unfilled orders past their deadline can be refunded by calling `refundRequest`.

### Caveats

When using the Provisioner directly, ensure you are familiar with the solver's solving policy. While solvers are operating in a fair manner, the platform in itself cannot provide guidelines that your orders will be successfully filled.


# Fee Vaults

### What is the `FeeVault`?

The `FeeVault` contract extends `BaseVault` with fee functionality. It's used by default in both single depositor and multi depositor implementations and allows guardians and other operators to be rewarded for participating in vault operations.

Since Aera vaults can hold arbitrary assets, the `FeeVault` incorporates a fee calculator contract which computes and provides the vault value. In practice fee calculators operate as an oracle with an accountant submitting the updated vault price over time.

Finally, `FeeVault` adds a single fee recipient which will receive fees while a protocol fee recipient is provided by the fee calculator. In practice the protocol fee recipient is managed by the Aera team and the fee recipient is chosen for each vault by the vault owner or whoever has the required authority.

The fees are received in the form of a designated `FEE_TOKEN`.

There is also a protocol fee recipient which is set in the relevant fee calculator contract.

### Why it Matters

The `FeeVault` provides a consistent interface for fee recipients to claim fees independent of whether it is a single depositor vault or a multi depositor vault. It also abstracts details about what types of fees are charged. Specifically, while the protocol currently provides built-in support for TVL fees and performance fees, other fee types could be added in future fee calculators.

### How to Use the `FeeVault`

The `FeeVault` is built into single and multiple depositor vaults directly. For the vault owner, the choice of fee recipient will likely be related to the choice of guardian.

In the event that multiple different entities are operating as guardians the fee recipient would either be the single entity that is performing Guardian services (as opposed to more operational duties) or the fee recipient could be a splitter contract if there are multiple guardians operating as vault strategists.

To claim fees, the fee recipient should call `claimFees` on the vault directly. Both fee recipient and protocol fee recipient fees are collected when this function is called.

The protocol fee recipient can also trigger a claim separately by calling `claimProtocolFees`.

### Caveats

Depending on which fee calculator contract is used, it's generally appropriate for the fee recipient to be claiming fees regularly as a vault owner can usually remove a fee recipient without prior notice.


# Merkle Trees

### What is the Merkle Tree?

Some guardian actions could be harmful and could lead to lost or frozen funds:

* Withdrawing a position to an address controlled by the guardian
* Swapping tokens without a slippage limit
* Taking too much risk (e.g., through a highly leveraged borrowing position)
* Sending assets to a protocol where they can't be retrieved

Each Aera vault maintains a whitelist of allowed operations which lets vaults reject operations that are not in this list.

Each entry in the Merkle tree supports a single operation type which includes:

* The contract that can be called
* The specific function called of the contract (specified via the sighash)
* The configurable or custom operation hook to use (if applicable), explained below
* Expected arguments returned by the pre-hook

During a submission, each operation is checked using the following steps:

* The guardian provides all the arguments needed to reconstruct the Merkle leaf when calling `submit`
* The pre-hook (provided by the guardian) is called with the calldata of the operation and returns a series of arguments selected for checking
* Since the contract, sighash and hooks are provided by the guardian and the selected arguments have been extracted by the pre-hook, we have everything we need to recreate the Merkle tree leaf. The Merkle tree leaf is then checked for inclusion using the provided Merkle proof
* Next, the operation is executed
* Any post-operation hook is executed (if it exists)
* If neither the pre-hook or post-hook reverts and the operation is in the Merkle tree, the operation is successful

If any operation fails during a submit, then the whole `submit` fails.

### Why It Matters

Previous versions of Aera constrained operations merely by capturing the value of each position onchain. This was simple to reason about and worked well to prevent loss of value. However, the costs of invoking onchain oracles rise with each integration. Moreover, an oracle based approach doesn't support more fine-grained constraints.

A comprehensive whitelist would not only support specific contracts and functions that can be called but also restrictions on specific arguments. To store it onchain in each vault and refer to during each submission would be too prohibitive from a gas perspective.

Merkle trees provide an alternative, very storage-light way to maintain a large whitelist. Only a single Merkle root needs to be maintained in the vault but guardians now have to submit Merkle proofs alongside their operations. The proving process is straightforward to automate but it's important that the vault owner and the guardian both have access to the Merkle tree off chain.

### How to use the Merkle Tree

The Aera team has built various tools to help with the construction of Merkle trees. Please reach out if you would like help.

### Caveats

Note that the Merkle tree that is approved for a guardian can radically impact the trust model of the vault. Aera protects assets against guardians only to the extent that guardians are correctly constrained via Merkle trees. Not doing so can be catastrophic in the event that a guardian is compromised.


# Using Configurable Hooks

### What is a Configurable hook?

Pre-hooks and post-hooks have two important differences:

* **Pre-hooks run before an operation and post-hooks run after an operation**. Generally speaking a pre-hook can be used to check the arguments of the operation whereas a post-hook can be used to validate specific outcomes.
* **Pre-hooks have return values, post-hooks don't**. Merkle leaves in the Merkle tree contain additional arguments that are checked against the return values of a pre-hook. For example, a pre-hook could be defined for a lending protocol to restrict what types of assets can be supplied from the vault. The way to do this would be to extract the asset from the supply function of the given lending protocol and then whitelist acceptable assets in the Merkle tree.

Configurable hooks are pre-hooks that extract information from operation calldata in specific configured positions.

### Why It Matters

They allow the vault owner to define a protocol integration without writing a custom hook contract.

### How to Create a Configurable Hook

Configurable hooks are added in the Merkle tree via a set of configurable hook offsets. These offsets specify indices in calldata from which to extract full words.

If we wanted to use Aera V3 to support depositing and withdrawing from an ERC4626 vault, we could use a pre-hook to do this. Here is the full signature of both operations for reference:

```solidity
function deposit(uint256 assets, address receiver) public returns (uint256 shares)
function withdraw(uint256 assets, address receiver, address owner) public returns (uint256 shares)
```

In order to safely interact with this ERC4626 we can add the following protections:

* Whitelist `deposit` and `withdraw` actions on the specific contract,
* Create a pre-hook for both deposit and withdraw that takes the calldata and returns the `receiver`,
* Create a Merkle leaf that contains a `receiver` that is equal to the address of the vault itself.

Now when a submission happens, it will revert if the guardian tries to send assets to a different recipient than the vault address.

But pre-hooks can be much more complicated and specific than that. For example, a pre-hook could add a daily or total supply cap for each ERC4626 token, it could run integrity checks such as checking whether the corresponding `convert` function agrees with the `preview` function and more depending on the use case.

For that, you need to build a custom hook.

### Caveats

Since configurable hook offsets extract full words, they are not perfectly suited to extract arguments for tightly packed data such as bitmaps. If you need to do so, you will need to develop a custom hook.


# Building Custom Hooks

### What are Custom Hooks?

Custom hooks are smart contracts that intercept and enforce arbitrary logic before and/or after vault operations.

Unlike configurable hooks which merely extract and check function arguments at specific calldata offsets custom hooks allow you to encode far more sophisticated, protocol-specific, or cross-cutting constraints.

Custom hooks use the same function signatures as the operations they are intercepting so they are easy to read.

A custom hook can:

* Interact with external oracles.
* Maintain their own state (such as cumulative slippage).
* Impose various limits.
* Integrate complex calculations that cannot be captured by static calldata offsets.

Custom hooks may be deployed as pre-hooks (executed before an operation, may return values) or post-hooks (executed after, don’t return values).

### Why They Matter

Custom hooks provide the necessary flexibility to incorporate many kinds of onchain constraints. While most commonly these are used for checking slippage bounds for traders or other transfers that require fees (e.g., bridging), custom hooks can support many more complex invariants such as allowing an operation depending on the state of another contract.

### How to Create a Custom Hook

Consider the ERC4626 example we used for configurable hooks. This is how it would look as a custom hook:

```solidity
contract ERC4626Hook {
  function withdraw(uint256 assets, address receiver, address owner) public returns (bytes memory returnData) {
    if (HooksLibrary.isBeforeHook()) {
      return abi.encode(receiver);
    } else if (HooksLibrary.isAfterHook()) {
      // no action to take for this hook
      return bytes("");
    } else {
      // this branch will only happen if the hook
      // is called by a contract other than the vault
    }
  }
}
```

In the most general case, a custom hook will use the `HooksLibrary` to implement actions both for the pre-hook case and the post-hook case.

### Caveats

Custom hooks have a major impact on the trust model and should be audited before being used for production vaults.


# Cross-chain Deployments

### What are Cross Chain Deployments?

Cross-chain deployments allow one Aera vault to access assets on other chains by leveraging other Aera vaults on those chains. Cross-chain deployments mean that each chain has an independent Aera vault with its own guardians and assets. However, the vaults on chains other than the parent vault are just `BaseVault` contracts since they don't need to collect fees separately or even support withdrawals/deposits.

### Why They Matter

Cross-chain access is crucial for incorporating unique assets, finding liquidity for long-tail assets and tapping into the best yields.

### How to do a Cross Chain Deployment

Cross-chain deployments should be planned carefully taking into account the behavior of assets on every chain involved but here is a list of concerns to think about:

* Which chains will be used and whether to use `BaseVault` or another type of vault on those chains?
* Is there a risk of assets becoming irretrievable?
* What address will be the vault owner and guardian on non-parent chains?
* How will the account take into account the value on other chains and value that is moving across the bridge?

### Caveats

Cross-chain deployments can be very risky depending on the specific bridge used. Cross-chain deployments to non-EVM chains are not currently supported.


# Multi Depositor Vaults


# Solving Orders in the Provisioner

### What is the Provisioner?

The Provisioner is an upgradable module in multi-depositor vaults that allows external depositors to enter and exit the vault using various order types and for solvers to fill these orders.

The solver supports 5 types of orders which can be enabled/disabled:

* Asynchronous deposits with a fixed price. A depositor may request a deposit at a fixed price (analogous to a limit order).
* Asynchronous deposits with an “automatic” price. A depositor may request a deposit at the accountant reported price. The price is checked when the order is filled but the depositor can put bounds on acceptable prices.
* Asynchronous redemptions with a fixed/automatic price. These work exactly like asynchronous deposits but allow users to exchange units for underlying assets.
* Synchronous deposits. Units are received instantly but there is a cooldown period for transfers. If the deposit price was found to be inaccurate (e.g., market volatility), the deposit may be refunded during the cooldown period.

Orders can be created in one of the whitelisted underlying assets.

### Why Does It Matter?

Since the accountant submits vault prices, allowing instant non-refundable deposits and redemptions could lead to exploitable arbitrage. To protect all depositors, the Provisioner introduces a solver layer that allows users to make orders and for them to be filled when prices are accurate.

The Provisioner also allows maximum flexibility for users and they can prioritize an order type that optimizes for the factors they care about such as price efficiency (fixed price orders), speed of solving (auto price orders) or price transparency (synchronous deposits), etc.

### How to Solve Orders in the Provisioner?

Anyone can be a direct solver for fixed price orders if they have the offer asset by calling `solveRequestsDirect`, however, they are effectively taking “the other side” of the order and need to be happy to solve at the fixed price. This allows sophisticated market participants another pathway to enter/exit the vault or speed up solving when deposit/redeem orders can be matched.

The Provisioner also may whitelist a number of vault solvers who can fill orders using the assets in the vault in accordance with their solving policy. The `solveRequestsVault` function will then solve a batch of orders as follows:

* If non-empty `preSolveSubmitData` was provided, the Provisioner will first call the vault as a guardian using the `preSolveSubmitData` as the calldata;
* For automatically priced orders, `solveRequestsVault` exchanges the assets between the Provisioner and the vault (at the last reported price) and pay the solver via the provided `solverTip` in the original order;
* For fixed price orders, `solveRequestsVault` solves the orders when the fixed price is favorable to the solver, exchanging assets between the Provisioner and vault (at the last reported price) and returning the difference as a solver tip to the solver. If a solver wants to do an “unprofitable” solve, they can do so via a direct solve;
* If non-empty `postSolveSubmitData` was provided, the Provisioner will last call the vault as a guardian using the `postSolveSubmitData` as the calldata.

The pre- and post- solve submit data are intended to support use cases such as instant liquidity where all vault assets are fully allocated and have to be "freed" atomically.

### Caveats

The solver needs to be aware of the accountant's price reporting policy to solve at the times which will result in the most accurate pricing of vault units.

Each solver should also maintain a consistent solving policy. While the protocol doesn't enshrine any particular policy, the following considerations are important among others:

* `solverTip` policy,
* Solving latency and guarantees,
* Solving during emergencies or moments of price volatility,
* Guarantees around solving order.

For pre- and post- solve submit data to work, the Provisioner needs to be added as a guardian for the vault with its own Merkle tree.


# Reporting Prices and Fees

### What is the `PriceAndFeeCalculatorV2`?

Every `FeeVault` (a vault that is capable of charging fees) has to use the corresponding fee calculator. Each fee calculator is shared among multiple vaults.

For multi depositor vaults the fee calculator also provides pricing which is used as input in the Provisioner. For that reason this contract is called the price and fee calculator.

Each fee calculator designates a special operator called the accountant which submits the vault's unit price on a regular cadence. The price updates are further guarded to prevent staleness or price manipulation and “out of bounds” updates lead to pausing the fee and price calculator for that vault.&#x20;

Two types of fees are derived from the submitted unit prices: TVL fees and performance fees. The price and fee calculator calculates the owed fees and reports them to the vault in the event of a claim.

**How fees are calculated**

TVL fees are accrued in proportion to the vault's TVL (lowest of the last two reported prices multiplied by the lowest of the last two reported total supply of vault units) and the time that has passed since the last reported price. TVL fees are accrued linearly on every price update.

Performance fees are accrued when the last reported price is larger than the highest vault unit price up to the prior price report (saved in `highestPrice`). Performance fees are accrued in proportion to the profit (calculated as the difference between the latest reported price and the `highestPrice`).

A protocol fee based on both TVL and performance is calculated in the same way but with smaller fee rates.

**Anchor and drift pricing**

The accountant can report the unit price using two different lanes. The anchor price is submitted at a regular interval and all parameters (such as expected price volatility) are calibrated against this submission interval. The drift price can be submitted with arbitrary frequency to make sure live prices are accurate.

Anchor prices can be used to diagnose liveness issues in the accountant as well as trigger pausing while drift prices allow greater price accuracy without compromising the anchor guardrails.

### Why It Matters

In order to price user orders and collect fees fairly, each vault needs to be able to calculate the value of its holdings. Since Aera vaults can support arbitrary assets, a universal onchain calculation is not possible. Instead, an accountant needs to compute the price of a vault unit (total holdings divided by total supply of vault units) and report it to the vault.

Doing so naively could lead to accidental price spikes so the `PriceAndFeeCalculator` embeds several protections on the range of prices that can be submitted and when they can be submitted.

### How to Report Prices as an Accountant

The designated vault accountant can call `setAnchorPrice`, naming the `vault`, the reported `timestamp` and the vault unit `price` at that timestamp.

The price is accepted if:

* The price is nonzero;
* The timestamp provided is after the last price update but not in the future;
* The timestamp is not too far into the future (meets `maxPriceAge` threshold). This avoids stale prices being submitted.

**pauseOnBadAnchorUpdate**

A `pauseOnBadAnchorUpdate` parameter can be set via `setPauseOnBadAnchorUpdate` to control whether an out of bounds anchor update submission should automatically pause the price and fee calculator.

If true, after the price is accepted, the price and fee calculator is immediately paused for that vault:

* If price was submitted too soon (under the `minUpdateInterval`);
* If price was submitted too late (longer than `maxUpdateDelayDays` since the last update time);
* Or if the price is larger than `currentPrice * maxPriceToleranceRatio` or lower than `currentPrice * minPriceToleranceRatio`.

If `pauseOnBadAnchorUpdate` is false, then invalid anchor submissions will simply revert.

Out of bounds drift price submissions will always revert.

It's important that the accountant uses a consistent policy for price submissions and that the solver is aware of this policy. We describe two valid policies that can be used for submitting anchor prices.

**Proactive price submission**

This is the simplest price reporting policy. The accountant submits prices on a predictable interval. The solver fills orders immediately after each price update. During price updates, the reported price may deviate from the vault's holdings.

**Reactive price submission**

In this model the accountant aims to prevent the reported price from deviating beyond a certain threshold (e.g., 0.5%). If the price is close to deviating, the accountant reports a price. In this model the solver can fill orders at any point as the accountant prevents the price from getting stale. Normally these can be submitted as drift prices, except for when the drift price would be out of bounds, the accountant could elect to submit an anchor price to trigger a safety pause.

Note the onchain protections don't enforce a specific policy but are compatible with both.

### Caveats

Since the vault does not maintain a highest price watermark for each depositor, in rare cases (e.g., after a significant price drop) the vault may need to reset the highest price watermark using `resetHighestPrice`.


# Using Transfer Hooks

### What are Transfer Hooks?

Aera transfer hooks exist to enable custom functionality during vault unit transfers such as whitelist/blacklist checks. Transfer hooks can embed arbitrary logic ranging from simple acceptance checks to more stateful logic such as rewards management.

### Why They Matter?

Many vault owners need to enforce restrictions on who can receive vault units. This enables permissioned vaults or can be used to support a holistic compliance strategy.

### How to Choose a Transfer Hook?

A vault transfer hook needs to implement a `beforeTransfer` function which accepts the `from` address, `to` address and the `transferAgent`. Two hooks are available to vault owners out-of-the-box: the `TransferWhitelistHook` and the `TransferBlacklistHook`.

The `TransferWhitelistHook` maintains a list of whitelisted addresses and only allows vault units to be sent to whitelisted addresses. The whitelist hook can be used in permissioned vaults with a fixed set of participants.

The `TransferBlacklistHook` can be configured with a blacklist oracle. It's compatible with the Chainalysis sanctions oracle interface but can be used with any custom blacklist.

### Caveats

Hooks can be updated using `setBeforeTransferHook`.


# Single Depositor Vaults


# Operating SingleDepositorVault

### What is the Single Depositor Vault?

The `SingleDepositorVault` is a vault structure that is designed to operate on funds owned by a single logical owner. It enables basic Aera functionality (guardianship, pausing, etc.) and also enables the vault owner to take actions on behalf of the vault such as depositing, withdrawing and arbitrary execution.

The `SingleDepositorVault` adds the following features over `FeeVault`:

1. **Direct deposits / withdrawals**. The vault owner can deposit / withdraw any ERC20 tokens.
2. **Execution**. The vault owner can use the vault as a wallet, retaining direct custody and the ability to execute with the vault's funds at all times.

We also provide a default fee calculator implementation `DelayedFeeCalculator` which should be used in conjunction with the `SingleDepositorVault`.

### Why it Matters

The `SingleDepositorVault` is can be used to support the following scenarios:

* **Treasury management**. A treasury could be managed with a `SingleDepositorVault`. In this sense the `SingleDepositorVault` is a direct upgrade over the Aera V2 vault allowing the same functionality but with a much more advanced guardian interface.
* **Complex integrations**. For example, creating a Yearn vault from an Aera vault could be done by using a wrapper over the single depositor vault and encoding owner actions. The recommended approach, however, would be to create a new type of vault that directly inherits from `BaseVault`.

### How to Use the `SingleDepositorVault`

**`deposit`**

Deposit requires you to specify an array of `TokenAmount[]` where each entry has a `token` (the deposit token) and `amount` (the amount to deposit). Each ERC20 token will be directly deposited and doesn't need to exist on a whitelist.

`withdraw` works analogously but for redemptions.

**`execute`**

This function allows the vault owner to execute arbitrary actions by passing an array of `OperationPayable[]` (with the conventional `target`, `data` and `value` arguments). Note that `execute` can also be used to transfer tokens out of the vault (among many other things).

No whitelist of actions or hooks are enabled on execute operations (unlike `submit` constraints for guardians).

### Caveats

Note that no tokens are withheld even if unclaimed fees exist in the vault during a `withdraw` or `execute` operation. The implicit trust model is that fees have to be claimed regularly by the fee recipient.


# Reporting Fees

### What is the `DelayedFeeCalculator`?

Every `FeeVault` (a vault that is capable of charging fees) has to use the corresponding fee calculator. For single-depositor vaults the fee calculator operates on a review model where the each snapshot is automatically approved once the dispute period is passed.

Each fee calculator designates a special operator called the accountant which submits the vault's fee inputs on a regular cadence (the variables required to calculate earned TVL and performance fees).

### Why it Matters

In order to collect fees fairly, each vault needs to be able to calculate the value of its holdings. Since Aera vaults can support arbitrary assets, a universal onchain calculation is not possible. Instead, an accountant needs to compute the fees accumulating and report it to the vault.

Doing so naively could lead to accidental price spikes so the `DelayedFeeCalculator` provides a dispute period within which the vault owner could effectively reject the latest proposed vault fee calculation.

### How to Report Prices as an Accountant

The designated vault accountant can report a snapshot using `submitSnapshot` with the following arguments:

* `vault` is the vault to submit the snapshot for (a single `DelayedFeeCalculator` supports multiple vaults)
* `averageValue` is the average vault value over the period between the last snapshot and the current snapshot (used for TVL calculation)
* `highestProfit` is the highest overall profit achieved so far
* `timestamp` is the timestamp when the snapshot was computed

### Caveats

When a new snapshot is submitted but the dispute period hasn't passed for the previous snapshot, the previous snapshot will be overridden.


# Chaining Operations

### What is input/output chaining?

Guardians can use arbitrary calldata in operations, however, not all callback data is known when the submit `transaction` is created. A common example is chaining operations together, for example, when swapping WETH to USDC and then depositing all of the USDC into a lending protocol. The amount of USDC obtained can vary depending on available liquidity and can often not be predicted when the original operation is created. For this reason Aera supports input/output chaining for guardian operations.

Static functions are an important addition to input/output chaining. Aera can be instructed to perform a static call to a contract in an operation which would then make the return value available as an input for future operations.

### Why They Matter

Chaining inputs and outputs is crucial to avoid inefficiency or unnecessary transactions. When an operation cannot predict the exact amount to use, it will likely lean on being conservative and leaving dust behind in the contract.

Solvers have been using sophisticated routing contracts to facilitate chaining (sometimes through opcode VMs) and some protocols (e.g., Uniswap) now have built in chaining, however, universal cross-protocol operation chaining has not been available for guardians.

With input/output chaining, static calls allow additional efficiency and safety ensuring that calls used only for providing a return value are not stateful.

### How to use input/output chaining

Every operation passed to submit may have a clipboard. The clipboard argument has a length (1 byte field) and a series of 4-byte entries:

* `resultIndex` (1 byte): which prior operation to take the return value from;
* `copyWord` (1 byte): which 32-byte word in the return value to copy;
* `pasteOffset` (2 bytes): which offset in the current calldata to override with the word that is being copied.

As Aera executes operations as part of a `submit` call, it will go through the following flow:

* Load the initial calldata as specified in the operation;
* Check if a clipboard is specified for the given operation;
* Iterate over each clipboard element. For each element, extract the given word from the result of a prior operation and override the calldata at the paste offset;
* Proceed as normal with the final modified calldata (check pre-hooks, run operation, check post-hooks).

To use static calls, you just need to set the `isStaticCall` flag in submit. Return values from static calls can be read with the clipboard in the same way.

### Caveats

Guardians have to ensure that copy word offsets are within bounds of return data to enjoy predictable results.


# Receiving Callbacks

### What are Callback Handlers?

The hooks system is very flexible and allows guardians to invoke arbitrary operations, however, some interactions may trigger callbacks into the Aera Vault. A common instance is flash loans which need to be paid back atomically and therefore only provide access to funds through a callback.

Callback handlers provide a way for guardians to prepare for a callback by “queueing up” additional operations that would be executed during the callback. These operations are protected by the same Merkle tree (see [Merkle Trees](/merkle-trees)) and hooks (see [Using Configurable Hooks](/using-configurable-hooks)) as general Aera vault operations (see [Operations and submit()](/operations-and-submit)).

### Why They Matter

Aera Vaults can inherit from `BaseVault` and add functions to respond to callbacks. However, this approach defeats the purpose of the Aera system which is to allow guardians to take flexible operations that are informed by off chain computation with onchain protections.

Callback handlers provide significantly more flexibility and together with input/output chaining allow guardians to implement complex multi-step operations that were previously only available to MEV bots.

### How to use Callback Handlers

To invoke a callback handler, you need to do the following:

* Decide which operation will trigger a callback into the Aera vault
* Call submit and for that operation:
  * Set `hasCallback` to 1,
  * Provide `selector`, the function selector for the callback function that the vault will be called with,
  * Provide `calldataOffset` the specific byte in the calldata that Aera Vault should extract the operations for,
  * Provide `caller` , the contract that will call the Aera Vault.

Whenever the callback occurs, the Aera Vault will check if the callback is expected with the given selector and caller. If so, it will decode the series of operations starting at the calldata offset and execute them while complying to the guardian's Merkle tree.

Consider Morpho flash loans as an example. The following would happen:

* The guardian creates a submit transaction with the operation `morpho.flashLoan` ;
* `flashLoan` accepts 3 arguments: `token`, `amount` and `data`. The guardian encodes all the operations which should trigger on the callback in the `data` argument;
* The guardian also adds a callback to the operation using the selector of `onMorphoFlashLoan(amount, data)` (`0x31f57072`) using `morpho` as the caller (`0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb`) and specifying the calldata index of `data` variable in `onMorphoFlashLoan` (100). Note that 100 is chosen because we need to skip the pointer and the length variable as `CalldataReader` uses a more compact length encoding;
* Now `submit` executes and calls the `morpho` contract;
* Morpho executes the `onMorphoFlashLoan` callback which is handled by Aera. Morpho forwards all `data` provided to flashLoan in the `data` argument. In the handler, the vault decodes operations and runs them from the handler.

### Caveats

Callback handlers only exist during a given `submit` call by a guardian. It means that they cannot be used for asynchronous interactions (such as receiving a settlement order in response to an RFQ) and that the same callback function can trigger different operations in different `submit` calls. To add new functions to an Aera vault, an extension to `BaseVault` is recommended.

Only “pull”-based flash loans are currently supported because push-based flash loans require a transfer operation to dynamically allow repayment based on how much was borrowed. Push-based flash loans can still be supported by creating a dedicated handler contract.


# Oracle Registry

### What is the Oracle Registry?

The Oracle Registry is a global contract that provides standardized price oracle access for Aera's ecosystem while being ERC7726 compliant for external consumers. It serves two primary functions:

1. **DEX hook integration**: Controls slippage in trades by providing onchain price quotes for asset pairs. This ensures trades initiated by guardians execute within acceptable price ranges, protecting users from adverse price movements. For example when swapping ETH to USDC on Uniswap, it's important for a vault to specify a reasonable minimum output amount of USDC and not rely on the guardian to provide a value. In this case the specific hook supporting Uniswap will refer to the oracle registry for the correct price.
2. **Multi-depositor vault exchange rate calculations**: Facilitates exchange rate calculations between different deposit/withdraw assets in multi-depositor vaults. This enables vaults to accurately value different assets and maintain proper accounting of user deposits and withdrawals.

### Why it Matters

The oracle registry brings several key benefits to the Aera ecosystem:

1. **Shared infrastructure**: Multiple vaults can reuse the same oracle registry, eliminating the need for each vault to maintain its own oracle setup. This is an improvement over the setup in Aera V2 where each vault had its own asset registry.
2. **Simplicity**:
   * The oracle registry standardizes interfaces by being ERC7726 compliant, making integration simpler
   * Using a quote-based interface reduces the need to embed oracle related safeguards into other parts of the codebase. For example, Chainlink oracles have built-in heartbeats and sequencer liveness checks that can all be executed in the oracle registry
   * Since ERC7726 contracts can serve multiple quote pairs, oracle registry reduces duplication in code paths without increasing efficiency since oracle data can be cached in code paths.

### How to Use the Oracle Registry

**Using the oracle registry in Aera**

The primary Oracle Registry is managed by the Aera team, ensuring high-quality oracle data and regular maintenance.

Teams are still free to deploy and maintain their own oracle registries if they want to make different price feed choices. Vault owners can deploy hooks that use the global oracle registry, use a different oracle registry or even use different oracles per hook (although we would advise against this).

**Maintaining an oracle registry and adding new oracles**

Two types of oracles can be added: Chainlink-compatible price feeds and other ERC7726 compliant oracles. Repositories for ERC7726 such as [awesome-oracles](https://github.com/alcueca/awesome-oracles) can be a good source of oracles for maintainers.

Note that while new oracles can be added liberally, oracles that already exist could be used by vaults and therefore are subject to a 21 day update delay. Each vault can accept an oracle early, however, and accelerate the update process.

Maintainers have access to the following operations:

* `addOracle` sets an oracle for a given asset pair
* `scheduleOracleUpdate` start an oracle update for a pair which already has an oracle (this minimizes disruption and risk for consumers)
* `commitOracleUpdate` can be called by anyone to commit a scheduled oracle update
* `cancelScheduledOracleUpdate` cancel an active oracle update
* `disableOracle` stop posting a price feed for a given base / quote pair
* `acceptPendingOracle` vault owners can use this function to accept oracle updates early

**Caching**

To make the development of composite oracles more efficient, the oracle registry has a hinting system for oracle data. For example, to build a product oracle that computes the price of X/Y \* Y/Z, you would first load the oracleData for X/Y and Y/Z feeds in the constructor and then use `getQuoteUnsafe` by passing the relevant oracleData. In the event the underlying feeds are upgraded, a new product oracle for the composite feed would need to be deployed.

### Caveats

Note that the oracle registry uses the ERC7726 standard in two ways:

* The oracle registry itself is an ERC7726 compatible contract and can be used onchain by other consumers to price tokens
* Other ERC7726 compatible tokens can be used by the oracle registry to define prices for common tokens.


# Security

### How Aera Protects Depositors

The Aera protocol is designed from the ground up to provide the most secure vault operating model without compromising flexibility in assets and strategies. At one extreme, a multisig allows ultimate flexibility for permissioned roles to act but does not offer any form of protection. At the other extreme are protocols which hard-code specific actions that guardians may take but fail to adapt to the needs of various strategies and market conditions.

**Guardians**

Aera guardians have the best of both worlds (see [Guardians and Strategies](/guardians-and-strategies)). They can take any actions but these actions have to be pre-approved by the vault owner and are protected by efficient onchain hooks. Hooks protect the vault in various ways, for example, limiting per-trade and daily slippage on trades and bridging fees, restricting interactions to specific protocols, vaults, assets and users, constraining borrowing factors and so on. Since hooks can be implemented as smart contracts (see [Building Custom Hooks](/building-custom-hooks)), constraints of arbitrary complexity can be enforced to support robust strategies.

**Role Separation**

Aera has granular vault and function-level permissions for the majority of actions. Each operation can have a designated number of addresses that may perform it. Uniquely, Aera can support complicated use cases with multiple guardians, for example, allowing one guardian to govern reward management/claiming and another to operate a diversification strategy (see [Auth2Step](/the-protocol/core/auth2step)).

**Modular design**

The Aera contracts are modular by design. Each vault contract is immutable but allows the vault owner to upgrade different modules such as the `Provisioner` (governs entry/exit) and `FeeCalculator` (calculates fees).  In practice, these upgrades are further protected by a timelock to allow depositors to observe and react to any changes.

### Audits

All relevant issues identified by auditors were addressed prior to the launch of V3.

<table><thead><tr><th width="150">Auditor</th><th width="223">Scope</th><th>Report</th></tr></thead><tbody><tr><td>Spearbit</td><td>V3</td><td><a href="https://drive.google.com/file/d/1YYJI6AIzcJku0VfWqDxyx7Jn7m0nIjtE/view?usp=sharing">Spearbit-June-2025.pdf</a></td></tr><tr><td>Spearbit</td><td>V2</td><td><a href="https://github.com/aera-finance/aera-contracts-public/blob/main/v2/audits/spearbit/2023-09-22.pdf">Spearbit-August-2023.pdf</a></td></tr><tr><td>OpenZeppelin</td><td>V2 LlamaPay Integration</td><td><a href="https://github.com/aera-finance/aera-contracts-public/blob/main/v2/audits/openzeppelin/2024-05-15.pdf">OpenZeppelin-May-2024.pdf</a></td></tr></tbody></table>

### **Competition**

We completed a Cantina competition for Aera V3: <https://cantina.xyz/competitions/ffe90f03-ffd0-449b-a15f-6e7702323d16>.

### Bug Bounty

Aera currently has an active bug bounty at Immunefi: <https://immunefi.com/bug-bounty/aera/information/>.

### **Our Security Framework**

We have designed a security framework to mitigate the probability and severity of human error. We actively look for opportunities to add relevant process steps and tooling to our security arsenal.

<figure><img src="/files/MNeP7bCnaqLsbOmS1vDj" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/CC4nNwiFL0FwNtamJoEH" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/TGlxwoWf7isegMLugnFH" alt=""><figcaption></figcaption></figure>

### **Highlighted Risks**

While these are by no means exhaustive, we think the following risks are helpful to understanding broader vault operation.

**Front running risk**

On chains and assets where Aera relies on onchain execution through AMMs, there is a risk for price manipulation and loss of value in the vault. While operation hooks currently limit slippage, we recommend only deploying Aera with a trusted guardian as a malicious guardian could atomically sandwich a transaction they initiated and leak value up to the slippage bound.

**Limits to real-time response**

While guardian code is automated, it's impossible to foresee every possible risk (depeg, hack, etc.) and sometimes urgent actions will be required that the contracts may not support (due to oracle issues in conjunction with these market movements). For treasury management, the vault is never reliant on the guardian to take actions as the owner has direct access to the `execute` function.

**Guardian submissions**

The guardian submission process relies on an off-chain algorithm. While we have worked hard to mitigate the power of the guardian role in the contracts, errors in the off-chain code (for example due to errors in data received from an ETL provider) could lead to incorrect operations being submitted to the vault or a missed submission. The vault owner has the power to stop vault operations at any point and to remove the guardian role.

**Collusion**

If roles are not assigned carefully, value could be extracted from the vault. The current solution mitigates this by only enlisting highly trusted entities as guardians.

**Oracle quality**

Aera's safeguards rely on onchain oracles to protect guardian actions. While we aim to identify the highest quality oracle for each asset, less liquid assets on less popular chains may carry more oracle risks. When lower quality oracles have to be used, guardian power should be mitigated through the operation whitelist in the Merkle tree.

**Dependencies**

Aera cannot protect against hacks in the DeFi protocols we interact with. However, we take a conservative approach to proposing new integrations and strategies. The Merkle tree only allows the guardian to interact with pre-approved contracts.

**Pricing Risk**

Aera V3 vaults are priced by an accountant and could be subject to errors if the accountant service submits an incorrect price due to failures in underlying APIs. The protocol mitigates this for single-depositor vaults by introducing a substantial review delay until new vault values are accepted and in multi-depositor vaults through the use of an independent solver and bounds around price changes.

**Solving Risk**

Since Aera withdrawals are served by solvers, it's possible that asset withdrawals are delayed past the intended filling frequency. In rare cases (e.g., bridge delays for cross-chain assets) guardians may be delayed in their ability to free up the necessary capital to support a withdrawal. Solving may also be delayed if a vault is paused due to large price fluctuation or accountant service error.

Please [Contact Us](/contact-us) to learn more.


# Core


# Auth2Step

**Inherits:** IAuth2Step, Auth

An extension of Auth.sol that supports two-step ownership transfer

## State Variables

### pendingOwner

Address of the pending owner

```solidity
address public pendingOwner;
```

## Functions

### onlyOwner

```solidity
modifier onlyOwner() virtual;
```

### constructor

```solidity
constructor(address newOwner_, Authority authority_) Auth(newOwner_, authority_);
```

### acceptOwnership

Accept ownership transfer

```solidity
function acceptOwnership() external virtual override;
```

### transferOwnership

Wrapper function for backward compatibility with legacy code expecting transferOwnership

*This function exists to maintain compatibility with contracts that were built against the previous version of Auth where ownership transfer was named `transferOwnership` The new Auth implementation renamed this to `setOwner` to better reflect the two-step ownership transfer process. This wrapper ensures existing code like BaseVault continues to work without modification Previous version: <https://github.com/transmissions11/solmate/blob/89365b880c4f3c786bdd453d4b8e8fe410344a69/src/auth/Auth.sol> New version: <https://github.com/transmissions11/solmate/blob/eaa7041378f9a6c12f943de08a6c41b31a9870fc/src/auth/Auth.sol>*

```solidity
function transferOwnership(address newOwner) public virtual onlyOwner;
```

**Parameters**

| Name       | Type      | Description                                        |
| ---------- | --------- | -------------------------------------------------- |
| `newOwner` | `address` | Address to start the ownership transfer process to |

### setOwner

Start the ownership transfer of the contract to a new account

*Replaces the pending transfer if there is one*

*Overrides the `Auth` contract's `transferOwnership` function*

*Zero check is not needed because pendingOwner can always be overwritten*

```solidity
function setOwner(address newOwner) public virtual override onlyOwner;
```

**Parameters**

| Name       | Type      | Description                      |
| ---------- | --------- | -------------------------------- |
| `newOwner` | `address` | Address to transfer ownership to |


# BaseFeeCalculator

**Inherits:** IBaseFeeCalculator, IFeeCalculator, Auth2Step, VaultAuth

Module used with FeeVault to allow an off-chain accountant to submit necessary inputs that help compute TVL and performance fees owed to the vault. Serves as a central registry for all vaults and their associated fees

## State Variables

### protocolFees

The protocol's fee configuration

```solidity
Fee public protocolFees;
```

### protocolFeeRecipient

The address that receives the protocol's fees

```solidity
address public protocolFeeRecipient;
```

### \_vaultAccruals

A mapping of vault addresses to their associated state

```solidity
mapping(address vault => VaultAccruals vaultAccruals) internal _vaultAccruals;
```

### vaultAccountant

A mapping of vault addresses to their assigned accountant

```solidity
mapping(address vault => address accountant) public vaultAccountant;
```

## Functions

### onlyVaultAccountant

Modifier that checks the caller is the accountant assigned to the specified vault

```solidity
modifier onlyVaultAccountant(address vault);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority) Auth2Step(initialOwner, initialAuthority);
```

### setProtocolFeeRecipient

Set the protocol fee recipient

```solidity
function setProtocolFeeRecipient(address feeRecipient) external requiresAuth;
```

**Parameters**

| Name           | Type      | Description                               |
| -------------- | --------- | ----------------------------------------- |
| `feeRecipient` | `address` | The address of the protocol fee recipient |

### setProtocolFees

Set the protocol fee rates

```solidity
function setProtocolFees(uint16 tvl, uint16 performance) external requiresAuth;
```

**Parameters**

| Name          | Type     | Description                              |
| ------------- | -------- | ---------------------------------------- |
| `tvl`         | `uint16` | The TVL fee rate in basis points         |
| `performance` | `uint16` | The performance fee rate in basis points |

### setVaultAccountant

Set the accountant for a vault

```solidity
function setVaultAccountant(address vault, address accountant) external requiresVaultAuth(vault);
```

**Parameters**

| Name         | Type      | Description                       |
| ------------ | --------- | --------------------------------- |
| `vault`      | `address` | The address of the vault          |
| `accountant` | `address` | The address of the new accountant |

### registerVault

Register a new vault with the fee calculator

```solidity
function registerVault() external virtual;
```

### setVaultFees

Set the vault-specific fee rates

```solidity
function setVaultFees(address vault, uint16 tvl, uint16 performance) external requiresVaultAuth(vault);
```

**Parameters**

| Name          | Type      | Description                              |
| ------------- | --------- | ---------------------------------------- |
| `vault`       | `address` | The address of the vault                 |
| `tvl`         | `uint16`  | The TVL fee rate in basis points         |
| `performance` | `uint16`  | The performance fee rate in basis points |

### claimFees

Process a fee claim for a specific vault

*Expected to be called by the vault only when claiming fees Only accrues fees and updates stored values; does not transfer tokens Caller must perform the actual transfers to avoid permanent fee loss*

```solidity
function claimFees(uint256 feeTokenBalance) external virtual returns (uint256, uint256, address);
```

**Parameters**

| Name              | Type      | Description                               |
| ----------------- | --------- | ----------------------------------------- |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute |

**Returns**

| Name     | Type      | Description                                                                  |
| -------- | --------- | ---------------------------------------------------------------------------- |
| `<none>` | `uint256` | earnedFees The amount of fees to be claimed by the fee recipient             |
| `<none>` | `uint256` | protocolEarnedFees The amount of protocol fees to be claimed by the protocol |
| `<none>` | `address` | protocolFeeRecipient The address of the protocol fee recipient               |

### claimProtocolFees

Process a protocol fee claim for a vault

*Expected to be called by the vault only when claiming protocol fees Only accrues protocol fees and updates stored values; does not transfer tokens Caller must perform the actual transfers to avoid permanent protocol fee loss*

```solidity
function claimProtocolFees(uint256 feeTokenBalance) external virtual returns (uint256, address);
```

**Parameters**

| Name              | Type      | Description                               |
| ----------------- | --------- | ----------------------------------------- |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute |

**Returns**

| Name     | Type      | Description                                                    |
| -------- | --------- | -------------------------------------------------------------- |
| `<none>` | `uint256` | accruedFees The amount of protocol fees claimed                |
| `<none>` | `address` | protocolFeeRecipient The address of the protocol fee recipient |

### previewFees

Returns the current claimable fees for the given vault, as if a claim was made now

```solidity
function previewFees(address vault, uint256 feeTokenBalance) external view virtual returns (uint256, uint256);
```

**Parameters**

| Name              | Type      | Description                                                                                                                                                                               |
| ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vault`           | `address` | The address of the vault to preview fees for                                                                                                                                              |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute If set to `type(uint256).max`, the function returns all accrued fees If set to an actual balance, the result is capped to that claimable amount |

**Returns**

| Name     | Type      | Description                                          |
| -------- | --------- | ---------------------------------------------------- |
| `<none>` | `uint256` | vaultFees The amount of claimable fees for the vault |
| `<none>` | `uint256` | protocolFees The amount of claimable protocol fees   |

### \_beforeClaimFees

Hook called before claiming fees

*Can be overridden by child contracts to add custom logic*

```solidity
function _beforeClaimFees() internal virtual;
```

### \_beforeClaimProtocolFees

Hook called before claiming protocol fees

*Can be overridden by child contracts to add custom logic*

```solidity
function _beforeClaimProtocolFees() internal virtual;
```

### \_calculateTvlFee

Calculates the TVL fee for a given period

*Fee is annualized and prorated for the time period*

```solidity
function _calculateTvlFee(uint256 averageValue, uint256 tvlFee, uint256 timeDelta) internal pure returns (uint256);
```

**Parameters**

| Name           | Type      | Description                               |
| -------------- | --------- | ----------------------------------------- |
| `averageValue` | `uint256` | The average value during the period       |
| `tvlFee`       | `uint256` | The TVL fee rate in basis points          |
| `timeDelta`    | `uint256` | The duration of the fee period in seconds |

**Returns**

| Name     | Type      | Description        |
| -------- | --------- | ------------------ |
| `<none>` | `uint256` | The earned TVL fee |

### \_calculatePerformanceFee

Calculates the performance fee for a given period

```solidity
function _calculatePerformanceFee(uint256 profit, uint256 feeRate) internal pure returns (uint256);
```

**Parameters**

| Name      | Type      | Description                              |
| --------- | --------- | ---------------------------------------- |
| `profit`  | `uint256` | The profit during the period             |
| `feeRate` | `uint256` | The performance fee rate in basis points |

**Returns**

| Name     | Type      | Description                |
| -------- | --------- | -------------------------- |
| `<none>` | `uint256` | The earned performance fee |


# BaseVault

**Inherits:** IBaseVault, Pausable, CallbackHandler, ReentrancyGuardTransient, Auth2Step, IERC721Receiver

This contract embeds core Aera platform functionality: the ability to enlist off-chain guardians to take guarded actions on a vault. It is meant to either be extended with deposit/withdraw capabilities for users or used directly. When used directly, a depositor can simply transfer assets to the vault and a guardian can transfer them out when needed Registered guardians call the submit function and trigger vault operations. The vault may run before and after submit hooks and revert if a guardian is using an unauthorized operation. Authorized operations are configured in an off-chain merkle tree and guardians need to provide a merkle proof for each operation. In addition to validating operation targets (the contract and function being called), the merkle tree can maintain custom per-operation hooks that extract specific parts of the calldata for validation or even perform (possibly stateful) validation during the submit call

## State Variables

### HOOK\_CALL\_TYPE\_SLOT

ERC7201-compliant transient storage slot for the current hook call type flag

*Equal to keccak256(abi.encode(uint256(keccak256("aera.basevault.hookCallType")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant HOOK_CALL_TYPE_SLOT = 0xb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e00;
```

### WHITELIST

The whitelist contract that controls vault permissions

```solidity
IWhitelist public immutable WHITELIST;
```

### submitHooks

Address of the submit hooks contract for vault-level operations

```solidity
ISubmitHooks public submitHooks;
```

### guardianRoots

Enumerable map of each guardian address to their merkle root

```solidity
EnumerableMap.AddressToBytes32Map internal guardianRoots;
```

## Functions

### onlyAuthOrGuardian

Ensures caller either has auth authorization requiresAuth (owner or authorized role) or is a guardian

```solidity
modifier onlyAuthOrGuardian();
```

### constructor

```solidity
constructor() Pausable() Auth2Step(msg.sender, Authority(address(0)));
```

### receive

Receive function to allow the vault to receive native tokens

```solidity
receive() external payable;
```

### submit

Submit a series of operations to the vault

```solidity
/// @notice Submit a series of operations to the vault
/// @param data Encoded array of operations to submit
/// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
/// │ FIELDS                      │ SIZE                    │ DESCRIPTION                                   │
/// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
/// │ operationsLength              1 byte                    Number of operations in the array             │
/// │                                                                                                       │
/// │ [for each operation]:                                                                                 │
/// │                                                                                                       │
/// │   SIGNATURE                                                                                           │
/// │   target                      20 bytes                  Target contract address                       │
/// │   calldataLength              2 bytes                   Length of calldata                            │
/// │   calldata                    <calldataLength> bytes    Calldata (before pipelining)                  │
/// │                                                                                                       │
/// │   CLIPBOARD                                                                                           │
/// │   clipboardsLength            1 byte                    Number of clipboards                          │
/// │   [for each clipboard entry]:                                                                         │
/// │       resultIndex             1 byte                    Which operation to take from                  │
/// │       copyWord                1 byte                    Which word to copy                            │
/// │       pasteOffset             2 bytes                   What offset to paste it at                    │
/// │                                                                                                       │
/// │   CALL TYPE                                                                                           │
/// │   isStaticCall                1 byte                    1 if static, 0 if a regular call              │
/// │   [if isStaticCall == 0]:                                                                             │
/// │                                                                                                       │
/// │     CALLBACK HANDLING                                                                                 │
/// │     hasCallback               1 byte                    Whether to allow callbacks during operation   │
/// │     [if hasCallback == 1]:                                                                            │
/// │       callbackData =          26 bytes                  Expected callback info                        │
/// │       ┌────────────────────┬──────────────────────────┬───────────────────┐                           │
/// │       │ selector (4 bytes) │ calldataOffset (2 bytes) │ caller (20 bytes) │                           │
/// │       └────────────────────┴──────────────────────────┴───────────────────┘                           │
/// │                                                                                                       │
/// │     HOOKS                                                                                             │
/// │     hookConfig =              1 byte                    Hook configuration                            │
/// │     ┌─────────────────┬────────────────────────────────────────┐                                      │
/// │     │ hasHook (1 bit) │ configurableHookOffsetsLength (7 bits) │                                      │
/// │     └─────────────────┴────────────────────────────────────────┘                                      │
/// │     if configurableHookOffsetsLength > 0:                                                             │
/// │         configurableHookOffsets 32 bytes                Packed configurable hook offsets              │
/// │     if hasHook == 1:                                                                                  │
/// │         hook                 20 bytes                   Hook contract address                         │
/// │                                                                                                       │
/// │     MERKLE PROOF                                                                                      │
/// │     proofLength              1 byte                     Merkle proof length                           │
/// │     proof                    <proofLength> * 32 bytes   Merkle proof data                             │
/// │                                                                                                       │
/// │     PAYABILITY                                                                                        │
/// │     hasValue                 1 byte                     Whether to send native token with the call    │
/// │     [if hasValue == 1]:                                                                               │
/// │       value                  32 bytes                   Amount of native token to send                │
/// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
function submit(bytes calldata data) external whenNotPaused nonReentrant;
```

**Parameters**

| Name   | Type    |
| ------ | ------- |
| `data` | `bytes` |

### setGuardianRoot

Set the merkle root for a guardian Used to add guardians and update their permissions

```solidity
function setGuardianRoot(address guardian, bytes32 root) external virtual requiresAuth;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `guardian` | `address` | Address of the guardian |
| `root`     | `bytes32` | Merkle root             |

### removeGuardian

Removes a guardian from the vault

```solidity
function removeGuardian(address guardian) external virtual requiresAuth;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `guardian` | `address` | Address of the guardian |

### checkGuardianWhitelist

Check if the guardian is whitelisted and set the root to zero if not Used to disable guardians who were removed from the whitelist after being selected as guardians

```solidity
function checkGuardianWhitelist(address guardian) external returns (bool isRemoved);
```

**Parameters**

| Name       | Type      | Description          |
| ---------- | --------- | -------------------- |
| `guardian` | `address` | The guardian address |

**Returns**

| Name        | Type   | Description                                         |
| ----------- | ------ | --------------------------------------------------- |
| `isRemoved` | `bool` | Whether the guardian was removed from the whitelist |

### setSubmitHooks

Set the submit hooks address

```solidity
function setSubmitHooks(ISubmitHooks newSubmitHooks) external virtual requiresAuth;
```

**Parameters**

| Name             | Type           | Description                              |
| ---------------- | -------------- | ---------------------------------------- |
| `newSubmitHooks` | `ISubmitHooks` | Address of the new submit hooks contract |

### pause

Pause the vault, halting the ability for guardians to submit

```solidity
function pause() external onlyAuthOrGuardian;
```

### unpause

Unpause the vault, allowing guardians to submit operations

```solidity
function unpause() external requiresAuth;
```

### getActiveGuardians

Get all active guardians

```solidity
function getActiveGuardians() external view returns (address[] memory);
```

**Returns**

| Name     | Type        | Description                        |
| -------- | ----------- | ---------------------------------- |
| `<none>` | `address[]` | Array of active guardian addresses |

### getGuardianRoot

Get the guardian root for a guardian

```solidity
function getGuardianRoot(address guardian) external view returns (bytes32);
```

**Parameters**

| Name       | Type      | Description          |
| ---------- | --------- | -------------------- |
| `guardian` | `address` | The guardian address |

**Returns**

| Name     | Type      | Description       |
| -------- | --------- | ----------------- |
| `<none>` | `bytes32` | The guardian root |

### getCurrentHookCallType

Get the current hook call type

```solidity
function getCurrentHookCallType() external view returns (HookCallType);
```

**Returns**

| Name     | Type           | Description                |
| -------- | -------------- | -------------------------- |
| `<none>` | `HookCallType` | The current hook call type |

### onERC721Received

*Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} by `operator` from `from`, this function is called. It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.*

```solidity
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4);
```

### \_handleCallbackOperations

```solidity
/// @notice Internal handler for validated callbacks
/// @dev Callback operations are like regular operations, but with a return value, which are encoded after
/// operations array
/// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
/// │ FIELDS                      │ SIZE                    │ DESCRIPTION                                   │
/// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
/// │  returnTypeFlag              1 byte                     0 = no return, 1 = static, 2 = dynamic        │
/// │  [if returnTypeFlag == 1]:                                                                            │
/// │     returnDataLength         2 bytes                    Length of return data                         │
/// │     returnData               <returnDataLength> bytes   Static return data                            │
/// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
/// @param root The merkle root of the callback
/// @param cursor The cursor to the callback data
/// @return returnValue The return value of the callback
function _handleCallbackOperations(bytes32 root, uint256 cursor)
    internal
    virtual
    override
    returns (bytes memory returnValue);
```

**Parameters**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `root`   | `bytes32` | The merkle root of the callback |
| `cursor` | `uint256` | The cursor to the callback data |

**Returns**

| Name          | Type    | Description                      |
| ------------- | ------- | -------------------------------- |
| `returnValue` | `bytes` | The return value of the callback |

### \_processExpectedCallback

Prepare for a callback if the guardian expects one

*Writes to transient storage to encode callback expectations*

```solidity
function _processExpectedCallback(CalldataReader reader, bytes32 root) internal returns (CalldataReader, uint208);
```

**Parameters**

| Name     | Type             | Description                                                        |
| -------- | ---------------- | ------------------------------------------------------------------ |
| `reader` | `CalldataReader` | Current position in the calldata                                   |
| `root`   | `bytes32`        | The merkle root of the active guardian that triggered the callback |

**Returns**

| Name     | Type             | Description             |
| -------- | ---------------- | ----------------------- |
| `<none>` | `CalldataReader` | Updated cursor position |
| `<none>` | `uint208`        | Packed callback data    |

### \_beforeSubmitHooks

Call the before submit hooks if defined

*Submit hooks passed as an argument to reduce storage loading*

```solidity
function _beforeSubmitHooks(address hooks, bytes calldata data) internal;
```

**Parameters**

| Name    | Type      | Description                                 |
| ------- | --------- | ------------------------------------------- |
| `hooks` | `address` | Address of the submit hooks contract        |
| `data`  | `bytes`   | Calldata to pass to the before submit hooks |

### \_afterSubmitHooks

Call the after submit hooks if defined

*Submit hooks passed as an argument to reduce storage loading*

```solidity
function _afterSubmitHooks(address hooks, bytes calldata data) internal;
```

**Parameters**

| Name    | Type      | Description                                |
| ------- | --------- | ------------------------------------------ |
| `hooks` | `address` | Address of the submit hooks contract       |
| `data`  | `bytes`   | Calldata to pass to the after submit hooks |

### \_beforeOperationHooks

Call the before operation hooks if defined

```solidity
function _beforeOperationHooks(address operationHooks, bytes memory data, uint256 i)
    internal
    returns (bytes memory result);
```

**Parameters**

| Name             | Type      | Description                             |
| ---------------- | --------- | --------------------------------------- |
| `operationHooks` | `address` | Address of the operation-specific hooks |
| `data`           | `bytes`   | Operation calldata                      |
| `i`              | `uint256` | Operation index                         |

**Returns**

| Name     | Type    | Description              |
| -------- | ------- | ------------------------ |
| `result` | `bytes` | Result of the hooks call |

### \_afterOperationHooks

Call the after operation hooks if defined

```solidity
function _afterOperationHooks(address operationHooks, bytes memory data, uint256 i) internal;
```

**Parameters**

| Name             | Type      | Description                             |
| ---------------- | --------- | --------------------------------------- |
| `operationHooks` | `address` | Address of the operation-specific hooks |
| `data`           | `bytes`   | Operation calldata                      |
| `i`              | `uint256` | Operation index                         |

### \_executeSubmit

Executes a series of operations

*Approvals are tracked so we can verify if they have been zeroed out at the end of submit*

```solidity
function _executeSubmit(bytes32 root, CalldataReader reader, bool isCalledFromCallback)
    internal
    returns (Approval[] memory approvals, uint256 approvalsLength, bytes[] memory results, CalldataReader newReader);
```

**Parameters**

| Name                   | Type             | Description                                                        |
| ---------------------- | ---------------- | ------------------------------------------------------------------ |
| `root`                 | `bytes32`        | The merkle root of the active guardian that triggered the callback |
| `reader`               | `CalldataReader` | Current position in the calldata                                   |
| `isCalledFromCallback` | `bool`           | Whether the submit is called from a callback                       |

**Returns**

| Name              | Type             | Description                                          |
| ----------------- | ---------------- | ---------------------------------------------------- |
| `approvals`       | `Approval[]`     | Array of outgoing approvals created during execution |
| `approvalsLength` | `uint256`        | Length of approvals array                            |
| `results`         | `bytes[]`        | Array of results from the operations                 |
| `newReader`       | `CalldataReader` | Updated cursor position                              |

### \_processBeforeOperationHooks

Processes all hooks for operation

Returns extracted data if configurable or contract before operation hooks is defined

*Custom hooks (with contracts) can run before and after each operation but a configurable hooks can only run before an operation. This function processes all of the possible configurations of hooks which doesn't allow using both a custom before hook and a configurable before hook*

```solidity
function _processBeforeOperationHooks(CalldataReader reader, bytes memory callData, uint256 i)
    internal
    returns (CalldataReader, bytes memory, uint256, address);
```

**Parameters**

| Name       | Type             | Description                      |
| ---------- | ---------------- | -------------------------------- |
| `reader`   | `CalldataReader` | Current position in the calldata |
| `callData` | `bytes`          | Operation calldata               |
| `i`        | `uint256`        | Operation index                  |

**Returns**

| Name     | Type             | Description                                |
| -------- | ---------------- | ------------------------------------------ |
| `<none>` | `CalldataReader` | reader Updated reader position             |
| `<none>` | `bytes`          | extractedData Extracted chunks of calldata |
| `<none>` | `uint256`        | hooksConfigBytes hooks configuration bytes |
| `<none>` | `address`        | operationHooks Operation hooks address     |

### \_setSubmitHooks

Set the submit hooks address

```solidity
function _setSubmitHooks(ISubmitHooks submitHooks_) internal;
```

**Parameters**

| Name           | Type           | Description                          |
| -------------- | -------------- | ------------------------------------ |
| `submitHooks_` | `ISubmitHooks` | Address of the submit hooks contract |

### \_setGuardianRoot

Set the guardian root

```solidity
function _setGuardianRoot(address guardian, bytes32 root) internal virtual;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `guardian` | `address` | Address of the guardian |
| `root`     | `bytes32` | Merkle root             |

### \_setHookCallType

Set the hook call type

```solidity
function _setHookCallType(HookCallType hookCallType) internal;
```

**Parameters**

| Name           | Type           | Description        |
| -------------- | -------------- | ------------------ |
| `hookCallType` | `HookCallType` | The hook call type |

### \_noPendingApprovalsInvariant

Verify no pending approvals remain at the end of a submit

*We iterate backwards to avoid extra i variable*

*While loop is preferred over for(;approvalsLength != 0;)*

*Iterator variable is not used because it's not needed and decrement needs to be unchecked*

```solidity
function _noPendingApprovalsInvariant(Approval[] memory approvals, uint256 approvalsLength) internal view;
```

**Parameters**

| Name              | Type         | Description                 |
| ----------------- | ------------ | --------------------------- |
| `approvals`       | `Approval[]` | Array of approvals to check |
| `approvalsLength` | `uint256`    | Length of approvals array   |

### \_getReturnValue

Get the return value from the operations

```solidity
function _getReturnValue(CalldataReader reader, bytes[] memory results)
    internal
    pure
    returns (CalldataReader newReader, bytes memory returnValue);
```

**Parameters**

| Name      | Type             | Description                          |
| --------- | ---------------- | ------------------------------------ |
| `reader`  | `CalldataReader` | Current position in the calldata     |
| `results` | `bytes[]`        | Array of results from the operations |

**Returns**

| Name          | Type             | Description                      |
| ------------- | ---------------- | -------------------------------- |
| `newReader`   | `CalldataReader` | Updated reader position          |
| `returnValue` | `bytes`          | Return value from the operations |

### \_verifyOperation

Verify an operation by validating the merkle proof

```solidity
function _verifyOperation(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure;
```

**Parameters**

| Name    | Type        | Description               |
| ------- | ----------- | ------------------------- |
| `proof` | `bytes32[]` | The merkle proof          |
| `root`  | `bytes32`   | The merkle root           |
| `leaf`  | `bytes32`   | The merkle leaf to verify |

### \_createMerkleLeaf

Create a merkle leaf

```solidity
function _createMerkleLeaf(OperationContext memory ctx, bytes memory extractedData) internal pure returns (bytes32);
```

**Parameters**

| Name            | Type               | Description           |
| --------------- | ------------------ | --------------------- |
| `ctx`           | `OperationContext` | The operation context |
| `extractedData` | `bytes`            | The extracted data    |

**Returns**

| Name     | Type      | Description          |
| -------- | --------- | -------------------- |
| `<none>` | `bytes32` | leaf The merkle leaf |

### \_extractApprovalSpender

Extract spender address from approval data

*Extract spender address from approval data*

```solidity
function _extractApprovalSpender(bytes memory data) internal pure returns (address spender);
```

**Parameters**

| Name   | Type    | Description       |
| ------ | ------- | ----------------- |
| `data` | `bytes` | Approval calldata |

**Returns**

| Name      | Type      | Description            |
| --------- | --------- | ---------------------- |
| `spender` | `address` | Address of the spender |

### \_hasBeforeHooks

Check if hooks needs to be called before the submit/operation

*Check if hooks needs to be called before the submit/operation*

```solidity
function _hasBeforeHooks(address hooks) internal pure returns (bool);
```

**Parameters**

| Name    | Type      | Description            |
| ------- | --------- | ---------------------- |
| `hooks` | `address` | Hooks address to check |

**Returns**

| Name     | Type   | Description                                                  |
| -------- | ------ | ------------------------------------------------------------ |
| `<none>` | `bool` | True if hooks needs to be called before the submit/operation |

### \_hasAfterHooks

least significant bit is 1 indicating it's a before hooks

Check if submit hooks needs to be called after the submit/operation

*Check if submit hooks needs to be called after the submit/operation*

```solidity
function _hasAfterHooks(address hooks) internal pure returns (bool);
```

**Parameters**

| Name    | Type      | Description                   |
| ------- | --------- | ----------------------------- |
| `hooks` | `address` | Submit hooks address to check |

**Returns**

| Name     | Type   | Description                                                        |
| -------- | ------ | ------------------------------------------------------------------ |
| `<none>` | `bool` | True if submit hooks needs to be called after the submit/operation |

### \_isAllowanceSelector

second least significant bit is 1 indicating it's a after hooks

Check if the selector is an allowance handling selector

*Check if the selector is an allowance handling selector*

```solidity
function _isAllowanceSelector(bytes4 selector) internal pure returns (bool);
```

**Parameters**

| Name       | Type     | Description       |
| ---------- | -------- | ----------------- |
| `selector` | `bytes4` | Selector to check |

**Returns**

| Name     | Type   | Description                                            |
| -------- | ------ | ------------------------------------------------------ |
| `<none>` | `bool` | True if the selector is an allowance handling selector |


# BaseVaultDeployer

**Inherits:** IBaseVaultDeployer

Base contract for deploying BaseVault and its variants

*Contains common deployment logic and parameter handling*

## State Variables

### BASE\_VAULT\_PARAMETERS\_SLOT

ERC7201-compliant transient storage slot for storing vault parameters during deployment

*Equal to keccak256(abi.encode(uint256(keccak256("aera.factory.baseVaultParameters")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant BASE_VAULT_PARAMETERS_SLOT =
    0xabbb07a7c84c47d0cde2038aa28d3c5b29638876472dc0cdc3a2448d1e4b7e00;
```

## Functions

### baseVaultParameters

Vault parameters for vault deployment

*Necessary to support deterministic vault deployments*

```solidity
function baseVaultParameters() external view returns (BaseVaultParameters memory params);
```

**Returns**

| Name     | Type                  | Description                                                                                   |
| -------- | --------------------- | --------------------------------------------------------------------------------------------- |
| `params` | `BaseVaultParameters` | parameters Parameters used for vault deployment, including owner, submit hooks, and whitelist |

### \_storeBaseVaultParameters

Store parameters in transient storage

```solidity
function _storeBaseVaultParameters(BaseVaultParameters calldata params) internal;
```

**Parameters**

| Name     | Type                  | Description             |
| -------- | --------------------- | ----------------------- |
| `params` | `BaseVaultParameters` | The parameters to store |


# BaseVaultFactory

**Inherits:** IBaseVaultFactory, BaseVaultDeployer, Sweepable

Used to deploy new BaseVault instances

*Only one instance of the factory will be required per chain*

## Functions

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority) Sweepable(initialOwner, initialAuthority);
```

### create

Create a new vault with the given parameters

```solidity
function create(
    bytes32 salt,
    string calldata description,
    BaseVaultParameters calldata baseVaultParams,
    address expectedVaultAddress
) external override requiresAuth returns (address deployedVault);
```

**Parameters**

| Name                   | Type                  | Description                            |
| ---------------------- | --------------------- | -------------------------------------- |
| `salt`                 | `bytes32`             | The salt value to use for create2      |
| `description`          | `string`              | Vault description                      |
| `baseVaultParams`      | `BaseVaultParameters` | Parameters for vault deployment        |
| `expectedVaultAddress` | `address`             | Expected address of the deployed vault |

**Returns**

| Name            | Type      | Description                   |
| --------------- | --------- | ----------------------------- |
| `deployedVault` | `address` | Address of the deployed vault |

### \_deployVault

Deploy vault

```solidity
function _deployVault(bytes32 salt, string calldata description, BaseVaultParameters calldata baseVaultParams)
    internal
    returns (address deployed);
```

**Parameters**

| Name              | Type                  | Description                     |
| ----------------- | --------------------- | ------------------------------- |
| `salt`            | `bytes32`             | The salt value to create vault  |
| `description`     | `string`              | Vault description               |
| `baseVaultParams` | `BaseVaultParameters` | Parameters for vault deployment |

**Returns**

| Name       | Type      | Description                   |
| ---------- | --------- | ----------------------------- |
| `deployed` | `address` | The address of deployed vault |


# CallbackHandler

**Inherits:** ICallbackHandler

Handles callback validation and execution for vault operations. This contract is designed to be used as a mixin in BaseVault, providing the ability to register logic for safely handling callbacks during guardian submissions. A common use case for handlers is receiving a flash loan. To receive a flashloan, the vault has to cede control when requesting a flashloan and then atomically handle the callback to repay the flashloan This requires two capabilities: the ability to register new handlers and the ability to initiate additional operations in the handle while being restricted by the merkle tree constraints. The callback handler contract achieves this by allowing guardians to "prepare" for a callback when they construct a given operation. If the operation "has a callback" then the fallback function in this contract will handle it. It will use transient storage to preserve information such as the expected callback caller, function selector of the callback and any approvals that are created during the callback

*Uses transient storage to manage callback state and approvals*

## State Variables

### CALLBACK\_CALL\_SLOT

ERC7201-compliant transient storage slot for storing the next authorized selector + caller

*Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.call")) - 1)) & \~bytes32(uint256(0xff));*

**Note:** security: Critical for callback validation

```solidity
bytes32 internal constant CALLBACK_CALL_SLOT = 0xa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f00;
```

### CALLBACK\_MERKLE\_ROOT\_SLOT

ERC7201-compliant transient storage slot for storing the callback merkle root

*Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.merkleRoot")) - 1) & \~bytes32(uint256(0xff));*

**Note:** security: Critical for callback validation

```solidity
bytes32 internal constant CALLBACK_MERKLE_ROOT_SLOT = 0x30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe00;
```

### APPROVALS\_SLOT

ERC7201-compliant transient storage slot for storing the approval tracking

*Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.approvals")) - 1)) & \~bytes32(uint256(0xff));*

**Note:** security: Critical for tracking token approvals during callbacks

```solidity
bytes32 internal constant APPROVALS_SLOT = 0xba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00;
```

## Functions

### fallback

Handle incoming callbacks and validates their authorization

*Extracts callback data and forwards to \_handleCallbackOperations if valid*

```solidity
fallback(bytes calldata) external returns (bytes memory returnValue);
```

### \_handleCallbackOperations

Internal handler for validated callbacks

*Callback operations are like regular operations, but with a return value, which are encoded after operations array ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐ │ FIELDS │ SIZE │ DESCRIPTION │ ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤ │ returnTypeFlag 1 byte 0 = no return, 1 = static, 2 = dynamic │ │ \[if returnTypeFlag == 1]: │ │ returnDataLength 2 bytes Length of return data │ │ returnData bytes Static return data │ └───────────────────────────────────────────────────────────────────────────────────────────────────────┘*

```solidity
function _handleCallbackOperations(bytes32 root, uint256 cursor) internal virtual returns (bytes memory returnValue);
```

**Parameters**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `root`   | `bytes32` | The merkle root of the callback |
| `cursor` | `uint256` | The cursor to the callback data |

**Returns**

| Name          | Type    | Description                      |
| ------------- | ------- | -------------------------------- |
| `returnValue` | `bytes` | The return value of the callback |

### \_allowCallback

Whitelist a function selector and caller as a valid callback

*Uses transient storage to store the callback data*

```solidity
function _allowCallback(bytes32 root, uint256 packedCallbackData) internal;
```

**Parameters**

| Name                 | Type      | Description                                          |
| -------------------- | --------- | ---------------------------------------------------- |
| `root`               | `bytes32` | The merkle root of the callback                      |
| `packedCallbackData` | `uint256` | Packed data containing caller, selector, and offsets |

### \_storeCallbackApprovals

Store approvals for the current callback context

*Uses transient storage to track approvals during callback execution*

*Length of the array, packed with the token address will be stored in the first slot*

*All other elements are laid out sequentially after the first slot, taking 2 slots per approval*

*If there are existing approvals, we will update length in the slot zero and append new approvals*

```solidity
function _storeCallbackApprovals(Approval[] memory approvals, uint256 length) internal;
```

**Parameters**

| Name        | Type         | Description                       |
| ----------- | ------------ | --------------------------------- |
| `approvals` | `Approval[]` | Array of token approvals to store |
| `length`    | `uint256`    | Length of the array               |

### \_getAllowedCallback

Retrieve the currently allowed callback data

*Store packed token and length in the zero slot, and spender in the second*

*Update the length and preserve the token in the zero slot Minus one to compensate for pre-increment in upcoming storage loop*

*Unpacks data from transient storage*

**Note:** security: Critical for callback validation

```solidity
function _getAllowedCallback() internal returns (address caller, bytes4 selector, uint16 userDataOffset);
```

**Returns**

| Name             | Type      | Description                                   |
| ---------------- | --------- | --------------------------------------------- |
| `caller`         | `address` | The authorized caller address                 |
| `selector`       | `bytes4`  | The authorized function selector              |
| `userDataOffset` | `uint16`  | The offset in calldata where user data begins |

### \_getAllowedMerkleRoot

Retrieves the currently allowed merkle root

*Unpacks data from transient storage*

**Note:** security: Critical for callback validation

```solidity
function _getAllowedMerkleRoot() internal returns (bytes32 root);
```

**Returns**

| Name   | Type      | Description                |
| ------ | --------- | -------------------------- |
| `root` | `bytes32` | The authorized merkle root |

### \_getCallbackApprovals

Retrieves the current callback approvals

*Decodes approvals from transient storage*

*The first slot contains the length of the array, packed with the token address*

*All other elements are laid out sequentially after the first slot, taking 2 slots per approval*

*Only length slot is cleared, the rest of the approvals are left in the transient storage*

*This is safe because even if new approvals are added, old ones will be overwritten for length slots*

```solidity
function _getCallbackApprovals() internal returns (Approval[] memory approvals);
```

**Returns**

| Name        | Type         | Description                      |
| ----------- | ------------ | -------------------------------- |
| `approvals` | `Approval[]` | Array of current token approvals |

### \_hasCallbackBeenCalled

Checks if an expected callback has been called

*If callback was expected but not received, CALLBACK\_CALL\_SLOT will not be reset to 0*

```solidity
function _hasCallbackBeenCalled() internal view returns (bool);
```

**Returns**

| Name     | Type   | Description                                                   |
| -------- | ------ | ------------------------------------------------------------- |
| `<none>` | `bool` | True if an expected callback has been called, false otherwise |

### \_unpackCallbackData

Unpacks callback data from a packed uint256

```solidity
function _unpackCallbackData(uint256 packed)
    private
    pure
    returns (address target, bytes4 selector, uint16 dataOffset);
```

**Parameters**

| Name     | Type      | Description                                 |
| -------- | --------- | ------------------------------------------- |
| `packed` | `uint256` | The packed uint256 containing callback data |

**Returns**

| Name         | Type      | Description                                   |
| ------------ | --------- | --------------------------------------------- |
| `target`     | `address` | The target address                            |
| `selector`   | `bytes4`  | The function selector                         |
| `dataOffset` | `uint16`  | The offset in calldata where user data begins |

### \_packLengthAndToken

Packs a token address and length into a uint256

*Used in transient storage slot zero*

*`length` is required to be less than `type(uint96).max + 1`*

```solidity
function _packLengthAndToken(uint256 length, address token) private pure returns (uint256);
```

**Parameters**

| Name     | Type      | Description                       |
| -------- | --------- | --------------------------------- |
| `length` | `uint256` | The length of the approvals array |
| `token`  | `address` | The token address                 |

**Returns**

| Name     | Type      | Description               |
| -------- | --------- | ------------------------- |
| `<none>` | `uint256` | packed The packed uint256 |


# Constants

### WORD\_SIZE

```solidity
uint256 constant WORD_SIZE = 32;
```

### SELECTOR\_SIZE

```solidity
uint256 constant SELECTOR_SIZE = 4;
```

### MINIMUM\_CALLDATA\_LENGTH

```solidity
uint256 constant MINIMUM_CALLDATA_LENGTH = WORD_SIZE + SELECTOR_SIZE;
```

### CALLDATA\_OFFSET

```solidity
uint256 constant CALLDATA_OFFSET = MINIMUM_CALLDATA_LENGTH;
```

### ERC20\_SPENDER\_OFFSET

```solidity
uint256 constant ERC20_SPENDER_OFFSET = 36;
```

### ADDRESS\_SIZE\_BITS

```solidity
uint256 constant ADDRESS_SIZE_BITS = 160;
```

### BEFORE\_HOOK\_MASK

```solidity
uint256 constant BEFORE_HOOK_MASK = 1;
```

### AFTER\_HOOK\_MASK

```solidity
uint256 constant AFTER_HOOK_MASK = 2;
```

### HOOKS\_FLAG\_MASK

```solidity
uint256 constant HOOKS_FLAG_MASK = 0x80;
```

### CONFIGURABLE\_HOOKS\_LENGTH\_MASK

```solidity
uint256 constant CONFIGURABLE_HOOKS_LENGTH_MASK = 0x7F;
```

### MASK\_8\_BIT

```solidity
uint256 constant MASK_8_BIT = 0xff;
```

### MASK\_16\_BIT

```solidity
uint256 constant MASK_16_BIT = 0xffff;
```

### RESULTS\_INDEX\_OFFSET

```solidity
uint256 constant RESULTS_INDEX_OFFSET = 24;
```

### COPY\_WORD\_OFFSET

```solidity
uint256 constant COPY_WORD_OFFSET = 16;
```

### EXTRACT\_OFFSET\_SIZE\_BITS

```solidity
uint256 constant EXTRACT_OFFSET_SIZE_BITS = 16;
```

### EXTRACTION\_OFFSET\_SHIFT\_BITS

```solidity
uint256 constant EXTRACTION_OFFSET_SHIFT_BITS = 240;
```

### MAX\_EXTRACT\_OFFSETS\_EXCLUSIVE

*Maximum number of extraction offsets(16) + 1*

```solidity
uint256 constant MAX_EXTRACT_OFFSETS_EXCLUSIVE = 17;
```

### NO\_CALLBACK\_DATA

```solidity
uint16 constant NO_CALLBACK_DATA = type(uint16).max;
```

### SELECTOR\_OFFSET

```solidity
uint256 constant SELECTOR_OFFSET = 48;
```

### CALLBACK\_DATA\_OFFSET

```solidity
uint256 constant CALLBACK_DATA_OFFSET = 160;
```

### ONE\_IN\_BPS

```solidity
uint256 constant ONE_IN_BPS = 1e4;
```

### MAX\_TVL\_FEE

```solidity
uint256 constant MAX_TVL_FEE = 2000;
```

### MAX\_PERFORMANCE\_FEE

```solidity
uint256 constant MAX_PERFORMANCE_FEE = ONE_IN_BPS;
```

### SECONDS\_PER\_YEAR

```solidity
uint256 constant SECONDS_PER_YEAR = 365 days;
```

### MAX\_DISPUTE\_PERIOD

```solidity
uint256 constant MAX_DISPUTE_PERIOD = 30 days;
```

### UNIT\_PRICE\_PRECISION

*Precision for unit price calculations (18 decimals)*

```solidity
uint256 constant UNIT_PRICE_PRECISION = 1e18;
```

### ONE\_MINUTE

*One minute in seconds*

```solidity
uint256 constant ONE_MINUTE = 1 minutes;
```

### ONE\_DAY

*One day in seconds*

```solidity
uint256 constant ONE_DAY = 1 days;
```

### MIN\_DEPOSIT\_MULTIPLIER

*Minimum deposit multiplier 50%*

```solidity
uint256 constant MIN_DEPOSIT_MULTIPLIER = 5000;
```

### MIN\_REDEEM\_MULTIPLIER

*Minimum redeem multiplier 50%*

```solidity
uint256 constant MIN_REDEEM_MULTIPLIER = 5000;
```

### DEPOSIT\_REDEEM\_FLAG

*Deposit/Redeem flag in RequestType enum*

```solidity
uint256 constant DEPOSIT_REDEEM_FLAG = 1;
```

### AUTO\_PRICE\_FIXED\_PRICE\_FLAG

*Auto/Fixed price flag in RequestType enum*

```solidity
uint256 constant AUTO_PRICE_FIXED_PRICE_FLAG = 2;
```

### ONE\_UNIT

*One unit with 18 decimals*

```solidity
uint256 constant ONE_UNIT = 1e18;
```

### MAX\_SECONDS\_TO\_DEADLINE

*Maximum seconds between request deadline and current timestamp*

```solidity
uint256 constant MAX_SECONDS_TO_DEADLINE = 365 days;
```

### MAX\_DEPOSIT\_REFUND\_TIMEOUT

*Upper bound for depositRefundTimeout to prevent indefinite user lockout*

```solidity
uint256 constant MAX_DEPOSIT_REFUND_TIMEOUT = 30 days;
```

### IS\_WHITELISTED\_FLAG

*Whitelist flag in AddressToUintMap*

```solidity
uint8 constant IS_WHITELISTED_FLAG = 1;
```


# DelayedFeeCalculator

**Inherits:** IDelayedFeeCalculator, BaseFeeCalculator

To protect vault owners from inaccurate submissions, the DelayedFeeCalculator uses a dispute period and pending snapshot system that only accepts submitted values after the dispute period has passed. Each vault accrues fees independently but a shared protocol fee recipient accrues protocol level fees from all vaults

*All fees are calculated in the numeraire token's decimals*

## State Variables

### DISPUTE\_PERIOD

Dispute period for vault snapshot

```solidity
uint256 public immutable DISPUTE_PERIOD;
```

### \_vaultSnapshots

A mapping of vault addresses to their associated state

```solidity
mapping(address vault => VaultSnapshot vaultSnapshot) internal _vaultSnapshots;
```

## Functions

### constructor

```solidity
constructor(address owner_, Authority authority_, uint256 disputePeriod) BaseFeeCalculator(owner_, authority_);
```

### registerVault

```solidity
function registerVault() external override;
```

### submitSnapshot

Submit a new snapshot for fee calculation

```solidity
function submitSnapshot(address vault, uint160 averageValue, uint128 highestProfit, uint32 timestamp)
    external
    onlyVaultAccountant(vault);
```

**Parameters**

| Name            | Type      | Description                                                                        |
| --------------- | --------- | ---------------------------------------------------------------------------------- |
| `vault`         | `address` | The address of the vault                                                           |
| `averageValue`  | `uint160` | The average value during the period since last snapshot to this snapshot timestamp |
| `highestProfit` | `uint128` | The highest profit achieved up to the snapshot timestamp                           |
| `timestamp`     | `uint32`  | The timestamp of the snapshot                                                      |

### accrueFees

Process fee accrual for a vault

```solidity
function accrueFees(address vault) external returns (uint256 protocolFeesEarned, uint256 vaultFeesEarned);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name                 | Type      | Description                                                                      |
| -------------------- | --------- | -------------------------------------------------------------------------------- |
| `protocolFeesEarned` | `uint256` | tvlFeesEarned The earned TVL fees for the vault and the protocol                 |
| `vaultFeesEarned`    | `uint256` | performanceFeesEarned The earned performance fees for the vault and the protocol |

### previewFees

```solidity
function previewFees(address vault, uint256 feeTokenBalance) external view override returns (uint256, uint256);
```

### vaultFeeState

Get the fee state for a vault

```solidity
function vaultFeeState(address vault) external view returns (VaultSnapshot memory, VaultAccruals memory);
```

**Parameters**

| Name    | Type      | Description                            |
| ------- | --------- | -------------------------------------- |
| `vault` | `address` | The vault address to get fee state for |

**Returns**

| Name     | Type            | Description                                                    |
| -------- | --------------- | -------------------------------------------------------------- |
| `<none>` | `VaultSnapshot` | The vault snapshot containing average value and highest profit |
| `<none>` | `VaultAccruals` | The vault accruals containing fees and accrued amounts         |

### \_beforeClaimFees

Called before claiming fees

*Can be overridden by child contracts to add custom logic*

```solidity
function _beforeClaimFees() internal override;
```

### \_beforeClaimProtocolFees

Called before claiming protocol fees

*Can be overridden by child contracts to add custom logic*

```solidity
function _beforeClaimProtocolFees() internal override;
```

### \_accrueFees

Accrues fees for a vault based on its pending snapshot

*Updates the vault's state including lastFeeAccrual, lastHighestProfit, and accruedFees*

*Deletes pending snapshot if dispute period has passed*

```solidity
function _accrueFees(
    VaultSnapshot storage vaultSnapshot,
    VaultAccruals storage vaultAccruals,
    uint256 lastFeeAccrualCached
) internal returns (uint256 protocolFeesEarned, uint256 vaultFeesEarned);
```

**Parameters**

| Name                   | Type            | Description                                                            |
| ---------------------- | --------------- | ---------------------------------------------------------------------- |
| `vaultSnapshot`        | `VaultSnapshot` | The storage pointer to the vault's state                               |
| `vaultAccruals`        | `VaultAccruals` | The storage pointer to the vault's accruals                            |
| `lastFeeAccrualCached` | `uint256`       | The last fee accrual timestamp cached to avoid re-reading from storage |

**Returns**

| Name                 | Type      | Description              |
| -------------------- | --------- | ------------------------ |
| `protocolFeesEarned` | `uint256` | The earned protocol fees |
| `vaultFeesEarned`    | `uint256` | The earned vault fees    |

### \_calculatePerformanceFees

Calculates performance fees for both vault and protocol

*Returns zero fees if no new profit has been made*

```solidity
function _calculatePerformanceFees(uint256 vaultPerformanceFeeRate, uint256 newHighestProfit, uint256 oldHighestProfit)
    internal
    view
    returns (uint256, uint256);
```

**Parameters**

| Name                      | Type      | Description                                 |
| ------------------------- | --------- | ------------------------------------------- |
| `vaultPerformanceFeeRate` | `uint256` | The performance fee rate for the vault      |
| `newHighestProfit`        | `uint256` | The highest profit in the pending snapshot  |
| `oldHighestProfit`        | `uint256` | The highest profit in the previous snapshot |

**Returns**

| Name     | Type      | Description                                                 |
| -------- | --------- | ----------------------------------------------------------- |
| `<none>` | `uint256` | vaultPerformanceFee The performance fee for the vault       |
| `<none>` | `uint256` | protocolPerformanceFee The performance fee for the protocol |

### \_calculateTvlFees

Calculates TVL fees for both vault and protocol

```solidity
function _calculateTvlFees(
    uint256 vaultTvlFeeRate,
    uint256 averageValue,
    uint256 snapshotTimestamp,
    uint256 lastFeeAccrual
) internal view returns (uint256, uint256);
```

**Parameters**

| Name                | Type      | Description                           |
| ------------------- | --------- | ------------------------------------- |
| `vaultTvlFeeRate`   | `uint256` | The TVL fee rate for the vault        |
| `averageValue`      | `uint256` | The average value of the vault        |
| `snapshotTimestamp` | `uint256` | The timestamp of the snapshot         |
| `lastFeeAccrual`    | `uint256` | The timestamp of the last fee accrual |

**Returns**

| Name     | Type      | Description                                        |
| -------- | --------- | -------------------------------------------------- |
| `<none>` | `uint256` | vaultTvlFee The earned TVL fee for the vault       |
| `<none>` | `uint256` | protocolTvlFee The earned TVL fee for the protocol |


# FeeVault

**Inherits:** IFeeVault, BaseVault

This contract extends BaseVault with fee capabilities for vaults that have a single logical owner of all assets. The vault relies on an external contract called the fee calculator which is shared across multiple vaults The fee calculator is responsible for calculating the TVL and performance fees for the vault, but the vault has control over those fees. Fee claims are initiated via the vault, which consults and updates the fee calculator upon successful claims

## State Variables

### FEE\_TOKEN

Address of the fee token

```solidity
IERC20 public immutable FEE_TOKEN;
```

### feeCalculator

Address of the fee calculator contract

```solidity
IFeeCalculator public feeCalculator;
```

### feeRecipient

Address of the fee recipient

```solidity
address public feeRecipient;
```

## Functions

### onlyFeeRecipient

Modifier to check that the caller is the fee recipient

```solidity
modifier onlyFeeRecipient();
```

### constructor

```solidity
constructor() BaseVault();
```

### setFeeCalculator

Set the fee calculator

*newFeeCalculator can be zero, which has the effect as disabling the fee calculator*

```solidity
function setFeeCalculator(IFeeCalculator newFeeCalculator) external requiresAuth;
```

**Parameters**

| Name               | Type             | Description            |
| ------------------ | ---------------- | ---------------------- |
| `newFeeCalculator` | `IFeeCalculator` | The new fee calculator |

### setFeeRecipient

Set the fee recipient

```solidity
function setFeeRecipient(address newFeeRecipient) external requiresAuth;
```

**Parameters**

| Name              | Type      | Description                   |
| ----------------- | --------- | ----------------------------- |
| `newFeeRecipient` | `address` | The new fee recipient address |

### claimFees

Claim accrued fees for msg.sender

*Automatically claims any earned protocol fees for the protocol*

```solidity
function claimFees() external onlyFeeRecipient returns (uint256 feeRecipientFees, uint256 protocolFees);
```

**Returns**

| Name               | Type      | Description                                               |
| ------------------ | --------- | --------------------------------------------------------- |
| `feeRecipientFees` | `uint256` | The amount of fees to be claimed by the fee recipient     |
| `protocolFees`     | `uint256` | The amount of protocol fees to be claimed by the protocol |

### claimProtocolFees

Claim accrued protocol fees

```solidity
function claimProtocolFees() external returns (uint256 protocolFees);
```

**Returns**

| Name           | Type      | Description                                               |
| -------------- | --------- | --------------------------------------------------------- |
| `protocolFees` | `uint256` | The amount of protocol fees to be claimed by the protocol |


# FeeVaultDeployer

**Inherits:** IFeeVaultDeployer, BaseVaultDeployer

Helper contract for deploying fee-based vaults with single or multiple depositors Does not deploy the fee vault itself or the FeeCalculator

*Stores and retrieves fee vault parameters using transient storage during deployment*

## State Variables

### FEE\_VAULT\_PARAMETERS\_SLOT

ERC7201-compliant transient storage slot for storing fee vault parameters during deployment

*Equal to keccak256(abi.encode(uint256(keccak256("aera.factory.feeVaultParameters")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant FEE_VAULT_PARAMETERS_SLOT = 0xe980a18a7f321cb444704cc245d4dfee0157b4ba12f1db4cab9c6992a98d2600;
```

## Functions

### feeVaultParameters

Get the deployment parameters for the fee vault

```solidity
function feeVaultParameters() external view returns (FeeVaultParameters memory params);
```

**Returns**

| Name     | Type                 | Description                             |
| -------- | -------------------- | --------------------------------------- |
| `params` | `FeeVaultParameters` | Deployment parameters for the fee vault |

### \_storeFeeVaultParameters

Stores fee vault parameters in transient storage

```solidity
function _storeFeeVaultParameters(FeeVaultParameters calldata params) internal;
```

**Parameters**

| Name     | Type                 | Description                                      |
| -------- | -------------------- | ------------------------------------------------ |
| `params` | `FeeVaultParameters` | Struct with fee calculator, token, and recipient |


# HasNumeraire

**Inherits:** IHasNumeraire

Abstract contract for contracts with an immutable numeraire token to be used for pricing

## State Variables

### NUMERAIRE

Address of the numeraire token

```solidity
address public immutable NUMERAIRE;
```

## Functions

### constructor

```solidity
constructor(address numeraire_);
```

### \_getNumeraire

Get the numeraire address

```solidity
function _getNumeraire() internal view virtual returns (address);
```

**Returns**

| Name     | Type      | Description                        |
| -------- | --------- | ---------------------------------- |
| `<none>` | `address` | The address of the numeraire token |


# MultiDepositorVault

**Inherits:** IMultiDepositorVault, ERC20, FeeVault

A vault that allows users to deposit and withdraw multiple tokens. This contract just mints and burns unit tokens and all logic and validation is handled by the provisioner

## State Variables

### beforeTransferHook

Hooks contract called before unit transfers/mints/burns

```solidity
IBeforeTransferHook public beforeTransferHook;
```

### provisioner

Role that can mint/burn vault units

```solidity
address public provisioner;
```

## Functions

### onlyProvisioner

Ensures caller is the provisioner

```solidity
modifier onlyProvisioner();
```

### constructor

```solidity
constructor()
    ERC20(IMultiDepositorVaultFactory(msg.sender).getERC20Name(), IMultiDepositorVaultFactory(msg.sender).getERC20Symbol())
    FeeVault();
```

### enter

Deposit tokens into the vault and mint units

```solidity
function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
    external
    whenNotPaused
    onlyProvisioner;
```

**Parameters**

| Name          | Type      | Description                    |
| ------------- | --------- | ------------------------------ |
| `sender`      | `address` | The sender of the tokens       |
| `token`       | `IERC20`  | The token to deposit           |
| `tokenAmount` | `uint256` | The amount of token to deposit |
| `unitsAmount` | `uint256` | The amount of units to mint    |
| `recipient`   | `address` | The recipient of the units     |

### exit

Withdraw tokens from the vault and burn units

```solidity
function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
    external
    whenNotPaused
    onlyProvisioner;
```

**Parameters**

| Name          | Type      | Description                     |
| ------------- | --------- | ------------------------------- |
| `sender`      | `address` | The sender of the units         |
| `token`       | `IERC20`  | The token to withdraw           |
| `tokenAmount` | `uint256` | The amount of token to withdraw |
| `unitsAmount` | `uint256` | The amount of units to burn     |
| `recipient`   | `address` | The recipient of the tokens     |

### setProvisioner

Sets the provisioner address

```solidity
function setProvisioner(address provisioner_) external requiresAuth;
```

**Parameters**

| Name           | Type      | Description                 |
| -------------- | --------- | --------------------------- |
| `provisioner_` | `address` | The new provisioner address |

### setBeforeTransferHook

Set the before transfer hooks

```solidity
function setBeforeTransferHook(IBeforeTransferHook hook) external requiresAuth;
```

**Parameters**

| Name   | Type                  | Description |
| ------ | --------------------- | ----------- |
| `hook` | `IBeforeTransferHook` |             |

### \_update

Internal function to update token balances with transfer hook checks

*Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding this function. Emits a {Transfer} event.*

```solidity
function _update(address from, address to, uint256 amount) internal override;
```

**Parameters**

| Name     | Type      | Description                             |
| -------- | --------- | --------------------------------------- |
| `from`   | `address` | The address tokens are transferred from |
| `to`     | `address` | The address tokens are transferred to   |
| `amount` | `uint256` | The amount of tokens to transfer        |

### \_setBeforeTransferHook

Set the transfer hook

```solidity
function _setBeforeTransferHook(IBeforeTransferHook hook_) internal;
```

**Parameters**

| Name    | Type                  | Description               |
| ------- | --------------------- | ------------------------- |
| `hook_` | `IBeforeTransferHook` | The transfer hook address |

### \_setProvisioner

Set the provisioner

```solidity
function _setProvisioner(address provisioner_) internal;
```

**Parameters**

| Name           | Type      | Description             |
| -------------- | --------- | ----------------------- |
| `provisioner_` | `address` | The provisioner address |


# MultiDepositorVaultDeployDelegate

**Inherits:** IVaultDeployDelegate

Deploys a new MultiDepositorVault contract

*This contract is used to deploy a new MultiDepositorVault contract through a delegatecall*

*It is separate from the MultiDepositorVaultFactory because of the 24kb contracts size limit*

## Functions

### createVault

Deploy a new vault

```solidity
function createVault(bytes32 salt) external returns (address);
```

**Parameters**

| Name   | Type      | Description                    |
| ------ | --------- | ------------------------------ |
| `salt` | `bytes32` | The salt value to create vault |

**Returns**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `<none>` | `address` | deployed Deployed vault address |


# MultiDepositorVaultFactory

**Inherits:** IMultiDepositorVaultFactory, FeeVaultDeployer, Sweepable

Used to create new multi-depositor vaults using delegate call

*Only one instance of the factory will be required per chain*

## State Variables

### ERC20\_NAME\_SLOT

ERC7201-compliant transient storage slot for storing vault token erc20 name during deployment

*Equal to keccak256(abi.encode(uint256(keccak256("aera.factory.erc20.name")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant ERC20_NAME_SLOT = 0x79a9bb099f009196aa3acc685f15554a8e8fd10fee7019652e2c9a6d65a86500;
```

### ERC20\_SYMBOL\_SLOT

ERC7201-compliant transient storage slot for storing vault token erc20 symbol during deployment

*Equal to keccak256(abi.encode(uint256(keccak256("aera.factory.erc20.symbol")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant ERC20_SYMBOL_SLOT = 0xab25fe6ab1c05d9a94c8d6727a857804585a85a98c2bb360f69300eb1a356300;
```

### MULTI\_DEPOSITOR\_VAULT\_PARAMETERS\_SLOT

ERC7201-compliant transient storage slot for storing multi depositor vault parameters during deployment

*Equal to keccak256(abi.encode(uint256(keccak256("aera.factory.multiDepositorVaultParameters")) - 1)) & \~bytes32(uint256(0xff));*

```solidity
bytes32 internal constant MULTI_DEPOSITOR_VAULT_PARAMETERS_SLOT =
    0xe5669a0cf4b353071b0fa74e3cea85f64b33cd9eee158e4f6614aca797ff3a00;
```

### \_DEPLOY\_DELEGATE

Address of the deploy delegate

```solidity
address internal immutable _DEPLOY_DELEGATE;
```

## Functions

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority, address deployDelegate)
    Sweepable(initialOwner, initialAuthority);
```

### create

Create multi depositor vault

```solidity
function create(
    bytes32 salt,
    string calldata description,
    ERC20Parameters calldata erc20Params,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata feeVaultParams,
    IBeforeTransferHook beforeTransferHook,
    address expectedVaultAddress
) external override requiresAuth returns (address deployedVault);
```

**Parameters**

| Name                   | Type                  | Description                                                    |
| ---------------------- | --------------------- | -------------------------------------------------------------- |
| `salt`                 | `bytes32`             | The salt used to generate the vault address                    |
| `description`          | `string`              | Vault description                                              |
| `erc20Params`          | `ERC20Parameters`     | ERC20 parameters for deployment                                |
| `baseVaultParams`      | `BaseVaultParameters` | Base vault parameters for deployment                           |
| `feeVaultParams`       | `FeeVaultParameters`  | Fee vault parameters for deployment                            |
| `beforeTransferHook`   | `IBeforeTransferHook` | Before transfer hooks for deployment                           |
| `expectedVaultAddress` | `address`             | Expected vault address to check against deployed vault address |

**Returns**

| Name            | Type      | Description            |
| --------------- | --------- | ---------------------- |
| `deployedVault` | `address` | Deployed vault address |

### getERC20Name

Get the ERC20 name of vault units

```solidity
function getERC20Name() external view returns (string memory name);
```

**Returns**

| Name   | Type     | Description                       |
| ------ | -------- | --------------------------------- |
| `name` | `string` | The name of the vault ERC20 token |

### getERC20Symbol

Get the ERC20 symbol of vault units

```solidity
function getERC20Symbol() external view returns (string memory symbol);
```

**Returns**

| Name     | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| `symbol` | `string` | The symbol of the vault ERC20 token |

### multiDepositorVaultParameters

Get the vault parameters

```solidity
function multiDepositorVaultParameters() external view returns (IBeforeTransferHook beforeTransferHook);
```

**Returns**

| Name                 | Type                  | Description                                  |
| -------------------- | --------------------- | -------------------------------------------- |
| `beforeTransferHook` | `IBeforeTransferHook` | The hooks called before vault unit transfers |

### \_deployVault

Deploy vault

```solidity
function _deployVault(
    bytes32 salt,
    string calldata description,
    ERC20Parameters calldata erc20Params,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata feeVaultParams,
    IBeforeTransferHook beforeTransferHook
) internal returns (address deployed);
```

**Parameters**

| Name                 | Type                  | Description                                                       |
| -------------------- | --------------------- | ----------------------------------------------------------------- |
| `salt`               | `bytes32`             | The salt value to create vault                                    |
| `description`        | `string`              | Vault description                                                 |
| `erc20Params`        | `ERC20Parameters`     | ERC20 parameters for vault deployment used in MultiDepositorVault |
| `baseVaultParams`    | `BaseVaultParameters` | Parameters for vault deployment used in BaseVault                 |
| `feeVaultParams`     | `FeeVaultParameters`  | Parameters for vault deployment specific to FeeVault              |
| `beforeTransferHook` | `IBeforeTransferHook` | Parameters for vault deployment specific to MultiDepositorVault   |

**Returns**

| Name       | Type      | Description            |
| ---------- | --------- | ---------------------- |
| `deployed` | `address` | Deployed vault address |

### \_storeERC20Parameters

Store ERC20 name and symbol in transient storage

```solidity
function _storeERC20Parameters(ERC20Parameters calldata params) internal;
```

**Parameters**

| Name     | Type              | Description                             |
| -------- | ----------------- | --------------------------------------- |
| `params` | `ERC20Parameters` | Struct containing ERC20 name and symbol |

### \_storeMultiDepositorVaultParameters

Store beforeTransferHook address in transient storage

```solidity
function _storeMultiDepositorVaultParameters(IBeforeTransferHook beforeTransferHook) internal;
```

**Parameters**

| Name                 | Type                  | Description                             |
| -------------------- | --------------------- | --------------------------------------- |
| `beforeTransferHook` | `IBeforeTransferHook` | The hooks called before token transfers |

### \_createVault

Create a new vault with delegate call

```solidity
function _createVault(bytes32 salt) internal returns (address deployed);
```

**Parameters**

| Name   | Type      | Description                    |
| ------ | --------- | ------------------------------ |
| `salt` | `bytes32` | The salt value to create vault |

**Returns**

| Name       | Type      | Description            |
| ---------- | --------- | ---------------------- |
| `deployed` | `address` | Deployed vault address |

### \_loadStringFromSlot

Load a short string from the given storage slot

```solidity
function _loadStringFromSlot(uint256 slot) internal view returns (string memory);
```

**Parameters**

| Name   | Type      | Description               |
| ------ | --------- | ------------------------- |
| `slot` | `uint256` | Storage slot to read from |

**Returns**

| Name     | Type     | Description    |
| -------- | -------- | -------------- |
| `<none>` | `string` | Decoded string |


# PriceAndFeeCalculator

**Inherits:** IPriceAndFeeCalculator, BaseFeeCalculator, HasNumeraire

Calculates and manages unit price and fees for multiple vaults that share the same numeraire token. Acts as a price oracle and fee accrual engine. Vault registration workflow is:

1. Register a new vault with registerVault()
2. Set the thresholds for the vault with setThresholds()
3. Set the initial price state with setInitialPrice() Once registered, a vault can have its price updated by an authorized entity. Vault owners set thresholds for price changes, update intervals, and price age. If a price update violates thresholds (too large change, too soon, or too old), the vault is paused. Paused vaults dont accrue fees and cannot have their price updated until they are unpaused by the vault owner. Accrues fees on each price update, based on TVL and performance since last update Supports conversion between vault units, tokens, and numeraire for deposits/withdrawals. All logic and state is per-vault, supporting many vaults in parallel. Only vault owners can set thresholds pause/unpause their vaults, whereas accountants can also pause their vaults Integrates with an external oracle registry for token price conversions

## State Variables

### ORACLE\_REGISTRY

Oracle registry contract for price feeds

```solidity
IOracleRegistry public immutable ORACLE_REGISTRY;
```

### \_vaultPriceStates

Mapping of vault addresses to their state information

```solidity
mapping(address vault => VaultPriceState vaultPriceState) internal _vaultPriceStates;
```

## Functions

### requiresVaultAuthOrAccountant

```solidity
modifier requiresVaultAuthOrAccountant(address vault);
```

### constructor

```solidity
constructor(IERC20 numeraire, IOracleRegistry oracleRegistry, address owner_, Authority authority_)
    BaseFeeCalculator(owner_, authority_)
    HasNumeraire(address(numeraire));
```

### registerVault

Register a new vault with the fee calculator

```solidity
function registerVault() external override;
```

### setInitialPrice

Set the initial price state for the vault

```solidity
function setInitialPrice(address vault, uint128 price, uint32 timestamp) external requiresVaultAuth(vault);
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | New unit price                        |
| `timestamp` | `uint32`  | Timestamp when the price was measured |

### setThresholds

Set vault thresholds

```solidity
function setThresholds(
    address vault,
    uint16 minPriceToleranceRatio,
    uint16 maxPriceToleranceRatio,
    uint16 minUpdateIntervalMinutes,
    uint8 maxPriceAge,
    uint8 maxUpdateDelayDays
) external requiresVaultAuth(vault);
```

**Parameters**

| Name                       | Type      | Description                                                                |
| -------------------------- | --------- | -------------------------------------------------------------------------- |
| `vault`                    | `address` | Address of the vault                                                       |
| `minPriceToleranceRatio`   | `uint16`  | Minimum ratio (of a price decrease) in basis points                        |
| `maxPriceToleranceRatio`   | `uint16`  | Maximum ratio (of a price increase) in basis points                        |
| `minUpdateIntervalMinutes` | `uint16`  | The minimum interval between updates in minutes                            |
| `maxPriceAge`              | `uint8`   | Max delay between when a vault was priced and when the price is acceptable |
| `maxUpdateDelayDays`       | `uint8`   | Max delay between two price updates                                        |

### setUnitPrice

Set the unit price for the vault in numeraire terms

```solidity
function setUnitPrice(address vault, uint128 price, uint32 timestamp) external onlyVaultAccountant(vault);
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | New unit price                        |
| `timestamp` | `uint32`  | Timestamp when the price was measured |

### pauseVault

Pause the vault

```solidity
function pauseVault(address vault) external requiresVaultAuthOrAccountant(vault);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

### unpauseVault

Unpause the vault

*MUST revert if price or timestamp don't exactly match last update*

```solidity
function unpauseVault(address vault, uint128 price, uint32 timestamp) external requiresVaultAuth(vault);
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | Expected price of the last update     |
| `timestamp` | `uint32`  | Expected timestamp of the last update |

### resetHighestPrice

Resets the highest price for a vault to the current price

```solidity
function resetHighestPrice(address vault) external requiresVaultAuth(vault);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

### convertUnitsToToken

Convert units to token amount

```solidity
function convertUnitsToToken(address vault, IERC20 token, uint256 unitsAmount)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

### convertUnitsToTokenIfActive

Convert units to token amount if vault is not paused

*MUST revert if vault is paused*

```solidity
function convertUnitsToTokenIfActive(address vault, IERC20 token, uint256 unitsAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `unitsAmount` | `uint256`       | Amount of units      |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

### convertUnitsToNumeraire

Convert units to numeraire token amount

```solidity
function convertUnitsToNumeraire(address vault, uint256 unitsAmount) external view returns (uint256);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | numeraireAmount Amount of numeraire |

### convertTokenToUnits

Convert token amount to units

```solidity
function convertTokenToUnits(address vault, IERC20 token, uint256 tokenAmount)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `tokenAmount` | `uint256` | Amount of tokens     |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

### convertTokenToUnitsIfActive

Convert token amount to units if vault is not paused

*MUST revert if vault is paused*

```solidity
function convertTokenToUnitsIfActive(address vault, IERC20 token, uint256 tokenAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `tokenAmount` | `uint256`       | Amount of tokens     |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

### getVaultState

Return the state of the vault

```solidity
function getVaultState(address vault) external view returns (VaultPriceState memory, VaultAccruals memory);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type              | Description                                   |
| -------- | ----------------- | --------------------------------------------- |
| `<none>` | `VaultPriceState` | vaultPriceState The price state of the vault  |
| `<none>` | `VaultAccruals`   | vaultAccruals The accruals state of the vault |

### getVaultsPriceAge

Returns the age of the last submitted price for a vault

```solidity
function getVaultsPriceAge(address vault) external view returns (uint256);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type      | Description                                                                      |
| -------- | --------- | -------------------------------------------------------------------------------- |
| `<none>` | `uint256` | priceAge The difference between block.timestamp and vault's unit price timestamp |

### isVaultPaused

Check if a vault is paused

```solidity
function isVaultPaused(address vault) external view returns (bool);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name     | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `<none>` | `bool` | True if the vault is paused, false otherwise |

### previewFees

```solidity
function previewFees(address vault, uint256 feeTokenBalance) external view override returns (uint256, uint256);
```

### \_accrueFees

Accrues fees for a vault

*It is assumed that validation has already been done Tvl is calculated as the product of the minimum of the current and last price and the minimum of the current and last total supply. This is to minimize potential issues with price spikes*

```solidity
function _accrueFees(address vault, uint256 price, uint256 timestamp) internal;
```

**Parameters**

| Name        | Type      | Description                       |
| ----------- | --------- | --------------------------------- |
| `vault`     | `address` | The address of the vault          |
| `price`     | `uint256` | The price of a single vault unit  |
| `timestamp` | `uint256` | The timestamp of the price update |

### \_setVaultPaused

Sets the paused state for a vault

```solidity
function _setVaultPaused(VaultPriceState storage vaultPriceState, address vault, bool paused) internal;
```

**Parameters**

| Name              | Type              | Description                                    |
| ----------------- | ----------------- | ---------------------------------------------- |
| `vaultPriceState` | `VaultPriceState` | The storage pointer to the vault's price state |
| `vault`           | `address`         | The address of the vault                       |
| `paused`          | `bool`            | The new paused state                           |

### \_convertTokenToUnits

Converts a token amount to units

```solidity
function _convertTokenToUnits(
    address vault,
    IERC20 token,
    uint256 tokenAmount,
    uint256 unitPrice,
    Math.Rounding rounding
) internal view returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type            | Description                      |
| ------------- | --------------- | -------------------------------- |
| `vault`       | `address`       | The address of the vault         |
| `token`       | `IERC20`        | The token to convert             |
| `tokenAmount` | `uint256`       | The amount of tokens to convert  |
| `unitPrice`   | `uint256`       | The price of a single vault unit |
| `rounding`    | `Math.Rounding` | The rounding direction           |

**Returns**

| Name          | Type      | Description         |
| ------------- | --------- | ------------------- |
| `unitsAmount` | `uint256` | The amount of units |

### \_convertUnitsToToken

Converts a units amount to tokens

```solidity
function _convertUnitsToToken(
    address vault,
    IERC20 token,
    uint256 unitsAmount,
    uint256 unitPrice,
    Math.Rounding rounding
) internal view returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type            | Description                      |
| ------------- | --------------- | -------------------------------- |
| `vault`       | `address`       | The address of the vault         |
| `token`       | `IERC20`        | The token to convert             |
| `unitsAmount` | `uint256`       | The amount of units to convert   |
| `unitPrice`   | `uint256`       | The price of a single vault unit |
| `rounding`    | `Math.Rounding` | The rounding direction           |

**Returns**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `tokenAmount` | `uint256` | The amount of tokens |

### \_validatePriceUpdate

Validates a price update

*Price is invalid if it is 0, before the last update, in the future, or if the price age is stale*

```solidity
function _validatePriceUpdate(VaultPriceState storage vaultPriceState, uint256 price, uint256 timestamp)
    internal
    view;
```

**Parameters**

| Name              | Type              | Description                                    |
| ----------------- | ----------------- | ---------------------------------------------- |
| `vaultPriceState` | `VaultPriceState` | The storage pointer to the vault's price state |
| `price`           | `uint256`         | The price of a single vault unit               |
| `timestamp`       | `uint256`         | The timestamp of the price update              |

### \_shouldPause

Determines if a price update should pause the vault

*Vault should pause if the price increase or decrease is too large, or if the min update interval has not passed*

```solidity
function _shouldPause(VaultPriceState storage state, uint256 price, uint32 timestamp) internal view returns (bool);
```

**Parameters**

| Name        | Type              | Description                                    |
| ----------- | ----------------- | ---------------------------------------------- |
| `state`     | `VaultPriceState` | The storage pointer to the vault's price state |
| `price`     | `uint256`         | The price of a single vault unit               |
| `timestamp` | `uint32`          | The timestamp of the price update              |

**Returns**

| Name     | Type   | Description                                                                  |
| -------- | ------ | ---------------------------------------------------------------------------- |
| `<none>` | `bool` | shouldPause True if the price update should pause the vault, false otherwise |


# PriceAndFeeCalculatorV2

**Title:** PriceAndFeeCalculator

Calculates and manages anchor/drift price and fees for multiple vaults that share the same numeraire token Acts as a price oracle and fee accrual engine. Vault registration workflow is:

1. Register a new vault with registerVault()
2. Set the thresholds for the vault with setThresholds()
3. Set the initial price state with setInitialPrice() Once registered, a vault can have its price updated by an authorized entity. Vault owners set thresholds for price changes, update intervals, and price age. Anchor-policy violations can either pause or revert depending on vault-level configuration, while drift-policy violations always revert. Paused vaults don’t accrue fees, reject drift updates, and unpause against the current anchor tuple. Accrues fees on each anchor update, based on TVL and performance since last update Supports conversion between vault units, tokens, and numeraire for deposits/withdrawals. All logic and state is per-vault, supporting many vaults in parallel. Only vault owners can set thresholds and pause/unpause their vaults, whereas accountants can also pause their vaults Integrates with an external oracle registry for token price conversions

### Constants <a href="#constants" id="constants"></a>

#### ORACLE\_REGISTRY <a href="#oracle_registry" id="oracle_registry"></a>

Oracle registry contract for price feeds

```solidity
IOracleRegistry public immutable ORACLE_REGISTRY
```

### State Variables <a href="#state-variables" id="state-variables"></a>

#### \_vaultPriceStates <a href="#vaultpricestates" id="vaultpricestates"></a>

Mapping of vault addresses to their state information

```solidity
mapping(address vault => VaultPriceStateV2 vaultPriceState) internal _vaultPriceStates
```

### Functions <a href="#functions" id="functions"></a>

#### requiresVaultAuthOrAccountant <a href="#requiresvaultauthoraccountant" id="requiresvaultauthoraccountant"></a>

```solidity
modifier requiresVaultAuthOrAccountant(address vault) ;
```

#### constructor <a href="#constructor" id="constructor"></a>

```solidity
constructor(IERC20 numeraire, IOracleRegistry oracleRegistry, address owner_, Authority authority_)
    BaseFeeCalculator(owner_, authority_)
    HasNumeraire(address(numeraire));
```

#### registerVault <a href="#registervault" id="registervault"></a>

Register a new vault with the fee calculator

```solidity
function registerVault() external override;
```

#### setInitialPrice <a href="#setinitialprice" id="setinitialprice"></a>

Set the initial anchor price state for the vault

```solidity
function setInitialPrice(address vault, uint128 price) external requiresVaultAuth(vault);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | Address of the vault     |
| `price` | `uint128` | New initial anchor price |

#### setThresholds <a href="#setthresholds" id="setthresholds"></a>

Set vault thresholds

```solidity
function setThresholds(
    address vault,
    uint16 minPriceToleranceRatio,
    uint16 maxPriceToleranceRatio,
    uint16 minUpdateIntervalMinutes,
    uint16 maxPriceAge,
    uint8 maxUpdateDelayDays
) external requiresVaultAuth(vault);
```

**Parameters**

| Name                       | Type      | Description                                                                |
| -------------------------- | --------- | -------------------------------------------------------------------------- |
| `vault`                    | `address` | Address of the vault                                                       |
| `minPriceToleranceRatio`   | `uint16`  | Minimum ratio (of a price decrease) in basis points                        |
| `maxPriceToleranceRatio`   | `uint16`  | Maximum ratio (of a price increase) in basis points                        |
| `minUpdateIntervalMinutes` | `uint16`  | The minimum interval between updates in minutes                            |
| `maxPriceAge`              | `uint16`  | Max delay between when a vault was priced and when the price is acceptable |
| `maxUpdateDelayDays`       | `uint8`   | Max delay between two price updates                                        |

#### setPauseOnBadAnchorUpdate <a href="#setpauseonbadanchorupdate" id="setpauseonbadanchorupdate"></a>

Set whether out-of-range updates should pause or revert

MUST be configurable by vault owner/authority

```solidity
function setPauseOnBadAnchorUpdate(address vault, bool pauseOnBadAnchorUpdate) external requiresVaultAuth(vault);
```

**Parameters**

| Name                     | Type      | Description                                                    |
| ------------------------ | --------- | -------------------------------------------------------------- |
| `vault`                  | `address` | Address of the vault                                           |
| `pauseOnBadAnchorUpdate` | `bool`    | True to pause on bad anchor update, false to revert atomically |

#### setAnchorPrice <a href="#setanchorprice" id="setanchorprice"></a>

Set the anchor price for the vault in numeraire terms

```solidity
function setAnchorPrice(address vault, uint128 price, uint32 timestamp) external onlyVaultAccountant(vault);
```

**Parameters**

| Name        | Type      | Description                                  |
| ----------- | --------- | -------------------------------------------- |
| `vault`     | `address` | Address of the vault                         |
| `price`     | `uint128` | New anchor price                             |
| `timestamp` | `uint32`  | Timestamp when the anchor price was measured |

#### setDriftPrice <a href="#setdriftprice" id="setdriftprice"></a>

Set the drift price for the vault in numeraire terms

MUST revert when the vault is paused

```solidity
function setDriftPrice(address vault, uint128 price, uint32 timestamp) external onlyVaultAccountant(vault);
```

**Parameters**

| Name        | Type      | Description                                 |
| ----------- | --------- | ------------------------------------------- |
| `vault`     | `address` | Address of the vault                        |
| `price`     | `uint128` | New drift price                             |
| `timestamp` | `uint32`  | Timestamp when the drift price was measured |

#### pauseVault <a href="#pausevault" id="pausevault"></a>

Pause the vault

```solidity
function pauseVault(address vault) external requiresVaultAuthOrAccountant(vault);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

#### unpauseVault <a href="#unpausevault" id="unpausevault"></a>

Unpause the vault

MUST revert if price or timestamp don’t exactly match the current anchor tuple

```solidity
function unpauseVault(address vault, uint128 price, uint32 timestamp) external requiresVaultAuth(vault);
```

**Parameters**

| Name        | Type      | Description                               |
| ----------- | --------- | ----------------------------------------- |
| `vault`     | `address` | Address of the vault                      |
| `price`     | `uint128` | Expected anchor price at unpause time     |
| `timestamp` | `uint32`  | Expected anchor timestamp at unpause time |

#### resetHighestPrice <a href="#resethighestprice" id="resethighestprice"></a>

Resets the highest price for a vault to the current anchor price

```solidity
function resetHighestPrice(address vault) external requiresVaultAuth(vault);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

#### convertUnitsToToken <a href="#convertunitstotoken" id="convertunitstotoken"></a>

Convert units to token amount

```solidity
function convertUnitsToToken(address vault, IERC20 token, uint256 unitsAmount)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

#### convertUnitsToTokenIfActive <a href="#convertunitstotokenifactive" id="convertunitstotokenifactive"></a>

Convert units to token amount if vault is not paused

MUST revert if vault is paused

```solidity
function convertUnitsToTokenIfActive(address vault, IERC20 token, uint256 unitsAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `unitsAmount` | `uint256`       | Amount of units      |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

#### convertUnitsToNumeraire <a href="#convertunitstonumeraire" id="convertunitstonumeraire"></a>

Convert units to numeraire token amount

```solidity
function convertUnitsToNumeraire(address vault, uint256 unitsAmount) external view returns (uint256);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | numeraireAmount Amount of numeraire |

#### convertUnitsToNumeraire <a href="#convertunitstonumeraire-1" id="convertunitstonumeraire-1"></a>

Convert units to numeraire token amount with rounding control

```solidity
function convertUnitsToNumeraire(address vault, uint256 unitsAmount, Math.Rounding rounding)
    external
    view
    returns (uint256);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `unitsAmount` | `uint256`       | Amount of units      |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | numeraireAmount Amount of numeraire |

#### convertNumeraireToUnits <a href="#convertnumerairetounits" id="convertnumerairetounits"></a>

Convert numeraire amount to vault units

```solidity
function convertNumeraireToUnits(address vault, uint256 numeraireAmount, Math.Rounding rounding)
    external
    view
    returns (uint256);
```

**Parameters**

| Name              | Type            | Description          |
| ----------------- | --------------- | -------------------- |
| `vault`           | `address`       | Address of the vault |
| `numeraireAmount` | `uint256`       | Amount of numeraire  |
| `rounding`        | `Math.Rounding` | The rounding mode    |

**Returns**

| Name     | Type      | Description                 |
| -------- | --------- | --------------------------- |
| `<none>` | `uint256` | unitsAmount Amount of units |

#### convertNumeraireToToken <a href="#convertnumerairetotoken" id="convertnumerairetotoken"></a>

Convert numeraire amount to token amount via oracle

Returns numeraireAmount unchanged when token is the numeraire

```solidity
function convertNumeraireToToken(address vault, IERC20 token, uint256 numeraireAmount)
    external
    view
    returns (uint256);
```

**Parameters**

| Name              | Type      | Description          |
| ----------------- | --------- | -------------------- |
| `vault`           | `address` | Address of the vault |
| `token`           | `IERC20`  | Address of the token |
| `numeraireAmount` | `uint256` | Amount of numeraire  |

**Returns**

| Name     | Type      | Description                  |
| -------- | --------- | ---------------------------- |
| `<none>` | `uint256` | tokenAmount Amount of tokens |

#### convertTokenToNumeraire <a href="#converttokentonumeraire" id="converttokentonumeraire"></a>

Convert token amount to numeraire via oracle

Returns tokenAmount unchanged when token is the numeraire

```solidity
function convertTokenToNumeraire(address vault, IERC20 token, uint256 tokenAmount) external view returns (uint256);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `tokenAmount` | `uint256` | Amount of tokens     |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | numeraireAmount Amount of numeraire |

#### convertTokenToUnits <a href="#converttokentounits" id="converttokentounits"></a>

Convert token amount to units

```solidity
function convertTokenToUnits(address vault, IERC20 token, uint256 tokenAmount)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `tokenAmount` | `uint256` | Amount of tokens     |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

#### convertTokenToUnitsIfActive <a href="#converttokentounitsifactive" id="converttokentounitsifactive"></a>

Convert token amount to units if vault is not paused

MUST revert if vault is paused

```solidity
function convertTokenToUnitsIfActive(address vault, IERC20 token, uint256 tokenAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `tokenAmount` | `uint256`       | Amount of tokens     |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

#### getVaultState <a href="#getvaultstate" id="getvaultstate"></a>

Return the state of the vault

```solidity
function getVaultState(address vault) external view returns (VaultPriceStateV2 memory, VaultAccruals memory);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type                | Description                                   |
| -------- | ------------------- | --------------------------------------------- |
| `<none>` | `VaultPriceStateV2` | vaultPriceState The price state of the vault  |
| `<none>` | `VaultAccruals`     | vaultAccruals The accruals state of the vault |

#### getVaultPriceTimestamp <a href="#getvaultpricetimestamp" id="getvaultpricetimestamp"></a>

Returns the timestamp of the last submitted price for a vault

```solidity
function getVaultPriceTimestamp(address vault) external view returns (uint256 timestamp);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name        | Type      | Description                                    |
| ----------- | --------- | ---------------------------------------------- |
| `timestamp` | `uint256` | The timestamp of the vault’s last price update |

#### getAnchorTimestamp <a href="#getanchortimestamp" id="getanchortimestamp"></a>

Returns the timestamp of the last submitted anchor price for a vault

```solidity
function getAnchorTimestamp(address vault) external view returns (uint32 timestamp);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name        | Type     | Description                                       |
| ----------- | -------- | ------------------------------------------------- |
| `timestamp` | `uint32` | The timestamp of the vault’s current anchor price |

#### getVaultValueAtLastUpdate <a href="#getvaultvalueatlastupdate" id="getvaultvalueatlastupdate"></a>

Returns the vault value in numeraire at the last price update

MUST revert if the vault is paused

```solidity
function getVaultValueAtLastUpdate(address vault) external view returns (uint256);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type      | Description                                                                           |
| -------- | --------- | ------------------------------------------------------------------------------------- |
| `<none>` | `uint256` | vaultValue The vault value in numeraire computed from lastTotalSupply and anchorPrice |

#### isVaultPaused <a href="#isvaultpaused" id="isvaultpaused"></a>

Check if a vault is paused

```solidity
function isVaultPaused(address vault) external view returns (bool);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name     | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `<none>` | `bool` | True if the vault is paused, false otherwise |

#### previewFees <a href="#previewfees" id="previewfees"></a>

```solidity
function previewFees(address vault, uint256 feeTokenBalance) external view override returns (uint256, uint256);
```

#### version <a href="#version" id="version"></a>

Returns the semantic version string for this contract surface

```solidity
function version() external pure returns (string memory);
```

**Returns**

| Name     | Type     | Description                 |
| -------- | -------- | --------------------------- |
| `<none>` | `string` | The semantic version string |

#### \_accrueFees <a href="#accruefees" id="accruefees"></a>

Accrues fees for a vault

It is assumed that validation has already been done Tvl is calculated as the product of the minimum of the current and last price and the minimum of the current and last total supply. This is to minimize potential issues with price spikes

```solidity
function _accrueFees(address vault, uint256 price, uint256 timestamp) internal;
```

**Parameters**

| Name        | Type      | Description                       |
| ----------- | --------- | --------------------------------- |
| `vault`     | `address` | The address of the vault          |
| `price`     | `uint256` | The price of a single vault unit  |
| `timestamp` | `uint256` | The timestamp of the price update |

#### \_setVaultPaused <a href="#setvaultpaused" id="setvaultpaused"></a>

Sets the paused state for a vault

```solidity
function _setVaultPaused(VaultPriceStateV2 storage vaultPriceState, address vault, bool paused) internal;
```

**Parameters**

| Name              | Type                | Description                                    |
| ----------------- | ------------------- | ---------------------------------------------- |
| `vaultPriceState` | `VaultPriceStateV2` | The storage pointer to the vault’s price state |
| `vault`           | `address`           | The address of the vault                       |
| `paused`          | `bool`              | The new paused state                           |

#### \_convertTokenToUnits <a href="#converttokentounits" id="converttokentounits"></a>

Converts a token amount to units

```solidity
function _convertTokenToUnits(
    address vault,
    IERC20 token,
    uint256 tokenAmount,
    uint256 unitPrice,
    Math.Rounding rounding
) internal view returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type            | Description                      |
| ------------- | --------------- | -------------------------------- |
| `vault`       | `address`       | The address of the vault         |
| `token`       | `IERC20`        | The token to convert             |
| `tokenAmount` | `uint256`       | The amount of tokens to convert  |
| `unitPrice`   | `uint256`       | The price of a single vault unit |
| `rounding`    | `Math.Rounding` | The rounding direction           |

**Returns**

| Name          | Type      | Description         |
| ------------- | --------- | ------------------- |
| `unitsAmount` | `uint256` | The amount of units |

#### \_convertUnitsToToken <a href="#convertunitstotoken" id="convertunitstotoken"></a>

Converts a units amount to tokens

```solidity
function _convertUnitsToToken(
    address vault,
    IERC20 token,
    uint256 unitsAmount,
    uint256 unitPrice,
    Math.Rounding rounding
) internal view returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type            | Description                      |
| ------------- | --------------- | -------------------------------- |
| `vault`       | `address`       | The address of the vault         |
| `token`       | `IERC20`        | The token to convert             |
| `unitsAmount` | `uint256`       | The amount of units to convert   |
| `unitPrice`   | `uint256`       | The price of a single vault unit |
| `rounding`    | `Math.Rounding` | The rounding direction           |

**Returns**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `tokenAmount` | `uint256` | The amount of tokens |

#### \_getQuoteCeil <a href="#getquoteceil" id="getquoteceil"></a>

Returns the value of `baseAmount` of `baseToken` in `quoteToken` terms for `vault`

```solidity
function _getQuoteCeil(address vault, uint256 baseAmount, IERC20 baseToken, IERC20 quoteToken)
    internal
    view
    returns (uint256 quoteAmount);
```

**Parameters**

| Name         | Type      | Description                                                  |
| ------------ | --------- | ------------------------------------------------------------ |
| `vault`      | `address` | The address of the vault used for oracle override resolution |
| `baseAmount` | `uint256` | The amount of base token to convert                          |
| `baseToken`  | `IERC20`  | The base token being quoted                                  |
| `quoteToken` | `IERC20`  | The quote token to convert into                              |

**Returns**

| Name          | Type      | Description                   |
| ------------- | --------- | ----------------------------- |
| `quoteAmount` | `uint256` | The ceil-rounded quote amount |

#### \_validatePriceUpdate <a href="#validatepriceupdate" id="validatepriceupdate"></a>

Validates a price update

Price is invalid if it is 0, before the last update, in the future, or if the price age is stale

```solidity
function _validatePriceUpdate(
    VaultPriceStateV2 storage state,
    uint256 price,
    uint256 timestamp,
    uint256 referenceTimestamp
) internal view;
```

**Parameters**

| Name                 | Type                | Description                                           |
| -------------------- | ------------------- | ----------------------------------------------------- |
| `state`              | `VaultPriceStateV2` | The storage pointer to the vault’s price state        |
| `price`              | `uint256`           | The price of a single vault unit                      |
| `timestamp`          | `uint256`           | The timestamp of the price update                     |
| `referenceTimestamp` | `uint256`           | The timestamp that the candidate update MUST be after |

#### \_shouldPauseAnchor <a href="#shouldpauseanchor" id="shouldpauseanchor"></a>

Determines if a price update should pause the vault

Vault should pause if the price increase or decrease is too large, or if the min update interval has not passed

```solidity
function _shouldPauseAnchor(
    VaultPriceStateV2 storage state,
    uint256 price,
    uint32 timestamp,
    uint256 anchorTimestamp
) internal view returns (bool);
```

**Parameters**

| Name              | Type                | Description                                    |
| ----------------- | ------------------- | ---------------------------------------------- |
| `state`           | `VaultPriceStateV2` | The storage pointer to the vault’s price state |
| `price`           | `uint256`           | The price of a single vault unit               |
| `timestamp`       | `uint32`            | The timestamp of the price update              |
| `anchorTimestamp` | `uint256`           | The previous anchor timestamp                  |

**Returns**

| Name     | Type   | Description                                                                  |
| -------- | ------ | ---------------------------------------------------------------------------- |
| `<none>` | `bool` | shouldPause True if the price update should pause the vault, false otherwise |

#### \_getCurrentPrice <a href="#getcurrentprice" id="getcurrentprice"></a>

Returns the current price by latest update between anchor and drift

```solidity
function _getCurrentPrice(VaultPriceStateV2 storage state) internal view returns (uint128 price);
```

**Parameters**

| Name    | Type                | Description                                    |
| ------- | ------------------- | ---------------------------------------------- |
| `state` | `VaultPriceStateV2` | The storage pointer to the vault’s price state |

**Returns**

| Name    | Type      | Description   |
| ------- | --------- | ------------- |
| `price` | `uint128` | Current price |

#### \_getLastTimestamp <a href="#getlasttimestamp" id="getlasttimestamp"></a>

Returns the latest timestamp between anchor and drift updates

```solidity
function _getLastTimestamp(VaultPriceStateV2 storage state) internal view returns (uint256 timestamp);
```

**Parameters**

| Name    | Type                | Description                                    |
| ------- | ------------------- | ---------------------------------------------- |
| `state` | `VaultPriceStateV2` | The storage pointer to the vault’s price state |

**Returns**

| Name        | Type      | Description            |
| ----------- | --------- | ---------------------- |
| `timestamp` | `uint256` | Active price timestamp |

#### \_isPriceWithinAnchorBand <a href="#ispricewithinanchorband" id="ispricewithinanchorband"></a>

Validates whether a candidate price stays inside the anchor tolerance band

```solidity
function _isPriceWithinAnchorBand(VaultPriceStateV2 storage state, uint256 anchorPrice, uint256 candidatePrice)
    internal
    view
    returns (bool isWithinBand);
```

**Parameters**

| Name             | Type                | Description                                            |
| ---------------- | ------------------- | ------------------------------------------------------ |
| `state`          | `VaultPriceStateV2` | The storage pointer to the vault’s price state         |
| `anchorPrice`    | `uint256`           | The anchor price used for band checks                  |
| `candidatePrice` | `uint256`           | The proposed price to validate against the anchor band |

**Returns**

| Name           | Type   | Description                                             |
| -------------- | ------ | ------------------------------------------------------- |
| `isWithinBand` | `bool` | True when candidate is inside the anchor tolerance band |

#### \_isUpdateDelayExceeded <a href="#isupdatedelayexceeded" id="isupdatedelayexceeded"></a>

Checks whether the elapsed time between updates exceeds configured max delay

```solidity
function _isUpdateDelayExceeded(uint256 lastTimestamp, uint256 timestamp, uint256 maxUpdateDelayDays)
    internal
    pure
    returns (bool isExceeded);
```

**Parameters**

| Name                 | Type      | Description                          |
| -------------------- | --------- | ------------------------------------ |
| `lastTimestamp`      | `uint256` | The timestamp of the prior update    |
| `timestamp`          | `uint256` | The timestamp of the proposed update |
| `maxUpdateDelayDays` | `uint256` | Maximum allowed delay in days        |

**Returns**

| Name         | Type   | Description                                           |
| ------------ | ------ | ----------------------------------------------------- |
| `isExceeded` | `bool` | True when elapsed time exceeds the configured maximum |


# Provisioner

**Inherits:** IProvisioner, Auth2Step, ReentrancyGuardTransient

Entry and exit point for {MultiDepositorVault}. Handles all deposits and redemptions Uses {IPriceAndFeeCalculator} to convert between tokens and vault units. Supports both sync and async deposits; only async redeems. Manages deposit caps, refund timeouts, and request replay protection. All assets must flow through this contract to enter or exit the vault. Sync deposits are processed instantly, but stay refundable for a period of time. Async requests can either be solved by authorized solvers, going through the vault, or directly by anyone willing to pay units (for deposits) or tokens (for redeems), pocketing the solver tip, always paid in tokens

## State Variables

### PRICE\_FEE\_CALCULATOR

The price and fee calculator contract

```solidity
IPriceAndFeeCalculator public immutable PRICE_FEE_CALCULATOR;
```

### MULTI\_DEPOSITOR\_VAULT

The multi depositor vault contract

```solidity
address public immutable MULTI_DEPOSITOR_VAULT;
```

### tokensDetails

Mapping of token to token details

```solidity
mapping(IERC20 token => TokenDetails details) public tokensDetails;
```

### depositCap

Maximum total value of deposits in numeraire terms

```solidity
uint256 public depositCap;
```

### depositRefundTimeout

Time period in seconds during which sync deposits can be refunded

```solidity
uint256 public depositRefundTimeout;
```

### syncDepositHashes

Mapping of active sync deposit hashes

*True if a sync deposit is active with the hashed parameters*

```solidity
mapping(bytes32 syncDepositHash => bool exists) public syncDepositHashes;
```

### asyncDepositHashes

Mapping of async deposit hash to its existence

*True if deposit request exists, false if it was refunded or solved*

```solidity
mapping(bytes32 asyncDepositHash => bool exists) public asyncDepositHashes;
```

### asyncRedeemHashes

Mapping of async redeem hash to its existence

*True if redeem request exists, false if it was refunded or solved*

```solidity
mapping(bytes32 asyncRedeemHash => bool exists) public asyncRedeemHashes;
```

### userUnitsRefundableUntil

Mapping of user address to timestamp until which their units are locked

```solidity
mapping(address user => uint256 unitsLockedUntil) public userUnitsRefundableUntil;
```

## Functions

### anyoneButVault

Ensures the caller is not the vault

```solidity
modifier anyoneButVault();
```

### constructor

```solidity
constructor(
    IPriceAndFeeCalculator priceAndFeeCalculator,
    address multiDepositorVault,
    address owner_,
    Authority authority_
) Auth2Step(owner_, authority_);
```

### deposit

Deposit tokens directly into the vault

*MUST revert if tokensIn is 0, minUnitsOut is 0, or sync deposits are disabled*

```solidity
function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut)
    external
    anyoneButVault
    returns (uint256 unitsOut);
```

**Parameters**

| Name          | Type      | Description                          |
| ------------- | --------- | ------------------------------------ |
| `token`       | `IERC20`  | The token to deposit                 |
| `tokensIn`    | `uint256` | The amount of tokens to deposit      |
| `minUnitsOut` | `uint256` | The minimum amount of units expected |

**Returns**

| Name       | Type      | Description                                 |
| ---------- | --------- | ------------------------------------------- |
| `unitsOut` | `uint256` | The amount of shares minted to the receiver |

### mint

Mint exact amount of units by depositing required tokens

```solidity
function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn) external anyoneButVault returns (uint256 tokensIn);
```

**Parameters**

| Name          | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `token`       | `IERC20`  | The token to deposit                        |
| `unitsOut`    | `uint256` | The exact amount of units to mint           |
| `maxTokensIn` | `uint256` | Maximum amount of tokens willing to deposit |

**Returns**

| Name       | Type      | Description                                            |
| ---------- | --------- | ------------------------------------------------------ |
| `tokensIn` | `uint256` | The amount of tokens used to mint the requested shares |

### refundDeposit

Refund a deposit within the refund period

*Only callable by authorized addresses*

```solidity
function refundDeposit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, uint256 refundableUntil)
    external
    requiresAuth;
```

**Parameters**

| Name              | Type      | Description                              |
| ----------------- | --------- | ---------------------------------------- |
| `sender`          | `address` | The original depositor                   |
| `token`           | `IERC20`  | The deposited token                      |
| `tokenAmount`     | `uint256` | The amount of tokens deposited           |
| `unitsAmount`     | `uint256` | The amount of units minted               |
| `refundableUntil` | `uint256` | Timestamp until which refund is possible |

### requestDeposit

Create a new deposit request to be solved by solvers

```solidity
function requestDeposit(
    IERC20 token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external anyoneButVault;
```

**Parameters**

| Name           | Type      | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| `token`        | `IERC20`  | The token to deposit                               |
| `tokensIn`     | `uint256` | The amount of tokens to deposit                    |
| `minUnitsOut`  | `uint256` | The minimum amount of units expected               |
| `solverTip`    | `uint256` | The tip offered to the solver                      |
| `deadline`     | `uint256` | Duration in seconds for which the request is valid |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use      |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request       |

### requestRedeem

Create a new redeem request to be solved by solvers

```solidity
function requestRedeem(
    IERC20 token,
    uint256 unitsIn,
    uint256 minTokensOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external anyoneButVault;
```

**Parameters**

| Name           | Type      | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| `token`        | `IERC20`  | The token to receive                               |
| `unitsIn`      | `uint256` | The amount of units to redeem                      |
| `minTokensOut` | `uint256` | The minimum amount of tokens expected              |
| `solverTip`    | `uint256` | The tip offered to the solver                      |
| `deadline`     | `uint256` | Duration in seconds for which the request is valid |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use      |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request       |

### refundRequest

Refund an expired deposit or redeem request

*Can only be called after request deadline has passed*

```solidity
function refundRequest(IERC20 token, Request calldata request) external nonReentrant;
```

**Parameters**

| Name      | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `token`   | `IERC20`  | The token involved in the request |
| `request` | `Request` | The request to refund             |

### solveRequestsVault

Solve multiple requests using vault's liquidity

*Only callable by authorized addresses*

```solidity
function solveRequestsVault(IERC20 token, Request[] calldata requests) external requiresAuth nonReentrant;
```

**Parameters**

| Name       | Type        | Description                           |
| ---------- | ----------- | ------------------------------------- |
| `token`    | `IERC20`    | The token for which to solve requests |
| `requests` | `Request[]` | Array of requests to solve            |

### solveRequestsDirect

Solve multiple requests using solver's own liquidity

```solidity
function solveRequestsDirect(IERC20 token, Request[] calldata requests) external nonReentrant;
```

**Parameters**

| Name       | Type        | Description                           |
| ---------- | ----------- | ------------------------------------- |
| `token`    | `IERC20`    | The token for which to solve requests |
| `requests` | `Request[]` | Array of requests to solve            |

### setDepositDetails

Update deposit parameters

```solidity
function setDepositDetails(uint256 depositCap_, uint256 depositRefundTimeout_) external requiresAuth;
```

**Parameters**

| Name                    | Type      | Description                                   |
| ----------------------- | --------- | --------------------------------------------- |
| `depositCap_`           | `uint256` | New maximum total value that can be deposited |
| `depositRefundTimeout_` | `uint256` | New time window for deposit refunds           |

### setTokenDetails

Update token parameters

```solidity
function setTokenDetails(IERC20 token, TokenDetails calldata details) external requiresAuth;
```

**Parameters**

| Name      | Type           | Description         |
| --------- | -------------- | ------------------- |
| `token`   | `IERC20`       | The token to update |
| `details` | `TokenDetails` |                     |

### removeToken

Removes token from provisioner

```solidity
function removeToken(IERC20 token) external requiresAuth;
```

**Parameters**

| Name    | Type     | Description             |
| ------- | -------- | ----------------------- |
| `token` | `IERC20` | The token to be removed |

### maxDeposit

Return maximum amount that can still be deposited

```solidity
function maxDeposit() external view returns (uint256);
```

**Returns**

| Name     | Type      | Description                          |
| -------- | --------- | ------------------------------------ |
| `<none>` | `uint256` | Amount of deposit capacity remaining |

### areUserUnitsLocked

Check if a user's units are currently locked

```solidity
function areUserUnitsLocked(address user) external view returns (bool);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `user` | `address` | The address to check |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if user's units are locked, false otherwise |

### getDepositHash

Computes the hash for a sync deposit

```solidity
function getDepositHash(address user, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, uint256 refundableUntil)
    external
    pure
    returns (bytes32);
```

**Parameters**

| Name              | Type      | Description                                         |
| ----------------- | --------- | --------------------------------------------------- |
| `user`            | `address` | The address making the deposit                      |
| `token`           | `IERC20`  | The token being deposited                           |
| `tokenAmount`     | `uint256` | The amount of tokens to deposit                     |
| `unitsAmount`     | `uint256` | Minimum amount of units to receive                  |
| `refundableUntil` | `uint256` | The timestamp until which the deposit is refundable |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the deposit |

### getRequestHash

Computes the hash for a generic request

```solidity
function getRequestHash(IERC20 token, Request calldata request) external pure returns (bytes32);
```

**Parameters**

| Name      | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `token`   | `IERC20`  | The token involved in the request |
| `request` | `Request` | The request struct                |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

### \_syncDeposit

Handles a synchronous deposit, records the deposit hash, and enters the vault

*Reverts if the deposit hash already exists. Sets the refundable period for the user*

```solidity
function _syncDeposit(IERC20 token, uint256 tokenAmount, uint256 unitAmount) internal;
```

**Parameters**

| Name          | Type      | Description                                    |
| ------------- | --------- | ---------------------------------------------- |
| `token`       | `IERC20`  | The ERC20 token to deposit                     |
| `tokenAmount` | `uint256` | The amount of tokens to deposit                |
| `unitAmount`  | `uint256` | The amount of vault units to mint for the user |

### \_solveDepositVaultAutoPrice

Solves an async deposit request for the vault, transfering tokens or refunding as needed

*
* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* units out is less than min required, emits AmountBoundExceeded
* deposit cap would be exceeded, emits DepositCapExceeded
* If deadline not passed, processes deposit and emits DepositSolved
* If deadline passed, refunds and emits DepositRefunded
* Always unsets hash after processing\*

```solidity
function _solveDepositVaultAutoPrice(
    IERC20 token,
    uint256 depositMultiplier,
    Request calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name                | Type      | Description                                                            |
| ------------------- | --------- | ---------------------------------------------------------------------- |
| `token`             | `IERC20`  | The ERC20 token being deposited                                        |
| `depositMultiplier` | `uint256` | The multiplier (in BPS) applied to the deposit for premium calculation |
| `request`           | `Request` | The deposit request struct containing all user parameters              |
| `priceAge`          | `uint256` | The age of the price data used for conversion                          |
| `index`             | `uint256` | The index of the request in the given solving batch                    |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

### \_solveDepositVaultFixedPrice

Solves a fixed price deposit request for the vault, transfering tokens or refunding as needed

*User gets exactly min units out, but may over‑fund, the difference is paid to the solver as a tip*

*
* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* tokens needed exceed the maximum allowed, emits AmountBoundExceeded
* deposit cap would be exceeded, emits DepositCapExceeded
* If deadline not passed, processes deposit and emits DepositSolved
* If deadline passed, refunds and emits DepositRefunded
* Always unsets hash after processing\*

```solidity
function _solveDepositVaultFixedPrice(
    IERC20 token,
    uint256 depositMultiplier,
    Request calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name                | Type      | Description                                                            |
| ------------------- | --------- | ---------------------------------------------------------------------- |
| `token`             | `IERC20`  | The ERC20 token being deposited                                        |
| `depositMultiplier` | `uint256` | The multiplier (in BPS) applied to the deposit for premium calculation |
| `request`           | `Request` | The deposit request struct containing all user parameters              |
| `priceAge`          | `uint256` | The age of the price data used for conversion                          |
| `index`             | `uint256` | The index of the request in the given solving batch                    |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

### \_solveRedeemVaultAutoPrice

Solves an async redeem request for the vault, transfering tokens or refunding as needed

*
* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* token out after premium is less than min required, emits AmountBoundExceeded
* If deadline not passed, processes redeem and emits RedeemSolved
* If deadline passed, refunds and emits RedeemRefunded
* Always unsets hash after processing\*

```solidity
function _solveRedeemVaultAutoPrice(
    IERC20 token,
    uint256 redeemMultiplier,
    Request calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name               | Type      | Description                                                           |
| ------------------ | --------- | --------------------------------------------------------------------- |
| `token`            | `IERC20`  | The ERC20 token being redeemed                                        |
| `redeemMultiplier` | `uint256` | The multiplier (in BPS) applied to the redeem for premium calculation |
| `request`          | `Request` | The redeem request struct containing all user parameters              |
| `priceAge`         | `uint256` | The age of the price data used for conversion                         |
| `index`            | `uint256` | The index of the request in the given solving batch                   |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

### \_solveRedeemVaultFixedPrice

Solves a fixed price redeem request for the vault, transfering tokens or refunding as needed

*User gets exactly min tokens out, but may under‑fund, the difference is paid to the solver as a tip*

*
* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* If deadline not passed, processes redeem and emits RedeemSolved
* If deadline passed, refunds and emits RedeemRefunded
* Always unsets hash after processing\*

```solidity
function _solveRedeemVaultFixedPrice(
    IERC20 token,
    uint256 redeemMultiplier,
    Request calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name               | Type      | Description                                                           |
| ------------------ | --------- | --------------------------------------------------------------------- |
| `token`            | `IERC20`  | The ERC20 token being redeemed                                        |
| `redeemMultiplier` | `uint256` | The multiplier (in BPS) applied to the redeem for premium calculation |
| `request`          | `Request` | The redeem request struct containing all user parameters              |
| `priceAge`         | `uint256` | The age of the price data used for conversion                         |
| `index`            | `uint256` | The index of the request in the given solving batch                   |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

### \_solveDepositDirect

Solves a direct deposit request, transfering tokens and units between users

*
* Returns early if any of:
* request hash is not set, emits InvalidRequestHash
* If deadline not passed, transfers units and tokens, emits DepositSolved
* If deadline passed, refunds tokens, emits DepositRefunded
* Always unsets hash after processing\*

```solidity
function _solveDepositDirect(IERC20 token, Request calldata request) internal;
```

**Parameters**

| Name      | Type      | Description                                               |
| --------- | --------- | --------------------------------------------------------- |
| `token`   | `IERC20`  | The ERC20 token being deposited                           |
| `request` | `Request` | The deposit request struct containing all user parameters |

### \_solveRedeemDirect

Solves a direct redeem request, transfering tokens and units between users

*
* Returns early if:
* request hash is not set, emits InvalidRequestHash
* If deadline not passed, transfers units and tokens, emits RedeemSolved
* If deadline passed, refunds units, emits RedeemRefunded
* Always unsets hash after processing\*

```solidity
function _solveRedeemDirect(IERC20 token, Request calldata request) internal;
```

**Parameters**

| Name      | Type      | Description                                              |
| --------- | --------- | -------------------------------------------------------- |
| `token`   | `IERC20`  | The ERC20 token being redeemed                           |
| `request` | `Request` | The redeem request struct containing all user parameters |

### \_guardPriceAge

Checks if the price age exceeds the maximum allowed and emits an event if so

```solidity
function _guardPriceAge(uint256 priceAge, uint256 maxPriceAge, uint256 index) internal returns (bool);
```

**Parameters**

| Name          | Type      | Description                                                          |
| ------------- | --------- | -------------------------------------------------------------------- |
| `priceAge`    | `uint256` | The difference between when price was measured and submitted onchain |
| `maxPriceAge` | `uint256` | The maximum allowed price age                                        |
| `index`       | `uint256` | The index of the request in the given solving batch                  |

**Returns**

| Name     | Type   | Description                                    |
| -------- | ------ | ---------------------------------------------- |
| `<none>` | `bool` | True if price age is too high, false otherwise |

### \_guardInvalidRequestHash

Checks if the request hash exists and emits an event if not

```solidity
function _guardInvalidRequestHash(bool hashExists, bytes32 requestHash) internal returns (bool);
```

**Parameters**

| Name          | Type      | Description             |
| ------------- | --------- | ----------------------- |
| `hashExists`  | `bool`    | Whether the hash exists |
| `requestHash` | `bytes32` | The request hash        |

**Returns**

| Name     | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `<none>` | `bool` | True if hash does not exist, false otherwise |

### \_guardInsufficientTokensForTip

Checks if there are enough tokens for the solver tip and emits an event if not

```solidity
function _guardInsufficientTokensForTip(uint256 tokens, uint256 solverTip, uint256 index) internal returns (bool);
```

**Parameters**

| Name        | Type      | Description                                         |
| ----------- | --------- | --------------------------------------------------- |
| `tokens`    | `uint256` | The number of tokens                                |
| `solverTip` | `uint256` | The solver tip amount                               |
| `index`     | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                        |
| -------- | ------ | -------------------------------------------------- |
| `<none>` | `bool` | True if not enough tokens for tip, false otherwise |

### \_guardAmountBound

Checks if the amount is less than the bound and emits an event if so

```solidity
function _guardAmountBound(uint256 amount, uint256 bound, uint256 index) internal returns (bool);
```

**Parameters**

| Name     | Type      | Description                                         |
| -------- | --------- | --------------------------------------------------- |
| `amount` | `uint256` | The actual amount                                   |
| `bound`  | `uint256` | The minimum required amount                         |
| `index`  | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                        |
| -------- | ------ | -------------------------------------------------- |
| `<none>` | `bool` | True if amount is less than bound, false otherwise |

### \_guardDepositCapExceeded

Checks if the deposit cap would be exceeded and emits an event if so

```solidity
function _guardDepositCapExceeded(uint256 totalUnits, uint256 index) internal returns (bool);
```

**Parameters**

| Name         | Type      | Description                                         |
| ------------ | --------- | --------------------------------------------------- |
| `totalUnits` | `uint256` | The total units after deposit                       |
| `index`      | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                            |
| -------- | ------ | ------------------------------------------------------ |
| `<none>` | `bool` | True if deposit cap would be exceeded, false otherwise |

### \_requireSyncDepositsEnabled

Reverts if sync deposits are not enabled for the token

```solidity
function _requireSyncDepositsEnabled(IERC20 token) internal view returns (TokenDetails storage tokenDetails);
```

**Parameters**

| Name    | Type     | Description              |
| ------- | -------- | ------------------------ |
| `token` | `IERC20` | The ERC20 token to check |

**Returns**

| Name           | Type           | Description                         |
| -------------- | -------------- | ----------------------------------- |
| `tokenDetails` | `TokenDetails` | The token details storage reference |

### \_requireDepositCapNotExceeded

Reverts if deposit cap would be exceeded by adding units

```solidity
function _requireDepositCapNotExceeded(uint256 units) internal view;
```

**Parameters**

| Name    | Type      | Description                |
| ------- | --------- | -------------------------- |
| `units` | `uint256` | The number of units to add |

### \_isDepositCapExceeded

Checks if deposit cap would be exceeded by adding units

```solidity
function _isDepositCapExceeded(uint256 units) internal view returns (bool);
```

**Parameters**

| Name    | Type      | Description                |
| ------- | --------- | -------------------------- |
| `units` | `uint256` | The number of units to add |

**Returns**

| Name     | Type   | Description                                            |
| -------- | ------ | ------------------------------------------------------ |
| `<none>` | `bool` | True if deposit cap would be exceeded, false otherwise |

### \_tokensToUnitsFloorIfActive

Converts token amount to units, applying multiplier and flooring

```solidity
function _tokensToUnitsFloorIfActive(IERC20 token, uint256 tokens, uint256 multiplier)
    internal
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `tokens`     | `uint256` | The amount of tokens    |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `<none>` | `uint256` | The resulting units (floored) |

### \_unitsToTokensFloorIfActive

Converts units to token amount, applying multiplier and flooring

```solidity
function _unitsToTokensFloorIfActive(IERC20 token, uint256 units, uint256 multiplier) internal view returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `units`      | `uint256` | The amount of units     |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                          |
| -------- | --------- | ------------------------------------ |
| `<none>` | `uint256` | The resulting token amount (floored) |

### \_unitsToTokensCeilIfActive

Converts units to token amount, applying multiplier and ceiling

```solidity
function _unitsToTokensCeilIfActive(IERC20 token, uint256 units, uint256 multiplier) internal view returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `units`      | `uint256` | The amount of units     |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | The resulting token amount (ceiled) |

### \_getDepositHash

Get the hash of a deposit

*Since refundableUntil is block.timestamp + depositRefundTimeout (which is subject to change), it's theoretically possible to have a hash collision, but the probability is negligible and we optimize for the common case*

```solidity
function _getDepositHash(address user, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, uint256 refundableUntil)
    internal
    pure
    returns (bytes32);
```

**Parameters**

| Name              | Type      | Description                                        |
| ----------------- | --------- | -------------------------------------------------- |
| `user`            | `address` | The user who made the deposit                      |
| `token`           | `IERC20`  | The token that was deposited                       |
| `tokenAmount`     | `uint256` | The amount of tokens deposited                     |
| `unitsAmount`     | `uint256` | The amount of units received                       |
| `refundableUntil` | `uint256` | The timestamp at which the deposit can be refunded |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the deposit |

### \_getRequestHashParams

Get the hash of a request from parameters

```solidity
function _getRequestHashParams(
    IERC20 token,
    address user,
    RequestType requestType,
    uint256 tokens,
    uint256 units,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge
) internal pure returns (bytes32);
```

**Parameters**

| Name          | Type          | Description                              |
| ------------- | ------------- | ---------------------------------------- |
| `token`       | `IERC20`      | The token that was deposited or redeemed |
| `user`        | `address`     | The user who made the request            |
| `requestType` | `RequestType` | The type of request                      |
| `tokens`      | `uint256`     | The amount of tokens in the request      |
| `units`       | `uint256`     | The amount of units in the request       |
| `solverTip`   | `uint256`     | The tip paid to the solver               |
| `deadline`    | `uint256`     | The deadline of the request              |
| `maxPriceAge` | `uint256`     | The maximum age of the price data        |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

### \_getRequestHash

Get the hash of a request

```solidity
function _getRequestHash(IERC20 token, Request calldata request) internal pure returns (bytes32);
```

**Parameters**

| Name      | Type      | Description                              |
| --------- | --------- | ---------------------------------------- |
| `token`   | `IERC20`  | The token that was deposited or redeemed |
| `request` | `Request` | The request to get the hash of           |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

### \_isRequestTypeDeposit

Returns true if the request type is a deposit

```solidity
function _isRequestTypeDeposit(RequestType requestType) internal pure returns (bool);
```

**Parameters**

| Name          | Type          | Description      |
| ------------- | ------------- | ---------------- |
| `requestType` | `RequestType` | The request type |

**Returns**

| Name     | Type   | Description                      |
| -------- | ------ | -------------------------------- |
| `<none>` | `bool` | True if deposit, false otherwise |

### \_isRequestTypeAutoPrice

Returns true if the request type is fixed price

```solidity
function _isRequestTypeAutoPrice(RequestType requestType) internal pure returns (bool);
```

**Parameters**

| Name          | Type          | Description      |
| ------------- | ------------- | ---------------- |
| `requestType` | `RequestType` | The request type |

**Returns**

| Name     | Type   | Description                          |
| -------- | ------ | ------------------------------------ |
| `<none>` | `bool` | True if fixed price, false otherwise |


# ProvisionerV2

**Inherits:** IProvisionerV2, Auth2Step, ReentrancyGuardTransient

Entry and exit point for {MultiDepositorVault}. Handles all deposits and redemptions Uses {IPriceAndFeeCalculator} to convert between tokens and vault units. Supports both sync and async deposits; only async redeems. Manages deposit caps, refund timeouts, and request replay protection. All assets must flow through this contract to enter or exit the vault. Sync deposits are processed instantly, but stay refundable for a period of time. Async requests can either be solved by authorized solvers, going through the vault, or directly by anyone willing to pay units (for deposits) or tokens (for redeems), pocketing the solver tip, always paid in tokens

### Constants <a href="#constants" id="constants"></a>

#### RELEVANT\_AMOUNT\_SLOT <a href="#relevant_amount_slot" id="relevant_amount_slot"></a>

ERC7201-compliant transient storage slot for the relevant amount

Equal to keccak256(abi.encode(uint256(keccak256(“aera.provisioner.relevantAmount”)) - 1)) & \~bytes32(uint256(0xff))

```solidity
bytes32 internal constant RELEVANT_AMOUNT_SLOT = 0x35763d55300b221a73cb498654a5b850852575e97ef00a6947ad36dc59da0d00
```

#### PRICE\_FEE\_CALCULATOR <a href="#price_fee_calculator" id="price_fee_calculator"></a>

The price and fee calculator contract

```solidity
IPriceAndFeeCalculatorV2 public immutable PRICE_FEE_CALCULATOR
```

#### MULTI\_DEPOSITOR\_VAULT <a href="#multi_depositor_vault" id="multi_depositor_vault"></a>

The multi depositor vault contract

```solidity
address public immutable MULTI_DEPOSITOR_VAULT
```

#### SOLVING\_GATE\_ENABLED <a href="#solving_gate_enabled" id="solving_gate_enabled"></a>

Whether this provisioner supports solving status gating

```solidity
bool public immutable SOLVING_GATE_ENABLED
```

### State Variables <a href="#state-variables" id="state-variables"></a>

#### tokensDetails <a href="#tokensdetails" id="tokensdetails"></a>

Mapping of token to token details

```solidity
mapping(IERC20 token => TokenDetailsV2 details) public tokensDetails
```

#### depositCap <a href="#depositcap" id="depositcap"></a>

Maximum total value of deposits in numeraire terms

```solidity
uint224 public depositCap
```

#### depositRefundTimeout <a href="#depositrefundtimeout" id="depositrefundtimeout"></a>

Time period in seconds during which sync deposits can be refunded

```solidity
uint32 public depositRefundTimeout
```

#### syncDepositHashes <a href="#syncdeposithashes" id="syncdeposithashes"></a>

Mapping of active sync deposit hashes

True if a sync deposit is active with the hashed parameters

```solidity
mapping(bytes32 syncDepositHash => bool exists) public syncDepositHashes
```

#### asyncRequestHashes <a href="#asyncrequesthashes" id="asyncrequesthashes"></a>

Mapping of async request hash to its existence (deposits and redeems share one domain)

True if request exists, false if it was refunded or solved Collision between deposit and redeem hashes is impossible because the hash includes RequestType

```solidity
mapping(bytes32 asyncRequestHash => bool exists) public asyncRequestHashes
```

#### userUnitsRefundableUntil <a href="#userunitsrefundableuntil" id="userunitsrefundableuntil"></a>

Mapping of user address to timestamp until which their units are locked

```solidity
mapping(address user => uint256 unitsLockedUntil) public userUnitsRefundableUntil
```

#### \_syncRedeemMaxPriceAge <a href="#syncredeemmaxpriceage" id="syncredeemmaxpriceage"></a>

Maximum allowed vault price age for sync redeems (seconds)

```solidity
uint24 internal _syncRedeemMaxPriceAge
```

#### \_syncRedeemRelativeCapBps <a href="#syncredeemrelativecapbps" id="syncredeemrelativecapbps"></a>

Relative cap in bps of epoch-start vault value for sync redeems

```solidity
uint16 internal _syncRedeemRelativeCapBps
```

#### \_syncRedeemMaxDynamicPremiumBps <a href="#syncredeemmaxdynamicpremiumbps" id="syncredeemmaxdynamicpremiumbps"></a>

Maximum global dynamic premium in bps for sync redeems

```solidity
uint16 internal _syncRedeemMaxDynamicPremiumBps
```

#### \_syncRedeemEpochTimestamp <a href="#syncredeemepochtimestamp" id="syncredeemepochtimestamp"></a>

Timestamp of the current sync redeem epoch (from PFC vault state)

```solidity
uint32 internal _syncRedeemEpochTimestamp
```

#### \_syncRedeemAbsoluteCapNumeraire <a href="#syncredeemabsolutecapnumeraire" id="syncredeemabsolutecapnumeraire"></a>

Absolute cap in numeraire per epoch for sync redeems

```solidity
uint80 internal _syncRedeemAbsoluteCapNumeraire
```

#### \_syncRedeemEpochRedeemedNumeraire <a href="#syncredeemepochredeemednumeraire" id="syncredeemepochredeemednumeraire"></a>

Numeraire amount redeemed globally so far in the current sync redeem epoch

```solidity
uint80 internal _syncRedeemEpochRedeemedNumeraire
```

#### \_depositCancellationsEnabled <a href="#depositcancellationsenabled" id="depositcancellationsenabled"></a>

Whether user-initiated deposit request cancellations are enabled

```solidity
bool internal _depositCancellationsEnabled
```

#### \_redeemCancellationsEnabled <a href="#redeemcancellationsenabled" id="redeemcancellationsenabled"></a>

Whether user-initiated redeem request cancellations are enabled

```solidity
bool internal _redeemCancellationsEnabled
```

#### \_redeemCancellationCapNumeraire <a href="#redeemcancellationcapnumeraire" id="redeemcancellationcapnumeraire"></a>

Maximum redeem request size eligible for user self-cancellation, in numeraire

```solidity
uint80 internal _redeemCancellationCapNumeraire
```

#### \_depositCancellationFeeNumeraire <a href="#depositcancellationfeenumeraire" id="depositcancellationfeenumeraire"></a>

Fixed deposit cancellation fee for self-cancels before deadline, in numeraire

```solidity
uint80 internal _depositCancellationFeeNumeraire
```

#### \_redeemCancellationFeeNumeraire <a href="#redeemcancellationfeenumeraire" id="redeemcancellationfeenumeraire"></a>

Fixed redeem cancellation fee for self-cancels before deadline, in numeraire

```solidity
uint80 internal _redeemCancellationFeeNumeraire
```

#### \_redeemCancellationDynamicFeeCapNumeraire <a href="#redeemcancellationdynamicfeecapnumeraire" id="redeemcancellationdynamicfeecapnumeraire"></a>

Dynamic redeem cancellation fee cap for self-cancels before deadline, in numeraire

```solidity
uint80 internal _redeemCancellationDynamicFeeCapNumeraire
```

#### depositReceiverApprovals <a href="#depositreceiverapprovals" id="depositreceiverapprovals"></a>

Whether a receiver has approved a depositor to deposit on their behalf

```solidity
mapping(address receiver => mapping(address depositor => bool approved)) public depositReceiverApprovals
```

#### solvingGate <a href="#solvinggate" id="solvinggate"></a>

Returns the current solving gate address

```solidity
address public solvingGate
```

### Functions <a href="#functions" id="functions"></a>

#### anyoneButVault <a href="#anyonebutvault" id="anyonebutvault"></a>

Ensures the caller is not the vault

```solidity
modifier anyoneButVault() ;
```

#### solvingNotPaused <a href="#solvingnotpaused" id="solvingnotpaused"></a>

Reverts if the solving gate is set and reports that solving is paused

```solidity
modifier solvingNotPaused(IERC20 token) ;
```

**Parameters**

| Name    | Type     | Description                  |
| ------- | -------- | ---------------------------- |
| `token` | `IERC20` | The ERC20 token being solved |

#### constructor <a href="#constructor" id="constructor"></a>

```solidity
constructor(
    IPriceAndFeeCalculatorV2 priceAndFeeCalculator,
    address multiDepositorVault,
    bool solvingGateEnabled,
    address owner_,
    Authority authority_
) Auth2Step(owner_, authority_);
```

#### deposit <a href="#deposit" id="deposit"></a>

Deposit tokens directly into the vault

Caller must be the receiver or approved by the receiver via {setDepositReceiverApproval}

```solidity
function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut, address receiver)
    external
    nonReentrant
    anyoneButVault
    solvingNotPaused(token)
    returns (uint256 unitsOut);
```

**Parameters**

| Name          | Type      | Description                          |
| ------------- | --------- | ------------------------------------ |
| `token`       | `IERC20`  | The token to deposit                 |
| `tokensIn`    | `uint256` | The amount of tokens to deposit      |
| `minUnitsOut` | `uint256` | The minimum amount of units expected |
| `receiver`    | `address` | The address that receives units      |

**Returns**

| Name       | Type      | Description                                 |
| ---------- | --------- | ------------------------------------------- |
| `unitsOut` | `uint256` | The amount of shares minted to the receiver |

#### mint <a href="#mint" id="mint"></a>

Mint exact amount of units by depositing required tokens

Caller must be the receiver or approved by the receiver via {setDepositReceiverApproval}

```solidity
function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn, address receiver)
    external
    nonReentrant
    anyoneButVault
    solvingNotPaused(token)
    returns (uint256 tokensIn);
```

**Parameters**

| Name          | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `token`       | `IERC20`  | The token to deposit                        |
| `unitsOut`    | `uint256` | The exact amount of units to mint           |
| `maxTokensIn` | `uint256` | Maximum amount of tokens willing to deposit |
| `receiver`    | `address` | The address that receives units             |

**Returns**

| Name       | Type      | Description                                            |
| ---------- | --------- | ------------------------------------------------------ |
| `tokensIn` | `uint256` | The amount of tokens used to mint the requested shares |

#### refundDeposit <a href="#refunddeposit" id="refunddeposit"></a>

Refund a deposit within the refund period

```solidity
function refundDeposit(
    address sender,
    address receiver,
    IERC20 token,
    uint256 tokenAmount,
    uint256 unitsAmount,
    uint256 refundableUntil
) external requiresAuth;
```

**Parameters**

| Name              | Type      | Description                              |
| ----------------- | --------- | ---------------------------------------- |
| `sender`          | `address` | The original depositor                   |
| `receiver`        | `address` | The address whose units are reclaimed    |
| `token`           | `IERC20`  | The deposited token                      |
| `tokenAmount`     | `uint256` | The amount of tokens deposited           |
| `unitsAmount`     | `uint256` | The amount of units minted               |
| `refundableUntil` | `uint256` | Timestamp until which refund is possible |

#### requestDeposit <a href="#requestdeposit" id="requestdeposit"></a>

Create a new deposit request to be solved by solvers

```solidity
function requestDeposit(
    IERC20 token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external returns (bytes32 depositHash);
```

**Parameters**

| Name           | Type      | Description                                   |
| -------------- | --------- | --------------------------------------------- |
| `token`        | `IERC20`  | The token to deposit                          |
| `tokensIn`     | `uint256` | The amount of tokens to deposit               |
| `minUnitsOut`  | `uint256` | The minimum amount of units expected          |
| `solverTip`    | `uint256` | The tip offered to the solver                 |
| `deadline`     | `uint256` | Timestamp until which the request is valid    |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request  |

**Returns**

| Name          | Type      | Description                                          |
| ------------- | --------- | ---------------------------------------------------- |
| `depositHash` | `bytes32` | requestHash The hash identifying the created request |

#### requestRedeem <a href="#requestredeem" id="requestredeem"></a>

Create a new redeem request to be solved by solvers

```solidity
function requestRedeem(
    IERC20 token,
    uint256 unitsIn,
    uint256 minTokensOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external returns (bytes32 redeemHash);
```

**Parameters**

| Name           | Type      | Description                                   |
| -------------- | --------- | --------------------------------------------- |
| `token`        | `IERC20`  | The token to receive                          |
| `unitsIn`      | `uint256` | The amount of units to redeem                 |
| `minTokensOut` | `uint256` | The minimum amount of tokens expected         |
| `solverTip`    | `uint256` | The tip offered to the solver                 |
| `deadline`     | `uint256` | Timestamp until which the request is valid    |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request  |

**Returns**

| Name         | Type      | Description                                          |
| ------------ | --------- | ---------------------------------------------------- |
| `redeemHash` | `bytes32` | requestHash The hash identifying the created request |

#### refundRequest <a href="#refundrequest" id="refundrequest"></a>

Refund an expired deposit or redeem request

```solidity
function refundRequest(IERC20 token, RequestV2 calldata request) external nonReentrant;
```

**Parameters**

| Name      | Type        | Description                       |
| --------- | ----------- | --------------------------------- |
| `token`   | `IERC20`    | The token involved in the request |
| `request` | `RequestV2` | The request to refund             |

#### cancelRequest <a href="#cancelrequest" id="cancelrequest"></a>

Cancel an async deposit or redeem request

```solidity
function cancelRequest(IERC20 token, RequestV2 calldata request) external nonReentrant;
```

**Parameters**

| Name      | Type        | Description                       |
| --------- | ----------- | --------------------------------- |
| `token`   | `IERC20`    | The token involved in the request |
| `request` | `RequestV2` | The request to cancel             |

#### solveRequestsVault <a href="#solverequestsvault" id="solverequestsvault"></a>

Solve multiple requests using vault’s liquidity, with optional pre/post-solve guardian submissions for automatic fund movement

MUST revert if preSolveSubmitData is non-empty and vault.submit reverts

```solidity
function solveRequestsVault(
    IERC20 token,
    RequestV2[] calldata requests,
    bytes calldata preSolveSubmitData,
    bytes calldata postSolveSubmitData
) external requiresAuth nonReentrant solvingNotPaused(token);
```

**Parameters**

| Name                  | Type          | Description                                                                                                                                                               |
| --------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`               | `IERC20`      | The token for which to solve requests                                                                                                                                     |
| `requests`            | `RequestV2[]` | Array of requests to solve                                                                                                                                                |
| `preSolveSubmitData`  | `bytes`       | Encoded operations to submit before solving (e.g., pull funds from yield source). If non-empty, calls vault.submit and reverts if it fails If empty, skipped              |
| `postSolveSubmitData` | `bytes`       | Encoded operations to submit after solving (e.g., push funds to yield source). If non-empty, calls vault.submit via try/catch - failures are swallowed. If empty, skipped |

#### solveRequestsDirect <a href="#solverequestsdirect" id="solverequestsdirect"></a>

Solve multiple requests using solver’s own liquidity

Does not check the solving gate because direct solves are peer-to-peer and never touch the vault’s enter/exit flow, so they have no impact on underlying fund accounting The PFC vault-pause check still applies as the full-freeze mechanism

```solidity
function solveRequestsDirect(IERC20 token, RequestV2[] calldata requests) external nonReentrant;
```

**Parameters**

| Name       | Type          | Description                           |
| ---------- | ------------- | ------------------------------------- |
| `token`    | `IERC20`      | The token for which to solve requests |
| `requests` | `RequestV2[]` | Array of requests to solve            |

#### setDepositDetails <a href="#setdepositdetails" id="setdepositdetails"></a>

Update deposit parameters

```solidity
function setDepositDetails(uint224 depositCap_, uint32 depositRefundTimeout_) external requiresAuth;
```

**Parameters**

| Name                    | Type      | Description                                   |
| ----------------------- | --------- | --------------------------------------------- |
| `depositCap_`           | `uint224` | New maximum total value that can be deposited |
| `depositRefundTimeout_` | `uint32`  | New time window for deposit refunds           |

#### setCancellationDetails <a href="#setcancellationdetails" id="setcancellationdetails"></a>

Sets cancellation toggles and enabled-side fee configuration

If a deposit/redeem cancellation is disabled, params related to it must be 0

```solidity
function setCancellationDetails(
    bool depositCancellationsEnabled,
    bool redeemCancellationsEnabled,
    uint80 depositCancellationFeeNumeraire,
    uint80 redeemCancellationFeeNumeraire,
    uint80 redeemCancellationDynamicFeeCapNumeraire,
    uint80 redeemCancellationCapNumeraire
) external requiresAuth;
```

**Parameters**

| Name                                       | Type     | Description                                                     |
| ------------------------------------------ | -------- | --------------------------------------------------------------- |
| `depositCancellationsEnabled`              | `bool`   | Whether user-initiated deposit requests can be cancelled        |
| `redeemCancellationsEnabled`               | `bool`   | Whether user-initiated redeem requests can be cancelled         |
| `depositCancellationFeeNumeraire`          | `uint80` | Fixed deposit cancellation fee, in numeraire                    |
| `redeemCancellationFeeNumeraire`           | `uint80` | Fixed redeem cancellation fee, in numeraire                     |
| `redeemCancellationDynamicFeeCapNumeraire` | `uint80` | Dynamic redeem cancellation fee cap, in numeraire               |
| `redeemCancellationCapNumeraire`           | `uint80` | Maximum redeem request size users can self-cancel, in numeraire |

#### setTokenDetails <a href="#settokendetails" id="settokendetails"></a>

Update token parameters including push/pull funds SSTORE2 pointers

Admin must create SSTORE2 pointers externally before calling this function

```solidity
function setTokenDetails(IERC20 token, TokenDetailsV2 calldata details) external requiresAuth;
```

**Parameters**

| Name      | Type             | Description                                                                                                                                            |
| --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`   | `IERC20`         | The token to update                                                                                                                                    |
| `details` | `TokenDetailsV2` | The full token details struct. `pushFundsSubmitDataPointer` and `pullFundsSubmitDataPointer` must be valid SSTORE2 pointers or `address(0)` to disable |

#### removeToken <a href="#removetoken" id="removetoken"></a>

Removes token from provisioner

```solidity
function removeToken(IERC20 token) external requiresAuth;
```

**Parameters**

| Name    | Type     | Description             |
| ------- | -------- | ----------------------- |
| `token` | `IERC20` | The token to be removed |

#### setSyncRedeemDetails <a href="#setsyncredeemdetails" id="setsyncredeemdetails"></a>

Sets global sync redeem risk parameters

Only callable by authorized addresses. All parameters must be non-zero

```solidity
function setSyncRedeemDetails(
    uint24 maxPriceAge,
    uint16 relativeCapBps,
    uint80 absoluteCapNumeraire,
    uint16 maxDynamicPremiumBps
) external requiresAuth;
```

**Parameters**

| Name                   | Type     | Description                                        |
| ---------------------- | -------- | -------------------------------------------------- |
| `maxPriceAge`          | `uint24` | Maximum allowed vault price age (seconds)          |
| `relativeCapBps`       | `uint16` | Relative cap in bps of epoch-start TVL (numeraire) |
| `absoluteCapNumeraire` | `uint80` | Absolute cap in numeraire per epoch                |
| `maxDynamicPremiumBps` | `uint16` | Maximum global dynamic premium in bps              |

#### setDepositReceiverApproval <a href="#setdepositreceiverapproval" id="setdepositreceiverapproval"></a>

Approve or revoke a depositor’s permission to deposit on behalf of the caller

```solidity
function setDepositReceiverApproval(address depositor, bool approved) external;
```

**Parameters**

| Name        | Type      | Description                                                |
| ----------- | --------- | ---------------------------------------------------------- |
| `depositor` | `address` | The address to approve or revoke                           |
| `approved`  | `bool`    | Whether the depositor is approved to deposit to the caller |

#### setSolvingGate <a href="#setsolvinggate" id="setsolvinggate"></a>

Sets the solving gate contract that controls when solving is allowed

MUST only be callable by authorized addresses

```solidity
function setSolvingGate(address solvingGate_) external requiresAuth;
```

**Parameters**

| Name           | Type      | Description                                                                          |
| -------------- | --------- | ------------------------------------------------------------------------------------ |
| `solvingGate_` | `address` | The new solving gate address. Use address(0) to disable gating (solving always open) |

#### redeem <a href="#redeem" id="redeem"></a>

Synchronously redeem units for tokens using latest active price

Sync redeem must be enabled for token, vault must be active, and price must be fresh

```solidity
function redeem(IERC20 token, uint256 unitsIn, uint256 minTokensOut, address receiver)
    external
    anyoneButVault
    nonReentrant
    solvingNotPaused(token)
    returns (uint256 tokensOut);
```

**Parameters**

| Name           | Type      | Description                     |
| -------------- | --------- | ------------------------------- |
| `token`        | `IERC20`  | Token to receive                |
| `unitsIn`      | `uint256` | Units to redeem                 |
| `minTokensOut` | `uint256` | Minimum acceptable token output |
| `receiver`     | `address` | Address receiving output tokens |

**Returns**

| Name        | Type      | Description                          |
| ----------- | --------- | ------------------------------------ |
| `tokensOut` | `uint256` | Actual token amount sent to receiver |

#### withdraw <a href="#withdraw" id="withdraw"></a>

Synchronously withdraw exact tokens using latest active price

Sync redeem must be enabled for token, vault must be active, and price must be fresh

```solidity
function withdraw(IERC20 token, uint256 tokensOut, uint256 maxUnitsIn, address receiver)
    external
    anyoneButVault
    nonReentrant
    solvingNotPaused(token)
    returns (uint256 unitsIn);
```

**Parameters**

| Name         | Type      | Description                      |
| ------------ | --------- | -------------------------------- |
| `token`      | `IERC20`  | Token to receive                 |
| `tokensOut`  | `uint256` | Exact token output requested     |
| `maxUnitsIn` | `uint256` | Maximum acceptable units to burn |
| `receiver`   | `address` | Address receiving output tokens  |

**Returns**

| Name      | Type      | Description         |
| --------- | --------- | ------------------- |
| `unitsIn` | `uint256` | Actual units burned |

#### getSyncRedeemEpochState <a href="#getsyncredeemepochstate" id="getsyncredeemepochstate"></a>

Returns current global sync redeem epoch state

MUST NOT revert; returns view-consistent epoch state (if PFC is paused, returns zero TVL and zero cap for the current epoch) (if PFC timestamp differs from stored, shows fresh epoch with 0 redeemed)

```solidity
function getSyncRedeemEpochState()
    external
    view
    returns (
        uint256 epochTimestamp,
        uint256 epochStartTvlNumeraire,
        uint256 epochRedeemedNumeraire,
        uint256 epochCapNumeraire
    );
```

**Returns**

| Name                     | Type      | Description                                                |
| ------------------------ | --------- | ---------------------------------------------------------- |
| `epochTimestamp`         | `uint256` | Current anchor epoch timestamp from PFC                    |
| `epochStartTvlNumeraire` | `uint256` | Epoch-start TVL in numeraire                               |
| `epochRedeemedNumeraire` | `uint256` | Numeraire amount redeemed globally so far in current epoch |
| `epochCapNumeraire`      | `uint256` | Effective current epoch cap in numeraire                   |

#### getSyncRedeemDetails <a href="#getsyncredeemdetails" id="getsyncredeemdetails"></a>

Returns all sync redeem configuration and epoch state

```solidity
function getSyncRedeemDetails() external view returns (uint24, uint16, uint16, uint32, uint80, uint80);
```

**Returns**

| Name     | Type     | Description                                                                                       |
| -------- | -------- | ------------------------------------------------------------------------------------------------- |
| `<none>` | `uint24` | maxPriceAge Maximum allowed vault price age for sync redeems (seconds)                            |
| `<none>` | `uint16` | relativeCapBps Relative cap in bps of epoch-start TVL for sync redeems                            |
| `<none>` | `uint16` | maxDynamicPremiumBps Maximum global dynamic premium in bps for sync redeems                       |
| `<none>` | `uint32` | epochTimestamp Timestamp of the current sync redeem epoch (from PFC vault state)                  |
| `<none>` | `uint80` | absoluteCapNumeraire Absolute cap in numeraire per epoch for sync redeems                         |
| `<none>` | `uint80` | epochRedeemedNumeraire Numeraire amount redeemed globally so far in the current sync redeem epoch |

#### getRelevantAmount <a href="#getrelevantamount" id="getrelevantamount"></a>

Read an amount relevant for current submit operation from transient storage

```solidity
function getRelevantAmount() external view returns (uint256);
```

**Returns**

| Name     | Type      | Description       |
| -------- | --------- | ----------------- |
| `<none>` | `uint256` | The stored amount |

#### maxDeposit <a href="#maxdeposit" id="maxdeposit"></a>

Return maximum amount that can still be deposited

```solidity
function maxDeposit() external view returns (uint256);
```

**Returns**

| Name     | Type      | Description                          |
| -------- | --------- | ------------------------------------ |
| `<none>` | `uint256` | Amount of deposit capacity remaining |

#### previewCancellationFeeNumeraire <a href="#previewcancellationfeenumeraire" id="previewcancellationfeenumeraire"></a>

Preview the cancellation fee for a request in numeraire terms

No guard checks (enable flags, cap, deadline) — those revert in cancelRequest

```solidity
function previewCancellationFeeNumeraire(RequestV2 calldata request) external view returns (uint256);
```

**Parameters**

| Name      | Type        | Description                                     |
| --------- | ----------- | ----------------------------------------------- |
| `request` | `RequestV2` | The request to preview the cancellation fee for |

**Returns**

| Name     | Type      | Description                       |
| -------- | --------- | --------------------------------- |
| `<none>` | `uint256` | The cancellation fee in numeraire |

#### getCancellationDetails <a href="#getcancellationdetails" id="getcancellationdetails"></a>

Returns cancellation toggles and fee configuration

```solidity
function getCancellationDetails() external view returns (bool, bool, uint80, uint80, uint80, uint80);
```

**Returns**

| Name     | Type     | Description                                                                                    |
| -------- | -------- | ---------------------------------------------------------------------------------------------- |
| `<none>` | `bool`   | depositCancellationsEnabled Whether user-initiated deposit request cancellations are enabled   |
| `<none>` | `bool`   | redeemCancellationsEnabled Whether user-initiated redeem request cancellations are enabled     |
| `<none>` | `uint80` | depositCancellationFeeNumeraire Fixed deposit cancellation fee, in numeraire                   |
| `<none>` | `uint80` | redeemCancellationFeeNumeraire Fixed redeem cancellation fee, in numeraire                     |
| `<none>` | `uint80` | redeemCancellationDynamicFeeCapNumeraire Dynamic redeem cancellation fee cap, in numeraire     |
| `<none>` | `uint80` | redeemCancellationCapNumeraire Maximum redeem request size users can self-cancel, in numeraire |

#### areUserUnitsLocked <a href="#areuserunitslocked" id="areuserunitslocked"></a>

Check if a user’s units are currently locked

```solidity
function areUserUnitsLocked(address user) external view returns (bool);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `user` | `address` | The address to check |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if user’s units are locked, false otherwise |

#### getDepositHash <a href="#getdeposithash" id="getdeposithash"></a>

Computes the hash for a sync deposit

```solidity
function getDepositHash(
    address user,
    address receiver,
    IERC20 token,
    uint256 tokenAmount,
    uint256 unitsAmount,
    uint256 refundableUntil
) external pure returns (bytes32);
```

**Parameters**

| Name              | Type      | Description                                         |
| ----------------- | --------- | --------------------------------------------------- |
| `user`            | `address` | The address making the deposit                      |
| `receiver`        | `address` | The address receiving units from the deposit        |
| `token`           | `IERC20`  | The token being deposited                           |
| `tokenAmount`     | `uint256` | The amount of tokens to deposit                     |
| `unitsAmount`     | `uint256` | Minimum amount of units to receive                  |
| `refundableUntil` | `uint256` | The timestamp until which the deposit is refundable |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the deposit |

#### getRequestHash <a href="#getrequesthash" id="getrequesthash"></a>

Computes the hash for a request

```solidity
function getRequestHash(IERC20 token, RequestV2 calldata request) external pure returns (bytes32);
```

**Parameters**

| Name      | Type        | Description              |
| --------- | ----------- | ------------------------ |
| `token`   | `IERC20`    | The token in the request |
| `request` | `RequestV2` | The request to hash      |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

#### version <a href="#version" id="version"></a>

Returns the semantic version string for this contract surface

```solidity
function version() external pure returns (string memory);
```

**Returns**

| Name     | Type     | Description                 |
| -------- | -------- | --------------------------- |
| `<none>` | `string` | The semantic version string |

#### requestDeposit <a href="#requestdeposit-1" id="requestdeposit-1"></a>

Create a new deposit request to be solved by solvers

```solidity
function requestDeposit(
    IERC20 token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    address receiver
) public anyoneButVault returns (bytes32 depositHash);
```

**Parameters**

| Name           | Type      | Description                                   |
| -------------- | --------- | --------------------------------------------- |
| `token`        | `IERC20`  | The token to deposit                          |
| `tokensIn`     | `uint256` | The amount of tokens to deposit               |
| `minUnitsOut`  | `uint256` | The minimum amount of units expected          |
| `solverTip`    | `uint256` | The tip offered to the solver                 |
| `deadline`     | `uint256` | Timestamp until which the request is valid    |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request  |
| `receiver`     | `address` | The address that receives units when solved   |

**Returns**

| Name          | Type      | Description                                        |
| ------------- | --------- | -------------------------------------------------- |
| `depositHash` | `bytes32` | depositRequestHash The hash of the deposit request |

#### requestRedeem <a href="#requestredeem-1" id="requestredeem-1"></a>

Create a new redeem request to be solved by solvers

```solidity
function requestRedeem(
    IERC20 token,
    uint256 unitsIn,
    uint256 minTokensOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    address receiver
) public anyoneButVault returns (bytes32 redeemHash);
```

**Parameters**

| Name           | Type      | Description                                   |
| -------------- | --------- | --------------------------------------------- |
| `token`        | `IERC20`  | The token to receive                          |
| `unitsIn`      | `uint256` | The amount of units to redeem                 |
| `minTokensOut` | `uint256` | The minimum amount of tokens expected         |
| `solverTip`    | `uint256` | The tip offered to the solver                 |
| `deadline`     | `uint256` | Timestamp until which the request is valid    |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request  |
| `receiver`     | `address` | The address that receives tokens when solved  |

**Returns**

| Name         | Type      | Description                                      |
| ------------ | --------- | ------------------------------------------------ |
| `redeemHash` | `bytes32` | redeemRequestHash The hash of the redeem request |

#### \_solveRequestsVault <a href="#solverequestsvault" id="solverequestsvault"></a>

Internal solve logic shared by both solveRequestsVault overloads

```solidity
function _solveRequestsVault(IERC20 token, RequestV2[] calldata requests) internal;
```

**Parameters**

| Name       | Type          | Description                           |
| ---------- | ------------- | ------------------------------------- |
| `token`    | `IERC20`      | The token for which to solve requests |
| `requests` | `RequestV2[]` | Array of requests to solve            |

#### \_pullFundsIfNeeded <a href="#pullfundsifneeded" id="pullfundsifneeded"></a>

Pull funds from yield source if vault idle balance is insufficient

```solidity
function _pullFundsIfNeeded(IERC20 token, uint256 tokensOut) internal;
```

**Parameters**

| Name        | Type      | Description                                |
| ----------- | --------- | ------------------------------------------ |
| `token`     | `IERC20`  | The redeem token                           |
| `tokensOut` | `uint256` | The amount of tokens needed for the redeem |

#### \_pushFundsIfConfigured <a href="#pushfundsifconfigured" id="pushfundsifconfigured"></a>

Push deposited funds to yield source if configured

```solidity
function _pushFundsIfConfigured(TokenDetailsV2 storage tokenDetails, uint256 tokensIn) internal;
```

**Parameters**

| Name           | Type             | Description                         |
| -------------- | ---------------- | ----------------------------------- |
| `tokenDetails` | `TokenDetailsV2` | The token details storage reference |
| `tokensIn`     | `uint256`        | The amount of tokens deposited      |

#### \_storeAmount <a href="#storeamount" id="storeamount"></a>

Store a uint256 amount in transient storage

```solidity
function _storeAmount(uint256 amount) internal;
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `amount` | `uint256` | The amount to store |

#### \_syncDeposit <a href="#syncdeposit" id="syncdeposit"></a>

Handles a synchronous deposit, records the deposit hash, and enters the vault

Reverts if the deposit hash already exists. Sets the refundable period for the user

```solidity
function _syncDeposit(IERC20 token, uint256 tokenAmount, uint256 unitAmount, address receiver) internal;
```

**Parameters**

| Name          | Type      | Description                                    |
| ------------- | --------- | ---------------------------------------------- |
| `token`       | `IERC20`  | The ERC20 token to deposit                     |
| `tokenAmount` | `uint256` | The amount of tokens to deposit                |
| `unitAmount`  | `uint256` | The amount of vault units to mint for the user |
| `receiver`    | `address` | The address receiving the minted units         |

#### \_syncRedeem <a href="#syncredeem" id="syncredeem"></a>

Executes a synchronous redeem: rolls epoch, checks epoch cap, exits vault, and emits event

```solidity
function _syncRedeem(
    IERC20 token,
    uint256 tokensOut,
    uint256 unitsIn,
    address receiver,
    uint256 epochRedeemNumeraire,
    uint256 priceTimestamp
) internal;
```

**Parameters**

| Name                   | Type      | Description                                                                        |
| ---------------------- | --------- | ---------------------------------------------------------------------------------- |
| `token`                | `IERC20`  | The ERC20 token to receive                                                         |
| `tokensOut`            | `uint256` | The amount of tokens to send to the receiver                                       |
| `unitsIn`              | `uint256` | The amount of vault units to burn from the caller                                  |
| `receiver`             | `address` | The address to receive the tokens                                                  |
| `epochRedeemNumeraire` | `uint256` | The pre-computed numeraire value of this redeem for epoch cap accounting           |
| `priceTimestamp`       | `uint256` | The PFC anchor timestamp captured in \_prepareSyncRedeem (used for epoch rollover) |

#### \_solveDepositVaultAutoPrice <a href="#solvedepositvaultautoprice" id="solvedepositvaultautoprice"></a>

Solves an async deposit request for the vault, transferring tokens or refunding as needed

* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* units out is less than min required, emits AmountBoundExceeded
* deposit cap would be exceeded, emits DepositCapExceeded
* If deadline not passed, processes deposit and emits DepositSolved
* If deadline passed, refunds and emits DepositRefunded
* Always unsets hash after processing

```solidity
function _solveDepositVaultAutoPrice(
    IERC20 token,
    uint256 depositMultiplier,
    RequestV2 calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name                | Type        | Description                                                            |
| ------------------- | ----------- | ---------------------------------------------------------------------- |
| `token`             | `IERC20`    | The ERC20 token being deposited                                        |
| `depositMultiplier` | `uint256`   | The multiplier (in BPS) applied to the deposit for premium calculation |
| `request`           | `RequestV2` | The deposit request struct containing all user parameters              |
| `priceAge`          | `uint256`   | The age of the price data used for conversion                          |
| `index`             | `uint256`   | The index of the request in the given solving batch                    |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

#### \_solveDepositVaultFixedPrice <a href="#solvedepositvaultfixedprice" id="solvedepositvaultfixedprice"></a>

Solves a fixed price deposit request for the vault, transferring tokens or refunding as needed

User gets exactly min units out, but may over‑fund, the difference is paid to the solver as a tip

* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* tokens needed exceed the maximum allowed, emits AmountBoundExceeded
* deposit cap would be exceeded, emits DepositCapExceeded
* If deadline not passed, processes deposit and emits DepositSolved
* If deadline passed, refunds and emits DepositRefunded
* Always unsets hash after processing

```solidity
function _solveDepositVaultFixedPrice(
    IERC20 token,
    uint256 depositMultiplier,
    RequestV2 calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name                | Type        | Description                                                            |
| ------------------- | ----------- | ---------------------------------------------------------------------- |
| `token`             | `IERC20`    | The ERC20 token being deposited                                        |
| `depositMultiplier` | `uint256`   | The multiplier (in BPS) applied to the deposit for premium calculation |
| `request`           | `RequestV2` | The deposit request struct containing all user parameters              |
| `priceAge`          | `uint256`   | The age of the price data used for conversion                          |
| `index`             | `uint256`   | The index of the request in the given solving batch                    |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

#### \_solveRedeemVaultAutoPrice <a href="#solveredeemvaultautoprice" id="solveredeemvaultautoprice"></a>

Solves an async redeem request for the vault, transferring tokens or refunding as needed

* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* token out after premium is less than min required, emits AmountBoundExceeded
* If deadline not passed, processes redeem and emits RedeemSolved
* If deadline passed, refunds and emits RedeemRefunded
* Always unsets hash after processing

```solidity
function _solveRedeemVaultAutoPrice(
    IERC20 token,
    uint256 redeemMultiplier,
    RequestV2 calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name               | Type        | Description                                                           |
| ------------------ | ----------- | --------------------------------------------------------------------- |
| `token`            | `IERC20`    | The ERC20 token being redeemed                                        |
| `redeemMultiplier` | `uint256`   | The multiplier (in BPS) applied to the redeem for premium calculation |
| `request`          | `RequestV2` | The redeem request struct containing all user parameters              |
| `priceAge`         | `uint256`   | The age of the price data used for conversion                         |
| `index`            | `uint256`   | The index of the request in the given solving batch                   |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

#### \_solveRedeemVaultFixedPrice <a href="#solveredeemvaultfixedprice" id="solveredeemvaultfixedprice"></a>

Solves a fixed price redeem request for the vault, transferring tokens or refunding as needed

User gets exactly min tokens out, but may under‑fund, the difference is paid to the solver as a tip

* Returns 0 if any of:
* price age is too high, emits PriceAgeExceeded
* request hash is not set, emits InvalidRequestHash
* If deadline not passed, processes redeem and emits RedeemSolved
* If deadline passed, refunds and emits RedeemRefunded
* Always unsets hash after processing

```solidity
function _solveRedeemVaultFixedPrice(
    IERC20 token,
    uint256 redeemMultiplier,
    RequestV2 calldata request,
    uint256 priceAge,
    uint256 index
) internal returns (uint256 solverTip);
```

**Parameters**

| Name               | Type        | Description                                                           |
| ------------------ | ----------- | --------------------------------------------------------------------- |
| `token`            | `IERC20`    | The ERC20 token being redeemed                                        |
| `redeemMultiplier` | `uint256`   | The multiplier (in BPS) applied to the redeem for premium calculation |
| `request`          | `RequestV2` | The redeem request struct containing all user parameters              |
| `priceAge`         | `uint256`   | The age of the price data used for conversion                         |
| `index`            | `uint256`   | The index of the request in the given solving batch                   |

**Returns**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `solverTip` | `uint256` | The tip amount paid to the solver, or 0 if not processed |

#### \_solveRequestDirect <a href="#solverequestdirect" id="solverequestdirect"></a>

Solves a direct request (deposit or redeem), transferring tokens and units between users

* Returns early if request hash is not set, emits InvalidRequestHash
* If deadline not passed, transfers units and tokens, emits DepositSolved/RedeemSolved
* If deadline passed, refunds escrowed asset, emits DepositRefunded/RedeemRefunded
* Always unsets hash after processing

```solidity
function _solveRequestDirect(IERC20 token, RequestV2 calldata request) internal;
```

**Parameters**

| Name      | Type        | Description                                       |
| --------- | ----------- | ------------------------------------------------- |
| `token`   | `IERC20`    | The ERC20 token involved in the request           |
| `request` | `RequestV2` | The request struct containing all user parameters |

#### \_clearHashAndTransferRefund <a href="#clearhashandtransferrefund" id="clearhashandtransferrefund"></a>

Clears request hash, emits refund event, and transfers amount with requester fallback

```solidity
function _clearHashAndTransferRefund(
    IERC20 token,
    RequestV2 calldata request,
    address requester,
    uint256 amount,
    IERC20 transferToken
) internal;
```

**Parameters**

| Name            | Type        | Description                                                                 |
| --------------- | ----------- | --------------------------------------------------------------------------- |
| `token`         | `IERC20`    | The token used to derive the request hash                                   |
| `request`       | `RequestV2` | The request to clear                                                        |
| `requester`     | `address`   | The fallback recipient when transfer to request.receiver fails              |
| `amount`        | `uint256`   | The amount to transfer to the receiver (or requester on fallback)           |
| `transferToken` | `IERC20`    | The token to transfer (deposit token for deposits, vault units for redeems) |

#### \_transferWithFallback <a href="#transferwithfallback" id="transferwithfallback"></a>

Transfers amount to receiver and falls back to requester if receiver transfer fails

```solidity
function _transferWithFallback(IERC20 token, address requester, address receiver, uint256 amount) internal;
```

**Parameters**

| Name        | Type      | Description                                              |
| ----------- | --------- | -------------------------------------------------------- |
| `token`     | `IERC20`  | The ERC20 token being transferred                        |
| `requester` | `address` | The original requester address used as fallback receiver |
| `receiver`  | `address` | The intended receiver from the request                   |
| `amount`    | `uint256` | The amount of tokens to transfer                         |

#### \_guardPriceAge <a href="#guardpriceage" id="guardpriceage"></a>

Checks if the price age exceeds the maximum allowed and emits an event if so

```solidity
function _guardPriceAge(uint256 priceAge, uint256 maxPriceAge, uint256 index) internal returns (bool);
```

**Parameters**

| Name          | Type      | Description                                                          |
| ------------- | --------- | -------------------------------------------------------------------- |
| `priceAge`    | `uint256` | The difference between when price was measured and submitted onchain |
| `maxPriceAge` | `uint256` | The maximum allowed price age                                        |
| `index`       | `uint256` | The index of the request in the given solving batch                  |

**Returns**

| Name     | Type   | Description                                    |
| -------- | ------ | ---------------------------------------------- |
| `<none>` | `bool` | True if price age is too high, false otherwise |

#### \_guardInvalidRequestHash <a href="#guardinvalidrequesthash" id="guardinvalidrequesthash"></a>

Checks if the request hash exists and emits an event if not

```solidity
function _guardInvalidRequestHash(bytes32 requestHash) internal returns (bool);
```

**Parameters**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `requestHash` | `bytes32` | The request hash |

**Returns**

| Name     | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `<none>` | `bool` | True if hash does not exist, false otherwise |

#### \_guardInsufficientTokensForTip <a href="#guardinsufficienttokensfortip" id="guardinsufficienttokensfortip"></a>

Checks if there are enough tokens for the solver tip and emits an event if not

```solidity
function _guardInsufficientTokensForTip(uint256 tokens, uint256 solverTip, uint256 index) internal returns (bool);
```

**Parameters**

| Name        | Type      | Description                                         |
| ----------- | --------- | --------------------------------------------------- |
| `tokens`    | `uint256` | The number of tokens                                |
| `solverTip` | `uint256` | The solver tip amount                               |
| `index`     | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                        |
| -------- | ------ | -------------------------------------------------- |
| `<none>` | `bool` | True if not enough tokens for tip, false otherwise |

#### \_guardAmountBound <a href="#guardamountbound" id="guardamountbound"></a>

Checks if the amount is less than the bound and emits an event if so

```solidity
function _guardAmountBound(uint256 amount, uint256 bound, uint256 index) internal returns (bool);
```

**Parameters**

| Name     | Type      | Description                                         |
| -------- | --------- | --------------------------------------------------- |
| `amount` | `uint256` | The actual amount                                   |
| `bound`  | `uint256` | The minimum required amount                         |
| `index`  | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                        |
| -------- | ------ | -------------------------------------------------- |
| `<none>` | `bool` | True if amount is less than bound, false otherwise |

#### \_guardDepositCapExceeded <a href="#guarddepositcapexceeded" id="guarddepositcapexceeded"></a>

Checks if the deposit cap would be exceeded and emits an event if so

```solidity
function _guardDepositCapExceeded(uint256 totalUnits, uint256 index) internal returns (bool);
```

**Parameters**

| Name         | Type      | Description                                         |
| ------------ | --------- | --------------------------------------------------- |
| `totalUnits` | `uint256` | The total units after deposit                       |
| `index`      | `uint256` | The index of the request in the given solving batch |

**Returns**

| Name     | Type   | Description                                            |
| -------- | ------ | ------------------------------------------------------ |
| `<none>` | `bool` | True if deposit cap would be exceeded, false otherwise |

#### \_rollEpochIfNeeded <a href="#rollepochifneeded" id="rollepochifneeded"></a>

Rolls the sync redeem epoch if the PFC timestamp has changed

```solidity
function _rollEpochIfNeeded(uint256 pfcTimestamp) internal returns (uint256 epochRedeemedNumeraire_);
```

**Parameters**

| Name           | Type      | Description                           |
| -------------- | --------- | ------------------------------------- |
| `pfcTimestamp` | `uint256` | The current PFC vault state timestamp |

**Returns**

| Name                      | Type      | Description                                                                   |
| ------------------------- | --------- | ----------------------------------------------------------------------------- |
| `epochRedeemedNumeraire_` | `uint256` | The epoch redeemed numeraire after potential roll (avoids subsequent SSLOADs) |

#### \_requireEpochCapNotExceeded <a href="#requireepochcapnotexceeded" id="requireepochcapnotexceeded"></a>

Checks that the sync redeem epoch cap is not exceeded and tracks the redemption

```solidity
function _requireEpochCapNotExceeded(
    uint256 epochRedeemNumeraire,
    uint256 epochCapNumeraire,
    uint256 epochRedeemedNumeraire
) internal;
```

**Parameters**

| Name                     | Type      | Description                                                                           |
| ------------------------ | --------- | ------------------------------------------------------------------------------------- |
| `epochRedeemNumeraire`   | `uint256` | The pre-computed numeraire value of this redeem for epoch cap accounting              |
| `epochCapNumeraire`      | `uint256` | The effective epoch cap in numeraire                                                  |
| `epochRedeemedNumeraire` | `uint256` | The numeraire already redeemed in the current epoch (cached from \_rollEpochIfNeeded) |

#### \_requireValidReceiver <a href="#requirevalidreceiver" id="requirevalidreceiver"></a>

Reverts if the caller is not the receiver and the receiver has not approved the caller

```solidity
function _requireValidReceiver(address receiver) internal view;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `receiver` | `address` | The address to validate |

#### \_checkSolvingNotPaused <a href="#checksolvingnotpaused" id="checksolvingnotpaused"></a>

Reverts if the solving gate is set and reports that solving is paused

```solidity
function _checkSolvingNotPaused(IERC20 token) internal view;
```

**Parameters**

| Name    | Type     | Description                  |
| ------- | -------- | ---------------------------- |
| `token` | `IERC20` | The ERC20 token being solved |

#### \_checkCallerNotVault <a href="#checkcallernotvault" id="checkcallernotvault"></a>

Reverts if the caller is the vault

```solidity
function _checkCallerNotVault() internal view;
```

#### \_validateRequest <a href="#validaterequest" id="validaterequest"></a>

Validates common request parameters shared by requestDeposit and requestRedeem

```solidity
function _validateRequest(address receiver, uint256 solverTip, uint256 deadline, bool isFixedPrice) internal view;
```

**Parameters**

| Name           | Type      | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| `receiver`     | `address` | The address receiving funds when request is solved |
| `solverTip`    | `uint256` | The tip offered to the solver                      |
| `deadline`     | `uint256` | Timestamp until which the request is valid         |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request       |

#### \_computeDepositCancellationFeeTokens <a href="#computedepositcancellationfeetokens" id="computedepositcancellationfeetokens"></a>

Computes the pre-deadline deposit cancellation fee in request token terms

Oracle quoting floors and does not support rounding control, so a non-zero numeraire fee can round to zero request tokens for low-value cancellation fees

```solidity
function _computeDepositCancellationFeeTokens(IERC20 token) internal view returns (uint256 feeTokens);
```

**Parameters**

| Name    | Type     | Description   |
| ------- | -------- | ------------- |
| `token` | `IERC20` | Request token |

**Returns**

| Name        | Type      | Description                       |
| ----------- | --------- | --------------------------------- |
| `feeTokens` | `uint256` | Fee amount in request token terms |

#### \_computeRedeemCancellationFeeNumeraire <a href="#computeredeemcancellationfeenumeraire" id="computeredeemcancellationfeenumeraire"></a>

Computes the pre-deadline redeem cancellation fee in numeraire

```solidity
function _computeRedeemCancellationFeeNumeraire(uint256 requestNumeraire)
    internal
    view
    returns (uint256 feeNumeraire);
```

**Parameters**

| Name               | Type      | Description                      |
| ------------------ | --------- | -------------------------------- |
| `requestNumeraire` | `uint256` | Redeem request size in numeraire |

**Returns**

| Name           | Type      | Description             |
| -------------- | --------- | ----------------------- |
| `feeNumeraire` | `uint256` | Fee amount in numeraire |

#### \_computeRedeemCancellationFeeUnits <a href="#computeredeemcancellationfeeunits" id="computeredeemcancellationfeeunits"></a>

Computes the pre-deadline redeem cancellation fee in escrowed vault units

```solidity
function _computeRedeemCancellationFeeUnits(uint256 requestNumeraire) internal view returns (uint256 feeUnits);
```

**Parameters**

| Name               | Type      | Description                      |
| ------------------ | --------- | -------------------------------- |
| `requestNumeraire` | `uint256` | Redeem request size in numeraire |

**Returns**

| Name       | Type      | Description         |
| ---------- | --------- | ------------------- |
| `feeUnits` | `uint256` | Fee amount in units |

#### \_requireSyncDepositsEnabled <a href="#requiresyncdepositsenabled" id="requiresyncdepositsenabled"></a>

Reverts if sync deposits are not enabled for the token

```solidity
function _requireSyncDepositsEnabled(IERC20 token) internal view returns (TokenDetailsV2 storage tokenDetails);
```

**Parameters**

| Name    | Type     | Description              |
| ------- | -------- | ------------------------ |
| `token` | `IERC20` | The ERC20 token to check |

**Returns**

| Name           | Type             | Description                         |
| -------------- | ---------------- | ----------------------------------- |
| `tokenDetails` | `TokenDetailsV2` | The token details storage reference |

#### \_requireSyncRedeemsEnabled <a href="#requiresyncredeemsenabled" id="requiresyncredeemsenabled"></a>

Reverts if sync redeems are not enabled for the token

```solidity
function _requireSyncRedeemsEnabled(IERC20 token) internal view returns (TokenDetailsV2 storage tokenDetails);
```

**Parameters**

| Name    | Type     | Description              |
| ------- | -------- | ------------------------ |
| `token` | `IERC20` | The ERC20 token to check |

**Returns**

| Name           | Type             | Description                         |
| -------------- | ---------------- | ----------------------------------- |
| `tokenDetails` | `TokenDetailsV2` | The token details storage reference |

#### \_requireDepositCapNotExceeded <a href="#requiredepositcapnotexceeded" id="requiredepositcapnotexceeded"></a>

Reverts if deposit cap would be exceeded by adding units

```solidity
function _requireDepositCapNotExceeded(uint256 units) internal view;
```

**Parameters**

| Name    | Type      | Description                |
| ------- | --------- | -------------------------- |
| `units` | `uint256` | The number of units to add |

#### \_isDepositCapExceeded <a href="#isdepositcapexceeded" id="isdepositcapexceeded"></a>

Checks if deposit cap would be exceeded by adding units

```solidity
function _isDepositCapExceeded(uint256 units) internal view returns (bool);
```

**Parameters**

| Name    | Type      | Description                |
| ------- | --------- | -------------------------- |
| `units` | `uint256` | The number of units to add |

**Returns**

| Name     | Type   | Description                                            |
| -------- | ------ | ------------------------------------------------------ |
| `<none>` | `bool` | True if deposit cap would be exceeded, false otherwise |

#### \_tokensToUnitsFloorIfActive <a href="#tokenstounitsfloorifactive" id="tokenstounitsfloorifactive"></a>

Converts token amount to units, applying multiplier and flooring

```solidity
function _tokensToUnitsFloorIfActive(IERC20 token, uint256 tokens, uint256 multiplier)
    internal
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `tokens`     | `uint256` | The amount of tokens    |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `<none>` | `uint256` | The resulting units (floored) |

#### \_tokensToUnitsCeilIfActive <a href="#tokenstounitsceilifactive" id="tokenstounitsceilifactive"></a>

Converts token amount to units, reversing multiplier and ceiling

```solidity
function _tokensToUnitsCeilIfActive(IERC20 token, uint256 tokens, uint256 multiplier)
    internal
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description               |
| ------------ | --------- | ------------------------- |
| `token`      | `IERC20`  | The ERC20 token           |
| `tokens`     | `uint256` | The amount of tokens      |
| `multiplier` | `uint256` | The multiplier to reverse |

**Returns**

| Name     | Type      | Description                  |
| -------- | --------- | ---------------------------- |
| `<none>` | `uint256` | The resulting units (ceiled) |

#### \_unitsToTokensFloorIfActive <a href="#unitstotokensfloorifactive" id="unitstotokensfloorifactive"></a>

Converts units to token amount, applying multiplier and flooring

```solidity
function _unitsToTokensFloorIfActive(IERC20 token, uint256 units, uint256 multiplier)
    internal
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `units`      | `uint256` | The amount of units     |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                          |
| -------- | --------- | ------------------------------------ |
| `<none>` | `uint256` | The resulting token amount (floored) |

#### \_unitsToTokensCeilIfActive <a href="#unitstotokensceilifactive" id="unitstotokensceilifactive"></a>

Converts units to token amount, applying multiplier and ceiling

```solidity
function _unitsToTokensCeilIfActive(IERC20 token, uint256 units, uint256 multiplier)
    internal
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description             |
| ------------ | --------- | ----------------------- |
| `token`      | `IERC20`  | The ERC20 token         |
| `units`      | `uint256` | The amount of units     |
| `multiplier` | `uint256` | The multiplier to apply |

**Returns**

| Name     | Type      | Description                         |
| -------- | --------- | ----------------------------------- |
| `<none>` | `uint256` | The resulting token amount (ceiled) |

#### \_computeEpochCap <a href="#computeepochcap" id="computeepochcap"></a>

Computes the effective epoch cap in numeraire

```solidity
function _computeEpochCap() internal view returns (uint256 epochCapNumeraire, uint256 epochStartTvlNumeraire);
```

**Returns**

| Name                     | Type      | Description                                            |
| ------------------------ | --------- | ------------------------------------------------------ |
| `epochCapNumeraire`      | `uint256` | The effective epoch cap (min of relative and absolute) |
| `epochStartTvlNumeraire` | `uint256` | The epoch-start TVL in numeraire                       |

#### \_computeDynamicPremiumBps <a href="#computedynamicpremiumbps" id="computedynamicpremiumbps"></a>

Computes the global dynamic redeem premium in bps based on price staleness

Returns 0 when maxDynamicPremiumBps is 0 (dynamic premium disabled) Callers enforce priceAge <= syncRedeemMaxPriceAge via require, so dynamicPremiumBps <= maxDynamic Division safety: syncRedeemMaxPriceAge > 0 is enforced by setSyncRedeemDetails

```solidity
function _computeDynamicPremiumBps(uint256 priceAge) internal view returns (uint256 dynamicPremiumBps);
```

**Parameters**

| Name       | Type      | Description                      |
| ---------- | --------- | -------------------------------- |
| `priceAge` | `uint256` | The current price age in seconds |

**Returns**

| Name                | Type      | Description                         |
| ------------------- | --------- | ----------------------------------- |
| `dynamicPremiumBps` | `uint256` | The computed dynamic premium in bps |

#### \_prepareSyncRedeem <a href="#preparesyncredeem" id="preparesyncredeem"></a>

Fetches the anchor timestamp, validates price age, and derives the effective multiplier

```solidity
function _prepareSyncRedeem(uint256 syncRedeemMultiplier)
    internal
    view
    returns (uint256 effectiveMultiplier, uint256 priceTimestamp);
```

**Parameters**

| Name                   | Type      | Description                                            |
| ---------------------- | --------- | ------------------------------------------------------ |
| `syncRedeemMultiplier` | `uint256` | The base sync redeem multiplier for the token (in BPS) |

**Returns**

| Name                  | Type      | Description                                                       |
| --------------------- | --------- | ----------------------------------------------------------------- |
| `effectiveMultiplier` | `uint256` | The multiplier after dynamic premium adjustment                   |
| `priceTimestamp`      | `uint256` | The PFC anchor timestamp (consumed downstream for epoch rollover) |

#### \_getRequestType <a href="#getrequesttype" id="getrequesttype"></a>

Returns the request type based on whether the request is a deposit or redeem and fixed or auto price

```solidity
function _getRequestType(bool isFixedPrice, bool isDeposit) internal pure returns (RequestType);
```

**Parameters**

| Name           | Type   | Description                                               |
| -------------- | ------ | --------------------------------------------------------- |
| `isFixedPrice` | `bool` | Whether the request is a fixed price request              |
| `isDeposit`    | `bool` | Whether the request is a deposit (true) or redeem (false) |

**Returns**

| Name     | Type          | Description               |
| -------- | ------------- | ------------------------- |
| `<none>` | `RequestType` | The computed request type |

#### \_requireValidMultiplier <a href="#requirevalidmultiplier" id="requirevalidmultiplier"></a>

Reverts if a multiplier is outside \[MIN\_MULTIPLIER, ONE\_IN\_BPS]

```solidity
function _requireValidMultiplier(uint256 multiplier) internal pure;
```

**Parameters**

| Name         | Type      | Description                |
| ------------ | --------- | -------------------------- |
| `multiplier` | `uint256` | The multiplier to validate |

#### \_validateNonZeroAmounts <a href="#validatenonzeroamounts" id="validatenonzeroamounts"></a>

Reverts if either the units or tokens amount is zero

```solidity
function _validateNonZeroAmounts(uint256 units, uint256 tokens) internal pure;
```

**Parameters**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `units`  | `uint256` | The units amount to validate  |
| `tokens` | `uint256` | The tokens amount to validate |

#### \_getDepositHash <a href="#getdeposithash" id="getdeposithash"></a>

Get the hash of a deposit

Since refundableUntil is block.timestamp + depositRefundTimeout (which is subject to change), it’s theoretically possible to have a hash collision, but the probability is negligible and we optimize for the common case

```solidity
function _getDepositHash(
    address sender,
    address receiver,
    IERC20 token,
    uint256 tokenAmount,
    uint256 unitsAmount,
    uint256 refundableUntil
) internal pure returns (bytes32);
```

**Parameters**

| Name              | Type      | Description                                        |
| ----------------- | --------- | -------------------------------------------------- |
| `sender`          | `address` | The user who made the deposit                      |
| `receiver`        | `address` | The user that receives units from the deposit      |
| `token`           | `IERC20`  | The token that was deposited                       |
| `tokenAmount`     | `uint256` | The amount of tokens deposited                     |
| `unitsAmount`     | `uint256` | The amount of units received                       |
| `refundableUntil` | `uint256` | The timestamp at which the deposit can be refunded |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the deposit |

#### \_getRequestHashParams <a href="#getrequesthashparams" id="getrequesthashparams"></a>

Get the hash of a request from parameters

```solidity
function _getRequestHashParams(
    IERC20 token,
    address user,
    address receiver,
    RequestType requestType,
    uint256 tokens,
    uint256 units,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge
) internal pure returns (bytes32);
```

**Parameters**

| Name          | Type          | Description                                         |
| ------------- | ------------- | --------------------------------------------------- |
| `token`       | `IERC20`      | The token that was deposited or redeemed            |
| `user`        | `address`     | The user who made the request                       |
| `receiver`    | `address`     | The user that receives funds when request is solved |
| `requestType` | `RequestType` | The type of request                                 |
| `tokens`      | `uint256`     | The amount of tokens in the request                 |
| `units`       | `uint256`     | The amount of units in the request                  |
| `solverTip`   | `uint256`     | The tip paid to the solver                          |
| `deadline`    | `uint256`     | The deadline of the request                         |
| `maxPriceAge` | `uint256`     | The maximum age of the price data                   |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

#### \_getRequestHash <a href="#getrequesthash" id="getrequesthash"></a>

Get the hash of a request

```solidity
function _getRequestHash(IERC20 token, RequestV2 calldata request) internal pure returns (bytes32);
```

**Parameters**

| Name      | Type        | Description                              |
| --------- | ----------- | ---------------------------------------- |
| `token`   | `IERC20`    | The token that was deposited or redeemed |
| `request` | `RequestV2` | The request to get the hash of           |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

#### \_isRequestTypeDeposit <a href="#isrequesttypedeposit" id="isrequesttypedeposit"></a>

Returns true if the request type is a deposit

```solidity
function _isRequestTypeDeposit(RequestType requestType) internal pure returns (bool);
```

**Parameters**

| Name          | Type          | Description      |
| ------------- | ------------- | ---------------- |
| `requestType` | `RequestType` | The request type |

**Returns**

| Name     | Type   | Description                      |
| -------- | ------ | -------------------------------- |
| `<none>` | `bool` | True if deposit, false otherwise |

#### \_isRequestTypeAutoPrice <a href="#isrequesttypeautoprice" id="isrequesttypeautoprice"></a>

Returns true if the request type is fixed price

```solidity
function _isRequestTypeAutoPrice(RequestType requestType) internal pure returns (bool);
```

**Parameters**

| Name          | Type          | Description      |
| ------------- | ------------- | ---------------- |
| `requestType` | `RequestType` | The request type |

**Returns**

| Name     | Type   | Description                          |
| -------- | ------ | ------------------------------------ |
| `<none>` | `bool` | True if fixed price, false otherwise |


# SingleDepositorVault

**Inherits:** ISingleDepositorVault, FeeVault

A vault that allows a single depositor to deposit and withdraw assets and allows a fee recipient and the protocol to charge fees. The vault owner retains full custody of assets at all times and can take arbitrary actions through the execute function. For convenience, ERC20 assets can also be withdrawn using the withdraw function and deposited using the deposit function

*Fee logic is inherited from the fee vault and support for guardians is inherited from the BaseVault*

## Functions

### constructor

```solidity
constructor() FeeVault();
```

### deposit

Deposit assets into the vault

```solidity
function deposit(TokenAmount[] calldata tokenAmounts) external requiresAuth;
```

**Parameters**

| Name           | Type            | Description           |
| -------------- | --------------- | --------------------- |
| `tokenAmounts` | `TokenAmount[]` | The assets to deposit |

### withdraw

Withdraw assets from the vault

```solidity
function withdraw(TokenAmount[] calldata tokenAmounts) external requiresAuth;
```

**Parameters**

| Name           | Type            | Description            |
| -------------- | --------------- | ---------------------- |
| `tokenAmounts` | `TokenAmount[]` | The assets to withdraw |

### execute

Execute operations on the vault as a trusted entity

```solidity
function execute(OperationPayable[] calldata operations) external requiresAuth;
```

**Parameters**

| Name         | Type                 | Description               |
| ------------ | -------------------- | ------------------------- |
| `operations` | `OperationPayable[]` | The operations to execute |


# SingleDepositorVaultDeployDelegate

**Inherits:** IVaultDeployDelegate

Deploys a new SingleDepositorVault contract

*This contract is used to deploy a new SingleDepositorVault contract through a delegatecall*

*It is separate from the SingleDepositorVaultFactory because of the 24kb contracts size limit*

## Functions

### createVault

Deploy a new vault

```solidity
function createVault(bytes32 salt) external returns (address);
```

**Parameters**

| Name   | Type      | Description                    |
| ------ | --------- | ------------------------------ |
| `salt` | `bytes32` | The salt value to create vault |

**Returns**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `<none>` | `address` | deployed Deployed vault address |


# SingleDepositorVaultFactory

**Inherits:** ISingleDepositorVaultFactory, FeeVaultDeployer, Sweepable

Used to create new vaults

*Only one instance of the factory will be required per chain*

## State Variables

### \_DEPLOY\_DELEGATE

Address of the deploy delegate

```solidity
address internal immutable _DEPLOY_DELEGATE;
```

## Functions

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority, address deployDelegate)
    Sweepable(initialOwner, initialAuthority);
```

### create

Create single-depositor vault

```solidity
function create(
    bytes32 salt,
    string calldata description,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata singleDepositorVaultParams,
    address expectedVaultAddress
) external override requiresAuth returns (address deployedVault);
```

**Parameters**

| Name                         | Type                  | Description                                                         |
| ---------------------------- | --------------------- | ------------------------------------------------------------------- |
| `salt`                       | `bytes32`             | The salt used to generate the vault address                         |
| `description`                | `string`              | Vault description                                                   |
| `baseVaultParams`            | `BaseVaultParameters` | Base vault parameters for deployment                                |
| `singleDepositorVaultParams` | `FeeVaultParameters`  | Parameters for deployment related to single depositor functionality |
| `expectedVaultAddress`       | `address`             | Expected vault address to check against deployed vault address      |

**Returns**

| Name            | Type      | Description            |
| --------------- | --------- | ---------------------- |
| `deployedVault` | `address` | Deployed vault address |

### \_deployVault

Deploy vault

```solidity
function _deployVault(
    bytes32 salt,
    string calldata description,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata singleDepositorVaultParams
) internal returns (address deployed);
```

**Parameters**

| Name                         | Type                  | Description                                                      |
| ---------------------------- | --------------------- | ---------------------------------------------------------------- |
| `salt`                       | `bytes32`             | The salt value to create vault                                   |
| `description`                | `string`              | Vault description                                                |
| `baseVaultParams`            | `BaseVaultParameters` | Parameters for vault deployment used in BaseVault                |
| `singleDepositorVaultParams` | `FeeVaultParameters`  | Parameters for vault deployment specific to SingleDepositorVault |

**Returns**

| Name       | Type      | Description            |
| ---------- | --------- | ---------------------- |
| `deployed` | `address` | Deployed vault address |

### \_createVault

Create a new vault with delegate call

```solidity
function _createVault(bytes32 salt) internal returns (address deployed);
```

**Parameters**

| Name   | Type      | Description                    |
| ------ | --------- | ------------------------------ |
| `salt` | `bytes32` | The salt value to create vault |

**Returns**

| Name       | Type      | Description            |
| ---------- | --------- | ---------------------- |
| `deployed` | `address` | Deployed vault address |


# Sweepable

**Inherits:** ISweepable, Auth2Step

This contract allows the owner of the contract to recover accidentally sent tokens and the chain's native token

## Functions

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority) Auth2Step(initialOwner, initialAuthority);
```

### sweep

Withdraw any tokens accidentally sent to contract

```solidity
function sweep(address token, uint256 amount) external requiresAuth;
```

**Parameters**

| Name     | Type      | Description                                                            |
| -------- | --------- | ---------------------------------------------------------------------- |
| `token`  | `address` | Token address to withdraw or zero address for the chain's native token |
| `amount` | `uint256` | Amount to withdraw                                                     |


# VaultAuth

Abstract contract that provides authorization check for vault operations

*Used by contracts that need to verify if a caller has permission to perform vault-specific actions. The authorization can come from either being the vault owner or having explicit permission through the vault's authority*

## Functions

### requiresVaultAuth

```solidity
modifier requiresVaultAuth(address vault);
```

## Errors

### Aera\_\_CallerIsNotAuthorized

```solidity
error Aera__CallerIsNotAuthorized();
```


# Whitelist

**Inherits:** IWhitelist, Auth2Step

Contract for managing a whitelist of addresses

## State Variables

### whitelist

Mapping of addresses to whether they are whitelisted

```solidity
EnumerableMap.AddressToUintMap internal whitelist;
```

## Functions

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority) Auth2Step(initialOwner, initialAuthority);
```

### setWhitelisted

Set the address whitelisted status

```solidity
function setWhitelisted(address addr, bool isAddressWhitelisted) external requiresAuth;
```

**Parameters**

| Name                   | Type      | Description                                         |
| ---------------------- | --------- | --------------------------------------------------- |
| `addr`                 | `address` | The address to add/remove from the whitelist        |
| `isAddressWhitelisted` | `bool`    | Whether address should be whitelisted going forward |

### isWhitelisted

Checks if the address is whitelisted

```solidity
function isWhitelisted(address addr) external view returns (bool);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `addr` | `address` | The address to check |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if the addr is whitelisted, false otherwise |

### getAllWhitelisted

Get all whitelisted addresses

```solidity
function getAllWhitelisted() external view returns (address[] memory);
```

**Returns**

| Name     | Type        | Description                           |
| -------- | ----------- | ------------------------------------- |
| `<none>` | `address[]` | An array of all whitelisted addresses |


# IAuth2Step

Interface for the Auth2Step contract

## Functions

### acceptOwnership

Accept ownership transfer

```solidity
function acceptOwnership() external;
```

### transferOwnership

Wrapper function for backward compatibility with legacy code expecting transferOwnership

*This function exists to maintain compatibility with contracts that were built against the previous version of Auth where ownership transfer was named `transferOwnership` The new Auth implementation renamed this to `setOwner` to better reflect the two-step ownership transfer process. This wrapper ensures existing code like BaseVault continues to work without modification Previous version: <https://github.com/transmissions11/solmate/blob/89365b880c4f3c786bdd453d4b8e8fe410344a69/src/auth/Auth.sol> New version: <https://github.com/transmissions11/solmate/blob/eaa7041378f9a6c12f943de08a6c41b31a9870fc/src/auth/Auth.sol>*

```solidity
function transferOwnership(address newOwner) external;
```

**Parameters**

| Name       | Type      | Description                                        |
| ---------- | --------- | -------------------------------------------------- |
| `newOwner` | `address` | Address to start the ownership transfer process to |

## Events

### OwnershipTransferStarted

Emitted when ownership transfer is initiated

```solidity
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
```

**Parameters**

| Name            | Type      | Description                                    |
| --------------- | --------- | ---------------------------------------------- |
| `previousOwner` | `address` | The current owner initiating the transfer      |
| `newOwner`      | `address` | The new owner who needs to accept the transfer |

## Errors

### Aera\_\_ZeroAddressAuthority

```solidity
error Aera__ZeroAddressAuthority();
```

### Aera\_\_Unauthorized

```solidity
error Aera__Unauthorized();
```


# IBaseFeeCalculator

Base interface for a contract that calculates TVL and performance fees for a vault and protocol

## Functions

### setProtocolFeeRecipient

Set the protocol fee recipient

```solidity
function setProtocolFeeRecipient(address feeRecipient) external;
```

**Parameters**

| Name           | Type      | Description                               |
| -------------- | --------- | ----------------------------------------- |
| `feeRecipient` | `address` | The address of the protocol fee recipient |

### setProtocolFees

Set the protocol fee rates

```solidity
function setProtocolFees(uint16 tvl, uint16 performance) external;
```

**Parameters**

| Name          | Type     | Description                              |
| ------------- | -------- | ---------------------------------------- |
| `tvl`         | `uint16` | The TVL fee rate in basis points         |
| `performance` | `uint16` | The performance fee rate in basis points |

### setVaultFees

Set the vault-specific fee rates

```solidity
function setVaultFees(address vault, uint16 tvl, uint16 performance) external;
```

**Parameters**

| Name          | Type      | Description                              |
| ------------- | --------- | ---------------------------------------- |
| `vault`       | `address` | The address of the vault                 |
| `tvl`         | `uint16`  | The TVL fee rate in basis points         |
| `performance` | `uint16`  | The performance fee rate in basis points |

### setVaultAccountant

Set the accountant for a vault

```solidity
function setVaultAccountant(address vault, address accountant) external;
```

**Parameters**

| Name         | Type      | Description                       |
| ------------ | --------- | --------------------------------- |
| `vault`      | `address` | The address of the vault          |
| `accountant` | `address` | The address of the new accountant |

## Events

### VaultFeesSet

Emitted when a vault's fees are updated

```solidity
event VaultFeesSet(address indexed vault, uint16 tvlFee, uint16 performanceFee);
```

**Parameters**

| Name             | Type      | Description                                  |
| ---------------- | --------- | -------------------------------------------- |
| `vault`          | `address` | The address of the vault                     |
| `tvlFee`         | `uint16`  | The new TVL fee rate in basis points         |
| `performanceFee` | `uint16`  | The new performance fee rate in basis points |

### ProtocolFeeRecipientSet

Emitted when the protocol fee recipient is updated

```solidity
event ProtocolFeeRecipientSet(address indexed feeRecipient);
```

**Parameters**

| Name           | Type      | Description                               |
| -------------- | --------- | ----------------------------------------- |
| `feeRecipient` | `address` | The address of the protocol fee recipient |

### ProtocolFeesSet

Emitted when protocol fees are updated

```solidity
event ProtocolFeesSet(uint16 tvlFee, uint16 performanceFee);
```

**Parameters**

| Name             | Type     | Description                                           |
| ---------------- | -------- | ----------------------------------------------------- |
| `tvlFee`         | `uint16` | The new protocol TVL fee rate in basis points         |
| `performanceFee` | `uint16` | The new protocol performance fee rate in basis points |

### VaultAccountantSet

Emitted when the accountant for a vault is updated

```solidity
event VaultAccountantSet(address vault, address accountant);
```

**Parameters**

| Name         | Type      | Description                                                |
| ------------ | --------- | ---------------------------------------------------------- |
| `vault`      | `address` | The address of the vault whose accountant is being updated |
| `accountant` | `address` | The address of the new accountant assigned to the vault    |

## Errors

### Aera\_\_TvlFeeTooHigh

Thrown when attempting to set an TVL fee higher than the maximum allowed

```solidity
error Aera__TvlFeeTooHigh();
```

### Aera\_\_PerformanceFeeTooHigh

Thrown when attempting to set a performance fee higher than the maximum allowed

```solidity
error Aera__PerformanceFeeTooHigh();
```

### Aera\_\_ZeroAddressProtocolFeeRecipient

Thrown when attempting to set a protocol fee recipient to the zero address

```solidity
error Aera__ZeroAddressProtocolFeeRecipient();
```

### Aera\_\_CallerIsNotVaultOwner

Thrown when attempting to set vault fees for a vault that is not owned by the caller

```solidity
error Aera__CallerIsNotVaultOwner();
```

### Aera\_\_CallerIsNotVaultAccountant

Thrown when attempting to perform an action on a vault by someone who is not its assigned accountant

```solidity
error Aera__CallerIsNotVaultAccountant();
```

### Aera\_\_VaultNotRegistered

Thrown during a vault is not registered and action requires a registered vault

```solidity
error Aera__VaultNotRegistered();
```


# IBaseVault

Interface for the BaseVault

## Functions

### submit

Submit a series of operations to the vault

```solidity
function submit(bytes calldata data) external;
```

**Parameters**

| Name   | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data` | `bytes` | Encoded array of operations to submit ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐ │ FIELDS │ SIZE │ DESCRIPTION │ ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤ │ operationsLength 1 byte Number of operations in the array │ │ │ │ \[for each operation]: │ │ │ │ SIGNATURE │ │ target 20 bytes Target contract address │ │ calldataLength 2 bytes Length of calldata │ │ calldata bytes Calldata (before pipelining) │ │ │ │ CLIPBOARD │ │ clipboardsLength 1 byte Number of clipboards │ │ \[for each clipboard entry]: │ │ resultIndex 1 byte Which operation to take from │ │ copyWord 1 byte Which word to copy │ │ pasteOffset 2 bytes What offset to paste it at │ │ │ │ CALL TYPE │ │ isStaticCall 1 byte 1 if static, 0 if a regular call │ │ \[if isStaticCall == 0]: │ │ │ │ CALLBACK HANDLING │ │ hasCallback 1 byte Whether to allow callbacks during operation │ │ \[if hasCallback == 1]: │ │ callbackData = 26 bytes Expected callback info │ │ ┌────────────────────┬──────────────────────────┬───────────────────┐ │ │ │ selector (4 bytes) │ calldataOffset (2 bytes) │ caller (20 bytes) │ │ │ └────────────────────┴──────────────────────────┴───────────────────┘ │ │ │ │ HOOKS │ │ hookConfig = 1 byte Hook configuration │ │ ┌─────────────────┬────────────────────────────────────────┐ │ │ │ hasHook (1 bit) │ configurableHookOffsetsLength (7 bits) │ │ │ └─────────────────┴────────────────────────────────────────┘ │ │ if configurableHookOffsetsLength > 0: │ │ configurableHookOffsets 32 bytes Packed configurable hook offsets │ │ if hasHook == 1: │ │ hook 20 bytes Hook contract address │ │ │ │ MERKLE PROOF │ │ proofLength 1 byte Merkle proof length │ │ proof \* 32 bytes Merkle proof data │ │ │ │ PAYABILITY │ │ hasValue 1 byte Whether to send native token with the call │ │ \[if hasValue == 1]: │ │ value 32 bytes Amount of native token to send │ └───────────────────────────────────────────────────────────────────────────────────────────────────────┘ |

### setGuardianRoot

Set the merkle root for a guardian Used to add guardians and update their permissions

```solidity
function setGuardianRoot(address guardian, bytes32 root) external;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `guardian` | `address` | Address of the guardian |
| `root`     | `bytes32` | Merkle root             |

### removeGuardian

Removes a guardian from the vault

```solidity
function removeGuardian(address guardian) external;
```

**Parameters**

| Name       | Type      | Description             |
| ---------- | --------- | ----------------------- |
| `guardian` | `address` | Address of the guardian |

### setSubmitHooks

Set the submit hooks address

```solidity
function setSubmitHooks(ISubmitHooks newSubmitHooks) external;
```

**Parameters**

| Name             | Type           | Description                              |
| ---------------- | -------------- | ---------------------------------------- |
| `newSubmitHooks` | `ISubmitHooks` | Address of the new submit hooks contract |

### pause

Pause the vault, halting the ability for guardians to submit

```solidity
function pause() external;
```

### unpause

Unpause the vault, allowing guardians to submit operations

```solidity
function unpause() external;
```

### checkGuardianWhitelist

Check if the guardian is whitelisted and set the root to zero if not Used to disable guardians who were removed from the whitelist after being selected as guardians

```solidity
function checkGuardianWhitelist(address guardian) external returns (bool isRemoved);
```

**Parameters**

| Name       | Type      | Description          |
| ---------- | --------- | -------------------- |
| `guardian` | `address` | The guardian address |

**Returns**

| Name        | Type   | Description                                         |
| ----------- | ------ | --------------------------------------------------- |
| `isRemoved` | `bool` | Whether the guardian was removed from the whitelist |

### getActiveGuardians

Get all active guardians

```solidity
function getActiveGuardians() external view returns (address[] memory);
```

**Returns**

| Name     | Type        | Description                        |
| -------- | ----------- | ---------------------------------- |
| `<none>` | `address[]` | Array of active guardian addresses |

### getGuardianRoot

Get the guardian root for a guardian

```solidity
function getGuardianRoot(address guardian) external view returns (bytes32);
```

**Parameters**

| Name       | Type      | Description          |
| ---------- | --------- | -------------------- |
| `guardian` | `address` | The guardian address |

**Returns**

| Name     | Type      | Description       |
| -------- | --------- | ----------------- |
| `<none>` | `bytes32` | The guardian root |

### getCurrentHookCallType

Get the current hook call type

```solidity
function getCurrentHookCallType() external view returns (HookCallType);
```

**Returns**

| Name     | Type           | Description                |
| -------- | -------------- | -------------------------- |
| `<none>` | `HookCallType` | The current hook call type |

## Events

### SubmitHooksSet

Emitted when submit hooks are updated

```solidity
event SubmitHooksSet(address indexed submitHooksAddress);
```

**Parameters**

| Name                 | Type      | Description                           |
| -------------------- | --------- | ------------------------------------- |
| `submitHooksAddress` | `address` | The new submit hooks contract address |

### GuardianRootSet

Emitted when a guardian's merkle root is set

```solidity
event GuardianRootSet(address indexed guardian, bytes32 indexed root);
```

**Parameters**

| Name       | Type      | Description                          |
| ---------- | --------- | ------------------------------------ |
| `guardian` | `address` | The guardian's address               |
| `root`     | `bytes32` | The new merkle root for the guardian |

## Errors

### Aera\_\_ZeroAddressGuardian

```solidity
error Aera__ZeroAddressGuardian();
```

### Aera\_\_ZeroAddressOwner

```solidity
error Aera__ZeroAddressOwner();
```

### Aera\_\_CallerIsNotGuardian

```solidity
error Aera__CallerIsNotGuardian();
```

### Aera\_\_CallerIsNotAuthOrGuardian

```solidity
error Aera__CallerIsNotAuthOrGuardian();
```

### Aera\_\_SubmissionFailed

```solidity
error Aera__SubmissionFailed(uint256 index, bytes result);
```

### Aera\_\_AllowanceIsNotZero

```solidity
error Aera__AllowanceIsNotZero(address token, address spender);
```

### Aera\_\_ZeroAddressMerkleRoot

```solidity
error Aera__ZeroAddressMerkleRoot();
```

### Aera\_\_BeforeSubmitHooksFailed

```solidity
error Aera__BeforeSubmitHooksFailed(bytes result);
```

### Aera\_\_AfterSubmitHooksFailed

```solidity
error Aera__AfterSubmitHooksFailed(bytes result);
```

### Aera\_\_BeforeOperationHooksFailed

```solidity
error Aera__BeforeOperationHooksFailed(uint256 index, bytes result);
```

### Aera\_\_AfterOperationHooksFailed

```solidity
error Aera__AfterOperationHooksFailed(uint256 index, bytes result);
```

### Aera\_\_BeforeOperationHooksWithConfigurableHooks

```solidity
error Aera__BeforeOperationHooksWithConfigurableHooks();
```

### Aera\_\_ProofVerificationFailed

```solidity
error Aera__ProofVerificationFailed();
```

### Aera\_\_InvalidBeforeOperationHooksReturnDataLength

```solidity
error Aera__InvalidBeforeOperationHooksReturnDataLength();
```

### Aera\_\_GuardianNotWhitelisted

```solidity
error Aera__GuardianNotWhitelisted();
```

### Aera\_\_ExpectedCallbackNotReceived

```solidity
error Aera__ExpectedCallbackNotReceived();
```

### Aera\_\_NoResults

```solidity
error Aera__NoResults();
```


# IBaseVaultDeployer

Interface for vault deployer

## Functions

### baseVaultParameters

Vault parameters for vault deployment

*Necessary to support deterministic vault deployments*

```solidity
function baseVaultParameters() external view returns (BaseVaultParameters memory);
```

**Returns**

| Name     | Type                  | Description                                                                                   |
| -------- | --------------------- | --------------------------------------------------------------------------------------------- |
| `<none>` | `BaseVaultParameters` | parameters Parameters used for vault deployment, including owner, submit hooks, and whitelist |

## Errors

### Aera\_\_DescriptionIsEmpty

Thrown when vault description is empty

```solidity
error Aera__DescriptionIsEmpty();
```

### Aera\_\_VaultAddressMismatch

Thrown when deployed vault address doesn't match expected address

```solidity
error Aera__VaultAddressMismatch(address deployed, address expected);
```

**Parameters**

| Name       | Type      | Description                   |
| ---------- | --------- | ----------------------------- |
| `deployed` | `address` | Address of the deployed vault |
| `expected` | `address` | Expected address of the vault |


# IBaseVaultFactory

**Inherits:** IBaseVaultDeployer

Interface for the base vault factory

## Functions

### create

Create a new vault with the given parameters

```solidity
function create(
    bytes32 salt,
    string calldata description,
    BaseVaultParameters calldata baseVaultParams,
    address expectedVaultAddress
) external returns (address deployedVault);
```

**Parameters**

| Name                   | Type                  | Description                            |
| ---------------------- | --------------------- | -------------------------------------- |
| `salt`                 | `bytes32`             | The salt value to use for create2      |
| `description`          | `string`              | Vault description                      |
| `baseVaultParams`      | `BaseVaultParameters` | Parameters for vault deployment        |
| `expectedVaultAddress` | `address`             | Expected address of the deployed vault |

**Returns**

| Name            | Type      | Description                   |
| --------------- | --------- | ----------------------------- |
| `deployedVault` | `address` | Address of the deployed vault |

## Events

### VaultCreated

Emitted when the vault is created

```solidity
event VaultCreated(address indexed vault, address indexed owner, address submitHooks, string description);
```

**Parameters**

| Name          | Type      | Description           |
| ------------- | --------- | --------------------- |
| `vault`       | `address` | Vault address         |
| `owner`       | `address` | Initial owner address |
| `submitHooks` | `address` | Submit hooks address  |
| `description` | `string`  | Vault description     |


# IBeforeTransferHook

Interface for token transfer hooks used for vault units in multi-depositor vaults

## Functions

### setIsVaultUnitsTransferable

Set whether vault units should be transferable

```solidity
function setIsVaultUnitsTransferable(address vault, bool isTransferable) external;
```

**Parameters**

| Name             | Type      | Description                              |
| ---------------- | --------- | ---------------------------------------- |
| `vault`          | `address` | The vault to update status for           |
| `isTransferable` | `bool`    | Whether the vault units are transferable |

### beforeTransfer

Perform before transfer checks

```solidity
function beforeTransfer(address from, address to, address transferAgent) external view;
```

**Parameters**

| Name            | Type      | Description                                          |
| --------------- | --------- | ---------------------------------------------------- |
| `from`          | `address` | Address that is sending the units                    |
| `to`            | `address` | Address that is receiving the units                  |
| `transferAgent` | `address` | Address that is always allowed to transfer the units |

## Events

### VaultUnitTransferableSet

Emitted when vault unit transferability is updated

```solidity
event VaultUnitTransferableSet(address indexed vault, bool isTransferable);
```

**Parameters**

| Name             | Type      | Description                              |
| ---------------- | --------- | ---------------------------------------- |
| `vault`          | `address` | The vault address                        |
| `isTransferable` | `bool`    | Whether the vault units are transferable |

## Errors

### Aera\_\_NotVaultOwner

```solidity
error Aera__NotVaultOwner();
```

### Aera\_\_VaultUnitsNotTransferable

```solidity
error Aera__VaultUnitsNotTransferable(address vault);
```


# ICallbackHandler

Errors used in the CallbackHandler mixin

## Errors

### Aera\_\_UnauthorizedCallback

Thrown when we receive a callback (or a regular call) that wasn't authorized

```solidity
error Aera__UnauthorizedCallback();
```


# IDelayedFeeCalculator

Interface for a contract that calculates fee inputs for a single-depositor vault

## Functions

### submitSnapshot

Submit a new snapshot for fee calculation

```solidity
function submitSnapshot(address vault, uint160 averageValue, uint128 highestProfit, uint32 timestamp) external;
```

**Parameters**

| Name            | Type      | Description                                                                        |
| --------------- | --------- | ---------------------------------------------------------------------------------- |
| `vault`         | `address` | The address of the vault                                                           |
| `averageValue`  | `uint160` | The average value during the period since last snapshot to this snapshot timestamp |
| `highestProfit` | `uint128` | The highest profit achieved up to the snapshot timestamp                           |
| `timestamp`     | `uint32`  | The timestamp of the snapshot                                                      |

### accrueFees

Process fee accrual for a vault

```solidity
function accrueFees(address vault) external returns (uint256 tvlFeesEarned, uint256 performanceFeesEarned);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name                    | Type      | Description                                                |
| ----------------------- | --------- | ---------------------------------------------------------- |
| `tvlFeesEarned`         | `uint256` | The earned TVL fees for the vault and the protocol         |
| `performanceFeesEarned` | `uint256` | The earned performance fees for the vault and the protocol |

### vaultFeeState

The fee state of a vault

```solidity
function vaultFeeState(address vault) external view returns (VaultSnapshot memory, VaultAccruals memory);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name     | Type            | Description                                               |
| -------- | --------------- | --------------------------------------------------------- |
| `<none>` | `VaultSnapshot` | vaultSnapshotFeeState The snapshot fee state of the vault |
| `<none>` | `VaultAccruals` | baseVaultFeeState The base fee state of the vault         |

## Events

### SnapshotSubmitted

Emitted when a new snapshot of fee inputs is submitted for a vault

*highestProfit is equivalent to a high water mark but could be applicable to a subset of the vault*

```solidity
event SnapshotSubmitted(address indexed vault, uint160 averageValue, uint128 highestProfit, uint32 timestamp);
```

**Parameters**

| Name            | Type      | Description                                                          |
| --------------- | --------- | -------------------------------------------------------------------- |
| `vault`         | `address` | The vault address                                                    |
| `averageValue`  | `uint160` | The average value of the vault during the period since last snapshot |
| `highestProfit` | `uint128` | The highest profit achieved during the period since last snapshot    |
| `timestamp`     | `uint32`  | The timestamp of the snapshot                                        |

## Errors

### Aera\_\_SnapshotTooOld

Thrown when a snapshot's timestamp is older than the last fee accrual

```solidity
error Aera__SnapshotTooOld();
```

### Aera\_\_SnapshotInFuture

Thrown when a snapshot's timestamp is in the future

```solidity
error Aera__SnapshotInFuture();
```

### Aera\_\_HighestProfitDecreased

Thrown when attempting to accrue fees with a highest profit that is less than the last highest profit

```solidity
error Aera__HighestProfitDecreased();
```

### Aera\_\_DisputePeriodTooLong

Thrown when the dispute period is greater than the maximum allowed

```solidity
error Aera__DisputePeriodTooLong();
```


# IFeeCalculator

Interface for a contract that calculates fees for a vault and protocol

## Functions

### registerVault

Register a new vault with the fee calculator

```solidity
function registerVault() external;
```

### claimFees

Process a fee claim for a specific vault

*Expected to be called by the vault only when claiming fees Only accrues fees and updates stored values; does not transfer tokens Caller must perform the actual transfers to avoid permanent fee loss*

```solidity
function claimFees(uint256 feeTokenBalance) external returns (uint256, uint256, address);
```

**Parameters**

| Name              | Type      | Description                               |
| ----------------- | --------- | ----------------------------------------- |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute |

**Returns**

| Name     | Type      | Description                                                                  |
| -------- | --------- | ---------------------------------------------------------------------------- |
| `<none>` | `uint256` | earnedFees The amount of fees to be claimed by the fee recipient             |
| `<none>` | `uint256` | protocolEarnedFees The amount of protocol fees to be claimed by the protocol |
| `<none>` | `address` | protocolFeeRecipient The address of the protocol fee recipient               |

### claimProtocolFees

Process a protocol fee claim for a vault

*Expected to be called by the vault only when claiming protocol fees Only accrues protocol fees and updates stored values; does not transfer tokens Caller must perform the actual transfers to avoid permanent protocol fee loss*

```solidity
function claimProtocolFees(uint256 feeTokenBalance) external returns (uint256, address);
```

**Parameters**

| Name              | Type      | Description                               |
| ----------------- | --------- | ----------------------------------------- |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute |

**Returns**

| Name     | Type      | Description                                                    |
| -------- | --------- | -------------------------------------------------------------- |
| `<none>` | `uint256` | accruedFees The amount of protocol fees claimed                |
| `<none>` | `address` | protocolFeeRecipient The address of the protocol fee recipient |

### previewFees

Returns the current claimable fees for the given vault, as if a claim was made now

```solidity
function previewFees(address vault, uint256 feeTokenBalance)
    external
    view
    returns (uint256 vaultFees, uint256 protocolFees);
```

**Parameters**

| Name              | Type      | Description                                                                                                                                                                               |
| ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vault`           | `address` | The address of the vault to preview fees for                                                                                                                                              |
| `feeTokenBalance` | `uint256` | Available fee token balance to distribute If set to `type(uint256).max`, the function returns all accrued fees If set to an actual balance, the result is capped to that claimable amount |

**Returns**

| Name           | Type      | Description                                |
| -------------- | --------- | ------------------------------------------ |
| `vaultFees`    | `uint256` | The amount of claimable fees for the vault |
| `protocolFees` | `uint256` | The amount of claimable protocol fees      |

### protocolFeeRecipient

Returns the address that receives protocol fees

```solidity
function protocolFeeRecipient() external view returns (address);
```

**Returns**

| Name     | Type      | Description                                 |
| -------- | --------- | ------------------------------------------- |
| `<none>` | `address` | The address that receives the protocol fees |

## Events

### VaultRegistered

Emitted when a new vault is registered

```solidity
event VaultRegistered(address indexed vault);
```

**Parameters**

| Name    | Type      | Description                         |
| ------- | --------- | ----------------------------------- |
| `vault` | `address` | The address of the registered vault |

## Errors

### Aera\_\_VaultAlreadyRegistered

Thrown when attempting to register an already registered vault

```solidity
error Aera__VaultAlreadyRegistered();
```


# IFeeVault

Interface for vaults that support fees but don't have multiple depositors

## Functions

### setFeeRecipient

Set the fee recipient

```solidity
function setFeeRecipient(address newFeeRecipient) external;
```

**Parameters**

| Name              | Type      | Description                   |
| ----------------- | --------- | ----------------------------- |
| `newFeeRecipient` | `address` | The new fee recipient address |

### claimFees

Claim accrued fees for msg.sender

*Automatically claims any earned protocol fees for the protocol*

```solidity
function claimFees() external returns (uint256 feeRecipientFees, uint256 protocolFees);
```

**Returns**

| Name               | Type      | Description                                               |
| ------------------ | --------- | --------------------------------------------------------- |
| `feeRecipientFees` | `uint256` | The amount of fees to be claimed by the fee recipient     |
| `protocolFees`     | `uint256` | The amount of protocol fees to be claimed by the protocol |

### claimProtocolFees

Claim accrued protocol fees

```solidity
function claimProtocolFees() external returns (uint256 protocolFees);
```

**Returns**

| Name           | Type      | Description                                               |
| -------------- | --------- | --------------------------------------------------------- |
| `protocolFees` | `uint256` | The amount of protocol fees to be claimed by the protocol |

### setFeeCalculator

Set the fee calculator

*newFeeCalculator can be zero, which has the effect as disabling the fee calculator*

```solidity
function setFeeCalculator(IFeeCalculator newFeeCalculator) external;
```

**Parameters**

| Name               | Type             | Description            |
| ------------------ | ---------------- | ---------------------- |
| `newFeeCalculator` | `IFeeCalculator` | The new fee calculator |

### feeCalculator

Get the fee calculator

```solidity
function feeCalculator() external view returns (IFeeCalculator);
```

**Returns**

| Name     | Type             | Description                         |
| -------- | ---------------- | ----------------------------------- |
| `<none>` | `IFeeCalculator` | The current fee calculator contract |

### FEE\_TOKEN

Get the fee token

```solidity
function FEE_TOKEN() external view returns (IERC20);
```

**Returns**

| Name     | Type     | Description                     |
| -------- | -------- | ------------------------------- |
| `<none>` | `IERC20` | The token used for fee payments |

## Events

### FeesClaimed

Emitted when fees are claimed by the fee recipient

```solidity
event FeesClaimed(address indexed feeRecipient, uint256 fees);
```

**Parameters**

| Name           | Type      | Description                   |
| -------------- | --------- | ----------------------------- |
| `feeRecipient` | `address` | The address claiming the fees |
| `fees`         | `uint256` | The amount of fees claimed    |

### ProtocolFeesClaimed

Emitted when protocol fees are claimed

```solidity
event ProtocolFeesClaimed(address indexed protocolFeeRecipient, uint256 protocolEarnedFees);
```

**Parameters**

| Name                   | Type      | Description                            |
| ---------------------- | --------- | -------------------------------------- |
| `protocolFeeRecipient` | `address` | The address claiming the protocol fees |
| `protocolEarnedFees`   | `uint256` | The amount of protocol fees claimed    |

### FeeRecipientUpdated

Emitted when the fee recipient is updated

```solidity
event FeeRecipientUpdated(address indexed newFeeRecipient);
```

**Parameters**

| Name              | Type      | Description                   |
| ----------------- | --------- | ----------------------------- |
| `newFeeRecipient` | `address` | The new fee recipient address |

### FeeCalculatorUpdated

Emitted when the fee calculator is updated

```solidity
event FeeCalculatorUpdated(address indexed newFeeCalculator);
```

**Parameters**

| Name               | Type      | Description                    |
| ------------------ | --------- | ------------------------------ |
| `newFeeCalculator` | `address` | The new fee calculator address |

## Errors

### Aera\_\_ZeroAddressFeeCalculator

```solidity
error Aera__ZeroAddressFeeCalculator();
```

### Aera\_\_ZeroAddressFeeToken

```solidity
error Aera__ZeroAddressFeeToken();
```

### Aera\_\_ZeroAddressFeeRecipient

```solidity
error Aera__ZeroAddressFeeRecipient();
```

### Aera\_\_NoFeesToClaim

```solidity
error Aera__NoFeesToClaim();
```

### Aera\_\_CallerIsNotFeeRecipient

```solidity
error Aera__CallerIsNotFeeRecipient();
```

### Aera\_\_CallerIsNotProtocolFeeRecipient

```solidity
error Aera__CallerIsNotProtocolFeeRecipient();
```


# IFeeVaultDeployer

**Inherits:** IBaseVaultDeployer

Interface for the fee vault deployer

## Functions

### feeVaultParameters

Get the deployment parameters for the fee vault

```solidity
function feeVaultParameters() external view returns (FeeVaultParameters memory params);
```

**Returns**

| Name     | Type                 | Description                             |
| -------- | -------------------- | --------------------------------------- |
| `params` | `FeeVaultParameters` | Deployment parameters for the fee vault |


# IHasNumeraire

Interface for a contract with a numeraire token

## Functions

### NUMERAIRE

Get the vault's numeraire token

```solidity
function NUMERAIRE() external view returns (address);
```

**Returns**

| Name     | Type      | Description                        |
| -------- | --------- | ---------------------------------- |
| `<none>` | `address` | The address of the numeraire token |

## Errors

### Aera\_\_ZeroAddressNumeraire

```solidity
error Aera__ZeroAddressNumeraire();
```


# IMultiDepositorVault

Interface for vaults that can accept deposits from multiple addresses

## Functions

### setBeforeTransferHook

Set the before transfer hooks

```solidity
function setBeforeTransferHook(IBeforeTransferHook hooks) external;
```

**Parameters**

| Name    | Type                  | Description                       |
| ------- | --------------------- | --------------------------------- |
| `hooks` | `IBeforeTransferHook` | The before transfer hooks address |

### enter

Deposit tokens into the vault and mint units

```solidity
function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient) external;
```

**Parameters**

| Name          | Type      | Description                    |
| ------------- | --------- | ------------------------------ |
| `sender`      | `address` | The sender of the tokens       |
| `token`       | `IERC20`  | The token to deposit           |
| `tokenAmount` | `uint256` | The amount of token to deposit |
| `unitsAmount` | `uint256` | The amount of units to mint    |
| `recipient`   | `address` | The recipient of the units     |

### exit

Withdraw tokens from the vault and burn units

```solidity
function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient) external;
```

**Parameters**

| Name          | Type      | Description                     |
| ------------- | --------- | ------------------------------- |
| `sender`      | `address` | The sender of the units         |
| `token`       | `IERC20`  | The token to withdraw           |
| `tokenAmount` | `uint256` | The amount of token to withdraw |
| `unitsAmount` | `uint256` | The amount of units to burn     |
| `recipient`   | `address` | The recipient of the tokens     |

## Events

### BeforeTransferHookSet

Emitted when the before transfer hook is updated

```solidity
event BeforeTransferHookSet(address indexed beforeTransferHook);
```

**Parameters**

| Name                 | Type      | Description                          |
| -------------------- | --------- | ------------------------------------ |
| `beforeTransferHook` | `address` | The new before transfer hook address |

### ProvisionerSet

Emitted when the provisioner is updated

```solidity
event ProvisionerSet(address indexed provisioner);
```

**Parameters**

| Name          | Type      | Description                 |
| ------------- | --------- | --------------------------- |
| `provisioner` | `address` | The new provisioner address |

### Enter

Emitted when tokens are deposited into the vault

```solidity
event Enter(
    address indexed sender, address indexed recipient, IERC20 indexed token, uint256 tokenAmount, uint256 unitsAmount
);
```

**Parameters**

| Name          | Type      | Description                           |
| ------------- | --------- | ------------------------------------- |
| `sender`      | `address` | The address initiating the deposit    |
| `recipient`   | `address` | The address receiving the vault units |
| `token`       | `IERC20`  | The token being deposited             |
| `tokenAmount` | `uint256` | The amount of tokens deposited        |
| `unitsAmount` | `uint256` | The amount of vault units minted      |

### Exit

Emitted when tokens are withdrawn from the vault

```solidity
event Exit(
    address indexed sender, address indexed recipient, IERC20 indexed token, uint256 tokenAmount, uint256 unitsAmount
);
```

**Parameters**

| Name          | Type      | Description                           |
| ------------- | --------- | ------------------------------------- |
| `sender`      | `address` | The address initiating the withdrawal |
| `recipient`   | `address` | The address receiving the tokens      |
| `token`       | `IERC20`  | The token being withdrawn             |
| `tokenAmount` | `uint256` | The amount of tokens withdrawn        |
| `unitsAmount` | `uint256` | The amount of vault units burned      |

## Errors

### Aera\_\_UnitsLocked

```solidity
error Aera__UnitsLocked();
```

### Aera\_\_ZeroAddressProvisioner

```solidity
error Aera__ZeroAddressProvisioner();
```

### Aera\_\_CallerIsNotProvisioner

```solidity
error Aera__CallerIsNotProvisioner();
```


# IMultiDepositorVaultFactory

**Inherits:** IFeeVaultDeployer

Interface for the multi depositor vault factory

## Functions

### create

Create multi depositor vault

```solidity
function create(
    bytes32 salt,
    string calldata description,
    ERC20Parameters calldata erc20Params,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata feeVaultParams,
    IBeforeTransferHook beforeTransferHook,
    address expectedVaultAddress
) external returns (address deployedVault);
```

**Parameters**

| Name                   | Type                  | Description                                                    |
| ---------------------- | --------------------- | -------------------------------------------------------------- |
| `salt`                 | `bytes32`             | The salt used to generate the vault address                    |
| `description`          | `string`              | Vault description                                              |
| `erc20Params`          | `ERC20Parameters`     | ERC20 parameters for deployment                                |
| `baseVaultParams`      | `BaseVaultParameters` | Base vault parameters for deployment                           |
| `feeVaultParams`       | `FeeVaultParameters`  | Fee vault parameters for deployment                            |
| `beforeTransferHook`   | `IBeforeTransferHook` | Before transfer hooks for deployment                           |
| `expectedVaultAddress` | `address`             | Expected vault address to check against deployed vault address |

**Returns**

| Name            | Type      | Description            |
| --------------- | --------- | ---------------------- |
| `deployedVault` | `address` | Deployed vault address |

### getERC20Name

Get the ERC20 name of vault units

```solidity
function getERC20Name() external view returns (string memory name);
```

**Returns**

| Name   | Type     | Description                       |
| ------ | -------- | --------------------------------- |
| `name` | `string` | The name of the vault ERC20 token |

### getERC20Symbol

Get the ERC20 symbol of vault units

```solidity
function getERC20Symbol() external view returns (string memory symbol);
```

**Returns**

| Name     | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| `symbol` | `string` | The symbol of the vault ERC20 token |

### multiDepositorVaultParameters

Get the vault parameters

```solidity
function multiDepositorVaultParameters() external view returns (IBeforeTransferHook beforeTransferHook);
```

**Returns**

| Name                 | Type                  | Description                                  |
| -------------------- | --------------------- | -------------------------------------------- |
| `beforeTransferHook` | `IBeforeTransferHook` | The hooks called before vault unit transfers |

## Events

### VaultCreated

Emitted when the vault is created

```solidity
event VaultCreated(
    address indexed vault,
    address indexed owner,
    address hooks,
    ERC20Parameters erc20Params,
    FeeVaultParameters feeVaultParams,
    IBeforeTransferHook beforeTransferHook,
    string description
);
```

**Parameters**

| Name                 | Type                  | Description           |
| -------------------- | --------------------- | --------------------- |
| `vault`              | `address`             | Vault address         |
| `owner`              | `address`             | Initial owner address |
| `hooks`              | `address`             | Vault hooks address   |
| `erc20Params`        | `ERC20Parameters`     | ERC20 parameters      |
| `feeVaultParams`     | `FeeVaultParameters`  | Fee vault parameters  |
| `beforeTransferHook` | `IBeforeTransferHook` | Before transfer hooks |
| `description`        | `string`              | Vault description     |

## Errors

### Aera\_\_ZeroAddressDeployDelegate

Thrown when deploy delegate is the zero address

```solidity
error Aera__ZeroAddressDeployDelegate();
```


# IPriceAndFeeCalculator

Interface for the unit price provider

## Functions

### setInitialPrice

Set the initial price state for the vault

```solidity
function setInitialPrice(address vault, uint128 price, uint32 timestamp) external;
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | New unit price                        |
| `timestamp` | `uint32`  | Timestamp when the price was measured |

### setThresholds

Set vault thresholds

```solidity
function setThresholds(
    address vault,
    uint16 minPriceToleranceRatio,
    uint16 maxPriceToleranceRatio,
    uint16 minUpdateIntervalMinutes,
    uint8 maxPriceAge,
    uint8 maxUpdateDelayDays
) external;
```

**Parameters**

| Name                       | Type      | Description                                                                |
| -------------------------- | --------- | -------------------------------------------------------------------------- |
| `vault`                    | `address` | Address of the vault                                                       |
| `minPriceToleranceRatio`   | `uint16`  | Minimum ratio (of a price decrease) in basis points                        |
| `maxPriceToleranceRatio`   | `uint16`  | Maximum ratio (of a price increase) in basis points                        |
| `minUpdateIntervalMinutes` | `uint16`  | The minimum interval between updates in minutes                            |
| `maxPriceAge`              | `uint8`   | Max delay between when a vault was priced and when the price is acceptable |
| `maxUpdateDelayDays`       | `uint8`   | Max delay between two price updates                                        |

### setUnitPrice

Set the unit price for the vault in numeraire terms

```solidity
function setUnitPrice(address vault, uint128 price, uint32 timestamp) external;
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | New unit price                        |
| `timestamp` | `uint32`  | Timestamp when the price was measured |

### pauseVault

Pause the vault

```solidity
function pauseVault(address vault) external;
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

### unpauseVault

Unpause the vault

*MUST revert if price or timestamp don't exactly match last update*

```solidity
function unpauseVault(address vault, uint128 price, uint32 timestamp) external;
```

**Parameters**

| Name        | Type      | Description                           |
| ----------- | --------- | ------------------------------------- |
| `vault`     | `address` | Address of the vault                  |
| `price`     | `uint128` | Expected price of the last update     |
| `timestamp` | `uint32`  | Expected timestamp of the last update |

### resetHighestPrice

Resets the highest price for a vault to the current price

```solidity
function resetHighestPrice(address vault) external;
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

### convertUnitsToToken

Convert units to token amount

```solidity
function convertUnitsToToken(address vault, IERC20 token, uint256 unitsAmount)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

### convertUnitsToTokenIfActive

Convert units to token amount if vault is not paused

*MUST revert if vault is paused*

```solidity
function convertUnitsToTokenIfActive(address vault, IERC20 token, uint256 unitsAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 tokenAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `unitsAmount` | `uint256`       | Amount of units      |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description      |
| ------------- | --------- | ---------------- |
| `tokenAmount` | `uint256` | Amount of tokens |

### convertTokenToUnits

Convert token amount to units

```solidity
function convertTokenToUnits(address vault, IERC20 token, uint256 tokenAmount)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `token`       | `IERC20`  | Address of the token |
| `tokenAmount` | `uint256` | Amount of tokens     |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

### convertTokenToUnitsIfActive

Convert token amount to units if vault is not paused

*MUST revert if vault is paused*

```solidity
function convertTokenToUnitsIfActive(address vault, IERC20 token, uint256 tokenAmount, Math.Rounding rounding)
    external
    view
    returns (uint256 unitsAmount);
```

**Parameters**

| Name          | Type            | Description          |
| ------------- | --------------- | -------------------- |
| `vault`       | `address`       | Address of the vault |
| `token`       | `IERC20`        | Address of the token |
| `tokenAmount` | `uint256`       | Amount of tokens     |
| `rounding`    | `Math.Rounding` | The rounding mode    |

**Returns**

| Name          | Type      | Description     |
| ------------- | --------- | --------------- |
| `unitsAmount` | `uint256` | Amount of units |

### convertUnitsToNumeraire

Convert units to numeraire token amount

```solidity
function convertUnitsToNumeraire(address vault, uint256 unitsAmount) external view returns (uint256 numeraireAmount);
```

**Parameters**

| Name          | Type      | Description          |
| ------------- | --------- | -------------------- |
| `vault`       | `address` | Address of the vault |
| `unitsAmount` | `uint256` | Amount of units      |

**Returns**

| Name              | Type      | Description         |
| ----------------- | --------- | ------------------- |
| `numeraireAmount` | `uint256` | Amount of numeraire |

### getVaultState

Return the state of the vault

```solidity
function getVaultState(address vault) external view returns (VaultPriceState memory, VaultAccruals memory);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type              | Description                                   |
| -------- | ----------------- | --------------------------------------------- |
| `<none>` | `VaultPriceState` | vaultPriceState The price state of the vault  |
| `<none>` | `VaultAccruals`   | vaultAccruals The accruals state of the vault |

### getVaultsPriceAge

Returns the age of the last submitted price for a vault

```solidity
function getVaultsPriceAge(address vault) external view returns (uint256);
```

**Parameters**

| Name    | Type      | Description          |
| ------- | --------- | -------------------- |
| `vault` | `address` | Address of the vault |

**Returns**

| Name     | Type      | Description                                                                      |
| -------- | --------- | -------------------------------------------------------------------------------- |
| `<none>` | `uint256` | priceAge The difference between block.timestamp and vault's unit price timestamp |

### isVaultPaused

Check if a vault is paused

```solidity
function isVaultPaused(address vault) external view returns (bool);
```

**Parameters**

| Name    | Type      | Description              |
| ------- | --------- | ------------------------ |
| `vault` | `address` | The address of the vault |

**Returns**

| Name     | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `<none>` | `bool` | True if the vault is paused, false otherwise |

## Events

### ThresholdsSet

Emitted when thresholds are set for a vault

```solidity
event ThresholdsSet(
    address indexed vault,
    uint16 minPriceToleranceRatio,
    uint16 maxPriceToleranceRatio,
    uint16 minUpdateIntervalMinutes,
    uint8 maxPriceAge
);
```

**Parameters**

| Name                       | Type      | Description                                                                |
| -------------------------- | --------- | -------------------------------------------------------------------------- |
| `vault`                    | `address` | The address of the vault                                                   |
| `minPriceToleranceRatio`   | `uint16`  | Minimum ratio (of a price decrease) in basis points                        |
| `maxPriceToleranceRatio`   | `uint16`  | Maximum ratio (of a price increase) in basis points                        |
| `minUpdateIntervalMinutes` | `uint16`  | The minimum interval between updates in minutes                            |
| `maxPriceAge`              | `uint8`   | Max delay between when a vault was priced and when the price is acceptable |

### UnitPriceUpdated

Emitted when a vault's unit price is updated

```solidity
event UnitPriceUpdated(address indexed vault, uint128 price, uint32 timestamp);
```

**Parameters**

| Name        | Type      | Description                              |
| ----------- | --------- | ---------------------------------------- |
| `vault`     | `address` | The address of the vault                 |
| `price`     | `uint128` | The new unit price                       |
| `timestamp` | `uint32`  | The timestamp when the price was updated |

### VaultPausedChanged

Emitted when a vault's paused state is changed

```solidity
event VaultPausedChanged(address indexed vault, bool paused);
```

**Parameters**

| Name     | Type      | Description                 |
| -------- | --------- | --------------------------- |
| `vault`  | `address` | The address of the vault    |
| `paused` | `bool`    | Whether the vault is paused |

### HighestPriceReset

Emitted when a vault's highest price is reset

```solidity
event HighestPriceReset(address indexed vault, uint128 newHighestPrice);
```

**Parameters**

| Name              | Type      | Description              |
| ----------------- | --------- | ------------------------ |
| `vault`           | `address` | The address of the vault |
| `newHighestPrice` | `uint128` | The new highest price    |

## Errors

### Aera\_\_StalePrice

```solidity
error Aera__StalePrice();
```

### Aera\_\_TimestampMustBeAfterLastUpdate

```solidity
error Aera__TimestampMustBeAfterLastUpdate();
```

### Aera\_\_TimestampCantBeInFuture

```solidity
error Aera__TimestampCantBeInFuture();
```

### Aera\_\_ZeroAddressOracleRegistry

```solidity
error Aera__ZeroAddressOracleRegistry();
```

### Aera\_\_InvalidMaxPriceToleranceRatio

```solidity
error Aera__InvalidMaxPriceToleranceRatio();
```

### Aera\_\_InvalidMinPriceToleranceRatio

```solidity
error Aera__InvalidMinPriceToleranceRatio();
```

### Aera\_\_InvalidMaxPriceAge

```solidity
error Aera__InvalidMaxPriceAge();
```

### Aera\_\_InvalidMaxUpdateDelayDays

```solidity
error Aera__InvalidMaxUpdateDelayDays();
```

### Aera\_\_ThresholdNotSet

```solidity
error Aera__ThresholdNotSet();
```

### Aera\_\_VaultPaused

```solidity
error Aera__VaultPaused();
```

### Aera\_\_VaultNotPaused

```solidity
error Aera__VaultNotPaused();
```

### Aera\_\_UnitPriceMismatch

```solidity
error Aera__UnitPriceMismatch();
```

### Aera\_\_TimestampMismatch

```solidity
error Aera__TimestampMismatch();
```

### Aera\_\_VaultAlreadyInitialized

```solidity
error Aera__VaultAlreadyInitialized();
```

### Aera\_\_VaultNotInitialized

```solidity
error Aera__VaultNotInitialized();
```

### Aera\_\_InvalidPrice

```solidity
error Aera__InvalidPrice();
```

### Aera\_\_CurrentPriceAboveHighestPrice

```solidity
error Aera__CurrentPriceAboveHighestPrice();
```


# IProvisioner

Interface for the contract that can mint and burn vault units in exchange for tokens

## Functions

### deposit

Deposit tokens directly into the vault

*MUST revert if tokensIn is 0, minUnitsOut is 0, or sync deposits are disabled*

```solidity
function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut) external returns (uint256 unitsOut);
```

**Parameters**

| Name          | Type      | Description                          |
| ------------- | --------- | ------------------------------------ |
| `token`       | `IERC20`  | The token to deposit                 |
| `tokensIn`    | `uint256` | The amount of tokens to deposit      |
| `minUnitsOut` | `uint256` | The minimum amount of units expected |

**Returns**

| Name       | Type      | Description                                 |
| ---------- | --------- | ------------------------------------------- |
| `unitsOut` | `uint256` | The amount of shares minted to the receiver |

### mint

Mint exact amount of units by depositing required tokens

```solidity
function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn) external returns (uint256 tokensIn);
```

**Parameters**

| Name          | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `token`       | `IERC20`  | The token to deposit                        |
| `unitsOut`    | `uint256` | The exact amount of units to mint           |
| `maxTokensIn` | `uint256` | Maximum amount of tokens willing to deposit |

**Returns**

| Name       | Type      | Description                                            |
| ---------- | --------- | ------------------------------------------------------ |
| `tokensIn` | `uint256` | The amount of tokens used to mint the requested shares |

### refundDeposit

Refund a deposit within the refund period

*Only callable by authorized addresses*

```solidity
function refundDeposit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, uint256 refundableUntil)
    external;
```

**Parameters**

| Name              | Type      | Description                              |
| ----------------- | --------- | ---------------------------------------- |
| `sender`          | `address` | The original depositor                   |
| `token`           | `IERC20`  | The deposited token                      |
| `tokenAmount`     | `uint256` | The amount of tokens deposited           |
| `unitsAmount`     | `uint256` | The amount of units minted               |
| `refundableUntil` | `uint256` | Timestamp until which refund is possible |

### refundRequest

Refund an expired deposit or redeem request

*Can only be called after request deadline has passed*

```solidity
function refundRequest(IERC20 token, Request calldata request) external;
```

**Parameters**

| Name      | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `token`   | `IERC20`  | The token involved in the request |
| `request` | `Request` | The request to refund             |

### requestDeposit

Create a new deposit request to be solved by solvers

```solidity
function requestDeposit(
    IERC20 token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external;
```

**Parameters**

| Name           | Type      | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| `token`        | `IERC20`  | The token to deposit                               |
| `tokensIn`     | `uint256` | The amount of tokens to deposit                    |
| `minUnitsOut`  | `uint256` | The minimum amount of units expected               |
| `solverTip`    | `uint256` | The tip offered to the solver                      |
| `deadline`     | `uint256` | Duration in seconds for which the request is valid |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use      |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request       |

### requestRedeem

Create a new redeem request to be solved by solvers

```solidity
function requestRedeem(
    IERC20 token,
    uint256 unitsIn,
    uint256 minTokensOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice
) external;
```

**Parameters**

| Name           | Type      | Description                                        |
| -------------- | --------- | -------------------------------------------------- |
| `token`        | `IERC20`  | The token to receive                               |
| `unitsIn`      | `uint256` | The amount of units to redeem                      |
| `minTokensOut` | `uint256` | The minimum amount of tokens expected              |
| `solverTip`    | `uint256` | The tip offered to the solver                      |
| `deadline`     | `uint256` | Duration in seconds for which the request is valid |
| `maxPriceAge`  | `uint256` | Maximum age of price data that solver can use      |
| `isFixedPrice` | `bool`    | Whether the request is a fixed price request       |

### solveRequestsVault

Solve multiple requests using vault's liquidity

*Only callable by authorized addresses*

```solidity
function solveRequestsVault(IERC20 token, Request[] calldata requests) external;
```

**Parameters**

| Name       | Type        | Description                           |
| ---------- | ----------- | ------------------------------------- |
| `token`    | `IERC20`    | The token for which to solve requests |
| `requests` | `Request[]` | Array of requests to solve            |

### solveRequestsDirect

Solve multiple requests using solver's own liquidity

```solidity
function solveRequestsDirect(IERC20 token, Request[] calldata requests) external;
```

**Parameters**

| Name       | Type        | Description                           |
| ---------- | ----------- | ------------------------------------- |
| `token`    | `IERC20`    | The token for which to solve requests |
| `requests` | `Request[]` | Array of requests to solve            |

### setTokenDetails

Update token parameters

```solidity
function setTokenDetails(IERC20 token, TokenDetails calldata tokensDetails) external;
```

**Parameters**

| Name            | Type           | Description           |
| --------------- | -------------- | --------------------- |
| `token`         | `IERC20`       | The token to update   |
| `tokensDetails` | `TokenDetails` | The new token details |

### removeToken

Removes token from provisioner

```solidity
function removeToken(IERC20 token) external;
```

**Parameters**

| Name    | Type     | Description             |
| ------- | -------- | ----------------------- |
| `token` | `IERC20` | The token to be removed |

### setDepositDetails

Update deposit parameters

```solidity
function setDepositDetails(uint256 depositCap_, uint256 depositRefundTimeout_) external;
```

**Parameters**

| Name                    | Type      | Description                                   |
| ----------------------- | --------- | --------------------------------------------- |
| `depositCap_`           | `uint256` | New maximum total value that can be deposited |
| `depositRefundTimeout_` | `uint256` | New time window for deposit refunds           |

### maxDeposit

Return maximum amount that can still be deposited

```solidity
function maxDeposit() external view returns (uint256);
```

**Returns**

| Name     | Type      | Description                          |
| -------- | --------- | ------------------------------------ |
| `<none>` | `uint256` | Amount of deposit capacity remaining |

### areUserUnitsLocked

Check if a user's units are currently locked

```solidity
function areUserUnitsLocked(address user) external view returns (bool);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `user` | `address` | The address to check |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if user's units are locked, false otherwise |

### getDepositHash

Computes the hash for a sync deposit

```solidity
function getDepositHash(address user, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, uint256 refundableUntil)
    external
    pure
    returns (bytes32);
```

**Parameters**

| Name              | Type      | Description                                         |
| ----------------- | --------- | --------------------------------------------------- |
| `user`            | `address` | The address making the deposit                      |
| `token`           | `IERC20`  | The token being deposited                           |
| `tokenAmount`     | `uint256` | The amount of tokens to deposit                     |
| `unitsAmount`     | `uint256` | Minimum amount of units to receive                  |
| `refundableUntil` | `uint256` | The timestamp until which the deposit is refundable |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the deposit |

### getRequestHash

Computes the hash for a generic request

```solidity
function getRequestHash(IERC20 token, Request calldata request) external pure returns (bytes32);
```

**Parameters**

| Name      | Type      | Description                       |
| --------- | --------- | --------------------------------- |
| `token`   | `IERC20`  | The token involved in the request |
| `request` | `Request` | The request struct                |

**Returns**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `<none>` | `bytes32` | The hash of the request |

## Events

### Deposited

Emitted when a user deposits tokens directly into the vault

```solidity
event Deposited(address indexed user, IERC20 indexed token, uint256 tokensIn, uint256 unitsOut, bytes32 depositHash);
```

**Parameters**

| Name          | Type      | Description                        |
| ------------- | --------- | ---------------------------------- |
| `user`        | `address` | The address of the depositor       |
| `token`       | `IERC20`  | The token being deposited          |
| `tokensIn`    | `uint256` | The amount of tokens deposited     |
| `unitsOut`    | `uint256` | The amount of units minted         |
| `depositHash` | `bytes32` | Unique identifier for this deposit |

### DepositRefunded

Emitted when a deposit is refunded

```solidity
event DepositRefunded(bytes32 indexed depositHash);
```

**Parameters**

| Name          | Type      | Description                            |
| ------------- | --------- | -------------------------------------- |
| `depositHash` | `bytes32` | The hash of the deposit being refunded |

### DirectDepositRefunded

Emitted when a direct (sync) deposit is refunded

```solidity
event DirectDepositRefunded(bytes32 indexed depositHash);
```

**Parameters**

| Name          | Type      | Description                            |
| ------------- | --------- | -------------------------------------- |
| `depositHash` | `bytes32` | The hash of the deposit being refunded |

### DepositRequested

Emitted when a user creates a deposit request

```solidity
event DepositRequested(
    address indexed user,
    IERC20 indexed token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    bytes32 depositRequestHash
);
```

**Parameters**

| Name                 | Type      | Description                                          |
| -------------------- | --------- | ---------------------------------------------------- |
| `user`               | `address` | The address requesting the deposit                   |
| `token`              | `IERC20`  | The token being deposited                            |
| `tokensIn`           | `uint256` | The amount of tokens to deposit                      |
| `minUnitsOut`        | `uint256` | The minimum amount of units expected                 |
| `solverTip`          | `uint256` | The tip offered to the solver in deposit token terms |
| `deadline`           | `uint256` | Timestamp until which the request is valid           |
| `maxPriceAge`        | `uint256` | Maximum age of price data that solver can use        |
| `isFixedPrice`       | `bool`    | Whether the request is a fixed price request         |
| `depositRequestHash` | `bytes32` | The hash of the deposit request                      |

### RedeemRequested

Emitted when a user creates a redeem request

```solidity
event RedeemRequested(
    address indexed user,
    IERC20 indexed token,
    uint256 minTokensOut,
    uint256 unitsIn,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    bytes32 redeemRequestHash
);
```

**Parameters**

| Name                | Type      | Description                                              |
| ------------------- | --------- | -------------------------------------------------------- |
| `user`              | `address` | The address requesting the redemption                    |
| `token`             | `IERC20`  | The token requested in return for units                  |
| `minTokensOut`      | `uint256` | The minimum amount of tokens the user expects to receive |
| `unitsIn`           | `uint256` | The amount of units being redeemed                       |
| `solverTip`         | `uint256` | The tip offered to the solver in redeem token terms      |
| `deadline`          | `uint256` | The timestamp until which this request is valid          |
| `maxPriceAge`       | `uint256` | Maximum age of price data that solver can use            |
| `isFixedPrice`      | `bool`    | Whether the request is a fixed price request             |
| `redeemRequestHash` | `bytes32` | The hash of the redeem request                           |

### DepositSolved

Emitted when a deposit request is solved successfully

```solidity
event DepositSolved(bytes32 indexed depositHash);
```

**Parameters**

| Name          | Type      | Description                                                  |
| ------------- | --------- | ------------------------------------------------------------ |
| `depositHash` | `bytes32` | The unique identifier of the deposit request that was solved |

### RedeemSolved

Emitted when a redeem request is solved successfully

```solidity
event RedeemSolved(bytes32 indexed redeemHash);
```

**Parameters**

| Name         | Type      | Description                                                 |
| ------------ | --------- | ----------------------------------------------------------- |
| `redeemHash` | `bytes32` | The unique identifier of the redeem request that was solved |

### InvalidRequestHash

Emitted when an unrecognized async deposit hash is used

```solidity
event InvalidRequestHash(bytes32 indexed depositHash);
```

**Parameters**

| Name          | Type      | Description                                          |
| ------------- | --------- | ---------------------------------------------------- |
| `depositHash` | `bytes32` | The deposit hash that was not found in async records |

### AsyncDepositDisabled

Emitted when async deposits are disabled and a deposit request cannot be processed

```solidity
event AsyncDepositDisabled(uint256 indexed index);
```

**Parameters**

| Name    | Type      | Description                                        |
| ------- | --------- | -------------------------------------------------- |
| `index` | `uint256` | The index of the deposit request that was rejected |

### AsyncRedeemDisabled

Emitted when async redeems are disabled and a redeem request cannot be processed

```solidity
event AsyncRedeemDisabled(uint256 indexed index);
```

**Parameters**

| Name    | Type      | Description                                       |
| ------- | --------- | ------------------------------------------------- |
| `index` | `uint256` | The index of the redeem request that was rejected |

### PriceAgeExceeded

Emitted when the price age exceeds the maximum allowed for a request

```solidity
event PriceAgeExceeded(uint256 indexed index);
```

**Parameters**

| Name    | Type      | Description                                |
| ------- | --------- | ------------------------------------------ |
| `index` | `uint256` | The index of the request that was rejected |

### DepositCapExceeded

Emitted when a deposit exceeds the vault's configured deposit cap

```solidity
event DepositCapExceeded(uint256 indexed index);
```

**Parameters**

| Name    | Type      | Description                                |
| ------- | --------- | ------------------------------------------ |
| `index` | `uint256` | The index of the request that was rejected |

### InsufficientTokensForTip

Emitted when there are not enough tokens to cover the required solver tip

```solidity
event InsufficientTokensForTip(uint256 indexed index);
```

**Parameters**

| Name    | Type      | Description                                |
| ------- | --------- | ------------------------------------------ |
| `index` | `uint256` | The index of the request that was rejected |

### AmountBoundExceeded

Emitted when the output units are less than the amount requested

```solidity
event AmountBoundExceeded(uint256 indexed index, uint256 amount, uint256 bound);
```

**Parameters**

| Name     | Type      | Description                                |
| -------- | --------- | ------------------------------------------ |
| `index`  | `uint256` | The index of the request that was rejected |
| `amount` | `uint256` | The actual amount                          |
| `bound`  | `uint256` | The minimum amount                         |

### RedeemRefunded

Emitted when a redeem request is refunded due to expiration or cancellation

```solidity
event RedeemRefunded(bytes32 indexed redeemHash);
```

**Parameters**

| Name         | Type      | Description                                                   |
| ------------ | --------- | ------------------------------------------------------------- |
| `redeemHash` | `bytes32` | The unique identifier of the redeem request that was refunded |

### DepositDetailsUpdated

Emitted when the vault's deposit limits are updated

```solidity
event DepositDetailsUpdated(uint256 depositCap, uint256 depositRefundTimeout);
```

**Parameters**

| Name                   | Type      | Description                                                      |
| ---------------------- | --------- | ---------------------------------------------------------------- |
| `depositCap`           | `uint256` | The new maximum total value that can be deposited into the vault |
| `depositRefundTimeout` | `uint256` | The new time window during which deposits can be refunded        |

### TokenDetailsSet

Emitted when a token's deposit/withdrawal settings are updated

```solidity
event TokenDetailsSet(IERC20 indexed token, TokenDetails tokensDetails);
```

**Parameters**

| Name            | Type           | Description                                |
| --------------- | -------------- | ------------------------------------------ |
| `token`         | `IERC20`       | The token whose settings are being updated |
| `tokensDetails` | `TokenDetails` | The new token details                      |

### TokenRemoved

Emitted when a token is removed from the provisioner

```solidity
event TokenRemoved(IERC20 indexed token);
```

**Parameters**

| Name    | Type     | Description                |
| ------- | -------- | -------------------------- |
| `token` | `IERC20` | The token that was removed |

## Errors

### Aera\_\_SyncDepositDisabled

```solidity
error Aera__SyncDepositDisabled();
```

### Aera\_\_AsyncDepositDisabled

```solidity
error Aera__AsyncDepositDisabled();
```

### Aera\_\_AsyncRedeemDisabled

```solidity
error Aera__AsyncRedeemDisabled();
```

### Aera\_\_DepositCapExceeded

```solidity
error Aera__DepositCapExceeded();
```

### Aera\_\_MinUnitsOutNotMet

```solidity
error Aera__MinUnitsOutNotMet();
```

### Aera\_\_TokensInZero

```solidity
error Aera__TokensInZero();
```

### Aera\_\_UnitsInZero

```solidity
error Aera__UnitsInZero();
```

### Aera\_\_UnitsOutZero

```solidity
error Aera__UnitsOutZero();
```

### Aera\_\_MinUnitsOutZero

```solidity
error Aera__MinUnitsOutZero();
```

### Aera\_\_MaxTokensInZero

```solidity
error Aera__MaxTokensInZero();
```

### Aera\_\_MaxTokensInExceeded

```solidity
error Aera__MaxTokensInExceeded();
```

### Aera\_\_MaxDepositRefundTimeoutExceeded

```solidity
error Aera__MaxDepositRefundTimeoutExceeded();
```

### Aera\_\_DepositHashNotFound

```solidity
error Aera__DepositHashNotFound();
```

### Aera\_\_HashNotFound

```solidity
error Aera__HashNotFound();
```

### Aera\_\_RefundPeriodExpired

```solidity
error Aera__RefundPeriodExpired();
```

### Aera\_\_DeadlineInPast

```solidity
error Aera__DeadlineInPast();
```

### Aera\_\_DeadlineTooFarInFuture

```solidity
error Aera__DeadlineTooFarInFuture();
```

### Aera\_\_DeadlineInFutureAndUnauthorized

```solidity
error Aera__DeadlineInFutureAndUnauthorized();
```

### Aera\_\_MinTokenOutZero

```solidity
error Aera__MinTokenOutZero();
```

### Aera\_\_HashCollision

```solidity
error Aera__HashCollision();
```

### Aera\_\_ZeroAddressPriceAndFeeCalculator

```solidity
error Aera__ZeroAddressPriceAndFeeCalculator();
```

### Aera\_\_ZeroAddressMultiDepositorVault

```solidity
error Aera__ZeroAddressMultiDepositorVault();
```

### Aera\_\_DepositMultiplierTooLow

```solidity
error Aera__DepositMultiplierTooLow();
```

### Aera\_\_DepositMultiplierTooHigh

```solidity
error Aera__DepositMultiplierTooHigh();
```

### Aera\_\_RedeemMultiplierTooLow

```solidity
error Aera__RedeemMultiplierTooLow();
```

### Aera\_\_RedeemMultiplierTooHigh

```solidity
error Aera__RedeemMultiplierTooHigh();
```

### Aera\_\_DepositCapZero

```solidity
error Aera__DepositCapZero();
```

### Aera\_\_PriceAndFeeCalculatorVaultPaused

```solidity
error Aera__PriceAndFeeCalculatorVaultPaused();
```

### Aera\_\_AutoPriceSolveNotAllowed

```solidity
error Aera__AutoPriceSolveNotAllowed();
```

### Aera\_\_FixedPriceSolverTipNotAllowed

```solidity
error Aera__FixedPriceSolverTipNotAllowed();
```

### Aera\_\_TokenCantBePriced

```solidity
error Aera__TokenCantBePriced();
```

### Aera\_\_CallerIsVault

```solidity
error Aera__CallerIsVault();
```

### Aera\_\_InvalidToken

```solidity
error Aera__InvalidToken();
```


# ISingleDepositorVault

**Inherits:** IFeeVault

Interface for vaults that accept deposits/withdrawals from a single address

## Functions

### deposit

Deposit assets into the vault

```solidity
function deposit(TokenAmount[] calldata tokenAmounts) external;
```

**Parameters**

| Name           | Type            | Description           |
| -------------- | --------------- | --------------------- |
| `tokenAmounts` | `TokenAmount[]` | The assets to deposit |

### withdraw

Withdraw assets from the vault

```solidity
function withdraw(TokenAmount[] calldata tokenAmounts) external;
```

**Parameters**

| Name           | Type            | Description            |
| -------------- | --------------- | ---------------------- |
| `tokenAmounts` | `TokenAmount[]` | The assets to withdraw |

### execute

Execute operations on the vault as a trusted entity

```solidity
function execute(OperationPayable[] calldata operations) external;
```

**Parameters**

| Name         | Type                 | Description               |
| ------------ | -------------------- | ------------------------- |
| `operations` | `OperationPayable[]` | The operations to execute |

## Events

### Deposited

Emitted when tokens are deposited into the vault

```solidity
event Deposited(address indexed depositor, TokenAmount[] tokenAmounts);
```

**Parameters**

| Name           | Type            | Description                      |
| -------------- | --------------- | -------------------------------- |
| `depositor`    | `address`       | The address making the deposit   |
| `tokenAmounts` | `TokenAmount[]` | The tokens and amounts deposited |

### Withdrawn

Emitted when tokens are withdrawn from the vault

```solidity
event Withdrawn(address indexed withdrawer, TokenAmount[] tokenAmounts);
```

**Parameters**

| Name           | Type            | Description                       |
| -------------- | --------------- | --------------------------------- |
| `withdrawer`   | `address`       | The address making the withdrawal |
| `tokenAmounts` | `TokenAmount[]` | The tokens and amounts withdrawn  |

### Executed

Emitted when operations are executed

```solidity
event Executed(address indexed executor, OperationPayable[] operations);
```

**Parameters**

| Name         | Type                 | Description                          |
| ------------ | -------------------- | ------------------------------------ |
| `executor`   | `address`            | The address executing the operations |
| `operations` | `OperationPayable[]` | The operations that were executed    |

## Errors

### Aera\_\_ExecutionFailed

```solidity
error Aera__ExecutionFailed(uint256 index, bytes result);
```

### Aera\_\_UnexpectedTokenAllowance

```solidity
error Aera__UnexpectedTokenAllowance(uint256 allowance);
```


# ISubmitHooks

Interface for hooks that execute before and after submit calls

## Functions

### beforeSubmit

Called before a submit

```solidity
function beforeSubmit(bytes memory data, address guardian) external;
```

**Parameters**

| Name       | Type      | Description                            |
| ---------- | --------- | -------------------------------------- |
| `data`     | `bytes`   | Encoded data of the submit             |
| `guardian` | `address` | Address of the guardian that submitted |

### afterSubmit

Called after a submit

```solidity
function afterSubmit(bytes memory data, address guardian) external;
```

**Parameters**

| Name       | Type      | Description                            |
| ---------- | --------- | -------------------------------------- |
| `data`     | `bytes`   | Encoded data of the submit             |
| `guardian` | `address` | Address of the guardian that submitted |


# ISingleDepositorVaultFactory

**Inherits:** IBaseVaultDeployer, IFeeVaultDeployer

Interface for the single-depositor vault factory

## Functions

### create

Create single-depositor vault

```solidity
function create(
    bytes32 salt,
    string calldata description,
    BaseVaultParameters calldata baseVaultParams,
    FeeVaultParameters calldata singleDepositorVaultParams,
    address expectedVaultAddress
) external returns (address deployedVault);
```

**Parameters**

| Name                         | Type                  | Description                                                         |
| ---------------------------- | --------------------- | ------------------------------------------------------------------- |
| `salt`                       | `bytes32`             | The salt used to generate the vault address                         |
| `description`                | `string`              | Vault description                                                   |
| `baseVaultParams`            | `BaseVaultParameters` | Base vault parameters for deployment                                |
| `singleDepositorVaultParams` | `FeeVaultParameters`  | Parameters for deployment related to single depositor functionality |
| `expectedVaultAddress`       | `address`             | Expected vault address to check against deployed vault address      |

**Returns**

| Name            | Type      | Description            |
| --------------- | --------- | ---------------------- |
| `deployedVault` | `address` | Deployed vault address |

## Events

### VaultCreated

Emitted when the vault is created

```solidity
event VaultCreated(
    address indexed vault,
    address indexed owner,
    address submitHooks,
    IERC20 feeToken,
    IFeeCalculator feeCalculator,
    address feeRecipient,
    string description
);
```

**Parameters**

| Name            | Type             | Description            |
| --------------- | ---------------- | ---------------------- |
| `vault`         | `address`        | Vault address          |
| `owner`         | `address`        | Initial owner address  |
| `submitHooks`   | `address`        | Submit hooks address   |
| `feeToken`      | `IERC20`         | Fee token address      |
| `feeCalculator` | `IFeeCalculator` | Fee calculator address |
| `feeRecipient`  | `address`        | Fee recipient address  |
| `description`   | `string`         | Vault description      |

## Errors

### Aera\_\_ZeroAddressDeployDelegate

Thrown when deploy delegate is the zero address

```solidity
error Aera__ZeroAddressDeployDelegate();
```


# ISweepable

Interface for contracts that can recover tokens to a designated recipient

## Functions

### sweep

Withdraw any tokens accidentally sent to contract

```solidity
function sweep(address token, uint256 amount) external;
```

**Parameters**

| Name     | Type      | Description                                                            |
| -------- | --------- | ---------------------------------------------------------------------- |
| `token`  | `address` | Token address to withdraw or zero address for the chain's native token |
| `amount` | `uint256` | Amount to withdraw                                                     |

## Events

### Sweep

Emitted when sweep is called

```solidity
event Sweep(address indexed token, uint256 amount);
```

**Parameters**

| Name     | Type      | Description                                                          |
| -------- | --------- | -------------------------------------------------------------------- |
| `token`  | `address` | Token address or zero address if recovering the chain's native token |
| `amount` | `uint256` | Withdrawn amount of token                                            |

## Errors

### Aera\_\_FailedToSendNativeToken

Emitted when sweep of the native token has failed

```solidity
error Aera__FailedToSendNativeToken();
```


# IVaultDeployDelegate

Interface for the VaultDeployDelegate

## Functions

### createVault

Deploy a new vault

```solidity
function createVault(bytes32 salt) external returns (address);
```

**Parameters**

| Name   | Type      | Description                    |
| ------ | --------- | ------------------------------ |
| `salt` | `bytes32` | The salt value to create vault |

**Returns**

| Name     | Type      | Description                     |
| -------- | --------- | ------------------------------- |
| `<none>` | `address` | deployed Deployed vault address |


# IWhitelist

Interface for managing address whitelisting

## Functions

### setWhitelisted

Set the address whitelisted status

```solidity
function setWhitelisted(address addr, bool isAddressWhitelisted) external;
```

**Parameters**

| Name                   | Type      | Description                                         |
| ---------------------- | --------- | --------------------------------------------------- |
| `addr`                 | `address` | The address to add/remove from the whitelist        |
| `isAddressWhitelisted` | `bool`    | Whether address should be whitelisted going forward |

### isWhitelisted

Checks if the address is whitelisted

```solidity
function isWhitelisted(address addr) external view returns (bool);
```

**Parameters**

| Name   | Type      | Description          |
| ------ | --------- | -------------------- |
| `addr` | `address` | The address to check |

**Returns**

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| `<none>` | `bool` | True if the addr is whitelisted, false otherwise |

### getAllWhitelisted

Get all whitelisted addresses

```solidity
function getAllWhitelisted() external view returns (address[] memory);
```

**Returns**

| Name     | Type        | Description                           |
| -------- | ----------- | ------------------------------------- |
| `<none>` | `address[]` | An array of all whitelisted addresses |

## Events

### WhitelistSet

Emitted when an address whitelist status is updated

```solidity
event WhitelistSet(address indexed addr, bool isAddressWhitelisted);
```

**Parameters**

| Name                   | Type      | Description                                   |
| ---------------------- | --------- | --------------------------------------------- |
| `addr`                 | `address` | The address whose whitelist status is updated |
| `isAddressWhitelisted` | `bool`    | Whether the address is whitelisted            |


# CalldataExtractor

Library for extracting specific chunks of calldata based on configured offsets used in configurable hooks to extract 32 byte chunks from calldata and check them against expected values in the merkle tree

## Functions

### extract

Extract 32-byte chunks from calldata based on config offsets

*Number of provided offsets must be <= 16 because that's how many fit in uint256*

*Calldata must be at least 36 bytes long to be considered valid*

*All math is unchecked because we validate everything before doing any operations*

```solidity
function extract(bytes memory callData, uint256 calldataOffsetsPacked, uint256 calldataOffsetsCount)
    internal
    pure
    returns (bytes memory);
```

**Parameters**

| Name                    | Type      | Description                      |
| ----------------------- | --------- | -------------------------------- |
| `callData`              | `bytes`   | The calldata to extract from     |
| `calldataOffsetsPacked` | `uint256` | Packed 16-bit extraction offsets |
| `calldataOffsetsCount`  | `uint256` | Number of extractions to perform |

**Returns**

| Name     | Type    | Description                                         |
| -------- | ------- | --------------------------------------------------- |
| `<none>` | `bytes` | result Concatenated byte values at specific offsets |

## Errors

### Aera\_\_ExtractionNumberTooLarge

```solidity
error Aera__ExtractionNumberTooLarge();
```

### Aera\_\_CalldataTooShort

```solidity
error Aera__CalldataTooShort();
```

### Aera\_\_OffsetOutOfBounds

```solidity
error Aera__OffsetOutOfBounds();
```


# CalldataReaderLib

**Authors:** Aera <https://github.com/aera-finance>, philogy <https://github.com/philogy>

Modified version of the original CalldataReaderLib

No functions were changed, only added new functions

## Functions

### from

```solidity
function from(bytes calldata data) internal pure returns (CalldataReader reader);
```

### requireAtEndOf

```solidity
function requireAtEndOf(CalldataReader self, bytes calldata data) internal pure;
```

### requireAtEndOf

```solidity
function requireAtEndOf(CalldataReader self, CalldataReader end) internal pure;
```

### offset

```solidity
function offset(CalldataReader self) internal pure returns (uint256);
```

### readBool

```solidity
function readBool(CalldataReader self) internal pure returns (CalldataReader, bool value);
```

### readU8

```solidity
function readU8(CalldataReader self) internal pure returns (CalldataReader, uint8 value);
```

### readU16

```solidity
function readU16(CalldataReader self) internal pure returns (CalldataReader, uint16 value);
```

### readU32

```solidity
function readU32(CalldataReader self) internal pure returns (CalldataReader, uint32 value);
```

### readI24

```solidity
function readI24(CalldataReader self) internal pure returns (CalldataReader, int24 value);
```

### readU40

```solidity
function readU40(CalldataReader self) internal pure returns (CalldataReader, uint40 value);
```

### readU64

```solidity
function readU64(CalldataReader self) internal pure returns (CalldataReader, uint64 value);
```

### readU128

```solidity
function readU128(CalldataReader self) internal pure returns (CalldataReader, uint128 value);
```

### readAddr

```solidity
function readAddr(CalldataReader self) internal pure returns (CalldataReader, address addr);
```

### readU256

```solidity
function readU256(CalldataReader self) internal pure returns (CalldataReader, uint256 value);
```

### readU24End

```solidity
function readU24End(CalldataReader self) internal pure returns (CalldataReader, CalldataReader end);
```

### readBytes

```solidity
function readBytes(CalldataReader self) internal pure returns (CalldataReader, bytes calldata slice);
```

### readU208

ADDED BY AERA

```solidity
function readU208(CalldataReader self) internal pure returns (CalldataReader, uint208 value);
```

### readOptionalU256

```solidity
function readOptionalU256(CalldataReader reader) internal pure returns (CalldataReader, uint256 u256);
```

### readBytes32Array

```solidity
function readBytes32Array(CalldataReader self) internal pure returns (CalldataReader, bytes32[] memory array);
```

### readBytesEnd

```solidity
function readBytesEnd(CalldataReader self) internal pure returns (CalldataReader end);
```

### readBytesEnd

```solidity
function readBytesEnd(CalldataReader self, bytes calldata data) internal pure returns (CalldataReader end);
```

### readBytesToMemory

```solidity
function readBytesToMemory(CalldataReader self) internal pure returns (CalldataReader, bytes memory data);
```

### readBytesToMemory

```solidity
function readBytesToMemory(CalldataReader self, uint256 length)
    internal
    pure
    returns (CalldataReader, bytes memory data);
```

## Errors

### ReaderNotAtEnd

```solidity
error ReaderNotAtEnd();
```


# Pipeline

Library for handling pipeline operations that copy and paste data between operations

*Uses bit manipulation and assembly for efficient data movement*

## Functions

### pipe

Process pipeline operations by copying data between operations

```solidity
function pipe(bytes memory data, CalldataReader reader, bytes[] memory results)
    internal
    pure
    returns (CalldataReader);
```

**Parameters**

| Name      | Type             | Description                                      |
| --------- | ---------------- | ------------------------------------------------ |
| `data`    | `bytes`          | The calldata to modify                           |
| `reader`  | `CalldataReader` | Current position in the calldata                 |
| `results` | `bytes[]`        | Array of previous operation results to copy from |

**Returns**

| Name     | Type             | Description                                                          |
| -------- | ---------------- | -------------------------------------------------------------------- |
| `<none>` | `CalldataReader` | Updated CalldataReader position after processing pipeline operations |

## Errors

### Aera\_\_CopyOffsetOutOfBounds

Thrown when trying to copy from an invalid position in source data

```solidity
error Aera__CopyOffsetOutOfBounds();
```

### Aera\_\_PasteOffsetOutOfBounds

Thrown when trying to paste to an invalid position in target data

```solidity
error Aera__PasteOffsetOutOfBounds();
```


# Periphery


# Executor

**Inherits:** IExecutor, ReentrancyGuard

Abstract contract for executing operations

*A similar version of this contract was previously audited*

*See: <https://github.com/aera-finance/aera-contracts-public/blob/main/v2/periphery/Executor.sol>*

## Functions

### execute

Execute arbitrary actions

```solidity
function execute(OperationPayable[] calldata operations) external nonReentrant;
```

**Parameters**

| Name         | Type                 | Description               |
| ------------ | -------------------- | ------------------------- |
| `operations` | `OperationPayable[]` | The operations to execute |

### \_executeOperation

Execute a single operation

*Executes the operation and reverts if it fails*

```solidity
function _executeOperation(OperationPayable calldata operation) internal virtual;
```

**Parameters**

| Name        | Type               | Description              |
| ----------- | ------------------ | ------------------------ |
| `operation` | `OperationPayable` | The operation to execute |

### \_checkOperations

Authorize the execution of operations

*Intended to be marked by `onlyOwner` or similar access control modifier*

```solidity
function _checkOperations(OperationPayable[] calldata operations) internal view virtual;
```

**Parameters**

| Name         | Type                 | Description             |
| ------------ | -------------------- | ----------------------- |
| `operations` | `OperationPayable[]` | The operations to check |

### \_checkOperation

Authorize the execution of a single operation

```solidity
function _checkOperation(OperationPayable calldata operation) internal view virtual;
```

**Parameters**

| Name        | Type               | Description            |
| ----------- | ------------------ | ---------------------- |
| `operation` | `OperationPayable` | The operation to check |


# OracleRegistry

**Inherits:** IOracleRegistry, Auth2Step, ERC165

Canonical registry for ERC-7726-compatible price oracles Registry itself conforms to ERC-7726 (exposes `getQuote`) Owner seeds initial oracles on deploy; every subsequent oracle must be scheduled, then committed ≥ `ORACLE_UPDATE_DELAY` seconds later. A user (or its owner) may temporarily override with the pending oracle until the commit executes, enabling instant adoption if desired. Owner may disable any active oracle; `getQuote` then reverts unless a user override is in place

## State Variables

### ORACLE\_UPDATE\_DELAY

Mandatory delay (seconds) before a scheduled oracle can be committed

```solidity
uint256 public immutable ORACLE_UPDATE_DELAY;
```

### \_oracles

Registry mapping: base → quote → oracle data

```solidity
mapping(address base => mapping(address quote => OracleData oracleData)) internal _oracles;
```

### oracleOverrides

Per‑vault oracle overrides: user → base → quote → oracle

```solidity
mapping(address user => mapping(address base => mapping(address quote => IOracle))) public oracleOverrides;
```

## Functions

### requiresUserAuth

```solidity
modifier requiresUserAuth(address user);
```

### constructor

```solidity
constructor(address initialOwner, Authority initialAuthority, uint256 oracleUpdateDelay)
    Auth2Step(initialOwner, initialAuthority);
```

### addOracle

Adds an oracle for the provided base and quote assets

*MUST REVERT if not called by the authorized address*

```solidity
function addOracle(address base, address quote, IOracle oracle) external requiresAuth;
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `base`   | `address` | Base asset address  |
| `quote`  | `address` | Quote asset address |
| `oracle` | `IOracle` | Oracle to add       |

### scheduleOracleUpdate

Schedules an oracle update for the base/quote asset pair The update process is a two-step process: first, the new oracle data is set using this function; second, the update is committed using the commitOracleUpdate function

*MUST REVERT if not called by the authorized address*

```solidity
function scheduleOracleUpdate(address base, address quote, IOracle oracle) external requiresAuth;
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `base`   | `address` | Base asset address  |
| `quote`  | `address` | Quote asset address |
| `oracle` | `IOracle` | Oracle to schedule  |

### commitOracleUpdate

Commits the oracle update for the base/quote asset pair Can be called by anyone after the update process is initiated using `scheduleOracleUpdate` and the update delay has passed

*MUST REVERT if the update is not initiated*

```solidity
function commitOracleUpdate(address base, address quote) external;
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

### cancelScheduledOracleUpdate

Cancels the scheduled update for the base/quote asset pair

*MUST REVERT if not called by the authorized address*

```solidity
function cancelScheduledOracleUpdate(address base, address quote) external requiresAuth;
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

### disableOracle

Disables the oracle for the base/quote asset pair

*Performs a soft delete to forbid calling `addOracle` with the same base and quote assets and avoid front-running attack*

```solidity
function disableOracle(address base, address quote, IOracle oracle) external requiresAuth;
```

**Parameters**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `base`   | `address` | Base asset address            |
| `quote`  | `address` | Quote asset address           |
| `oracle` | `IOracle` | Oracle that is to be disabled |

### acceptPendingOracle

Allows a user to accept the pending oracle for a given base/quote pair during the delay period Can be called by the user to use the new oracle early

*MUST REVERT if the caller is not the user or its owner*

```solidity
function acceptPendingOracle(address base, address quote, address user, IOracle oracle)
    external
    requiresUserAuth(user);
```

**Parameters**

| Name     | Type      | Description                                |
| -------- | --------- | ------------------------------------------ |
| `base`   | `address` | Base asset address                         |
| `quote`  | `address` | Quote asset address                        |
| `user`   | `address` | Vault that is accepting the pending oracle |
| `oracle` | `IOracle` | Oracle that is to be accepted              |

### removeOracleOverride

Allows a user to remove the oracle override for a given base/quote pair

*MUST REVERT if the caller is not the user or its owner*

```solidity
function removeOracleOverride(address base, address quote, address user) external requiresUserAuth(user);
```

**Parameters**

| Name    | Type      | Description                             |
| ------- | --------- | --------------------------------------- |
| `base`  | `address` | Base asset address                      |
| `quote` | `address` | Quote asset address                     |
| `user`  | `address` | The vault address removing the override |

### getQuote

Returns the value of `baseAmount` of `base` in `quote` terms

*MUST round down towards 0 MUST revert with `OracleUnsupportedPair` if not capable to provide data for the specified `base` and `quote` pair MUST revert with `OracleUntrustedData` if not capable to provide data within a degree of confidence publicly specified*

```solidity
function getQuote(uint256 baseAmount, address base, address quote) external view virtual returns (uint256);
```

**Parameters**

| Name         | Type      | Description                                         |
| ------------ | --------- | --------------------------------------------------- |
| `baseAmount` | `uint256` | The amount of `base` to convert                     |
| `base`       | `address` | The asset that the user needs to know the value for |
| `quote`      | `address` | The asset in which the user needs to value the base |

**Returns**

| Name     | Type      | Description                                                      |
| -------- | --------- | ---------------------------------------------------------------- |
| `<none>` | `uint256` | quoteAmount The value of `baseAmount` of `base` in `quote` terms |

### getQuoteForUser

Returns the value of the base asset in terms of the quote asset with using the provided oracle data for the provided user (respects user-specific overrides)

```solidity
function getQuoteForUser(uint256 baseAmount, address base, address quote, address user)
    external
    view
    virtual
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description          |
| ------------ | --------- | -------------------- |
| `baseAmount` | `uint256` | Amount of base asset |
| `base`       | `address` | Base asset address   |
| `quote`      | `address` | Quote asset address  |
| `user`       | `address` | Vault address        |

**Returns**

| Name     | Type      | Description                                         |
| -------- | --------- | --------------------------------------------------- |
| `<none>` | `uint256` | value of the base asset in terms of the quote asset |

### getOracleData

Return oracle metadata for base/quote

```solidity
function getOracleData(address base, address quote) external view virtual returns (OracleData memory);
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

**Returns**

| Name     | Type         | Description      |
| -------- | ------------ | ---------------- |
| `<none>` | `OracleData` | data Oracle data |

### supportsInterface

*See {IERC165-supportsInterface}.*

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

### \_getOracleForVault

Get the oracle for a user

*Returns the current oracle if active or deprecated with no override reverts if the oracle is disabled and no override is set*

```solidity
function _getOracleForVault(address user, address base, address quote) internal view returns (IOracle);
```

**Parameters**

| Name    | Type      | Description                            |
| ------- | --------- | -------------------------------------- |
| `user`  | `address` | The user address to get the oracle for |
| `base`  | `address` | The base token address                 |
| `quote` | `address` | The quote token address                |

**Returns**

| Name     | Type      | Description                            |
| -------- | --------- | -------------------------------------- |
| `<none>` | `IOracle` | The oracle instance for the given pair |

### \_validateOracle

Validate that an oracle can convert one base token to a non‑zero quote token

*Implicitly checks zero address because the getQuote call reverts*

```solidity
function _validateOracle(IOracle oracle, address base, address quote) internal view;
```

**Parameters**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `oracle` | `IOracle` | The oracle to validate  |
| `base`   | `address` | The base token address  |
| `quote`  | `address` | The quote token address |

### \_getDecimals

Determine the decimals of an asset

*Defaults to 18 if the asset is not an ERC20*

```solidity
function _getDecimals(address asset) internal view returns (uint8);
```

**Parameters**

| Name    | Type      | Description                           |
| ------- | --------- | ------------------------------------- |
| `asset` | `address` | The asset address to get decimals for |

**Returns**

| Name     | Type    | Description               |
| -------- | ------- | ------------------------- |
| `<none>` | `uint8` | The decimals of the asset |


# IExecutor

Interface for executing operations

*A similar version of this interface was previously audited*

*See: <https://github.com/aera-finance/aera-contracts-public/blob/main/v2/periphery/interfaces/IExecutor.sol>*

## Functions

### execute

Execute arbitrary actions

```solidity
function execute(OperationPayable[] calldata operations) external;
```

**Parameters**

| Name         | Type                 | Description               |
| ------------ | -------------------- | ------------------------- |
| `operations` | `OperationPayable[]` | The operations to execute |

## Events

### Executed

Emitted when operations are executed

```solidity
event Executed(address indexed caller, OperationPayable operation);
```

**Parameters**

| Name        | Type               | Description                             |
| ----------- | ------------------ | --------------------------------------- |
| `caller`    | `address`          | The address that executed the operation |
| `operation` | `OperationPayable` | The operation that was executed         |

## Errors

### AeraPeriphery\_\_ExecutionFailed

Error emitted when the execution of an operation fails

```solidity
error AeraPeriphery__ExecutionFailed(bytes result);
```

**Parameters**

| Name     | Type    | Description                                        |
| -------- | ------- | -------------------------------------------------- |
| `result` | `bytes` | The error bytes returned from the failed operation |


# IOracleRegistry

**Inherits:** IOracle

Interface for an Oracle Registry

## Functions

### addOracle

Adds an oracle for the provided base and quote assets

*MUST REVERT if not called by the authorized address*

*MUST REVERT if the oracle is already set*

```solidity
function addOracle(address base, address quote, IOracle oracle) external;
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `base`   | `address` | Base asset address  |
| `quote`  | `address` | Quote asset address |
| `oracle` | `IOracle` | Oracle to add       |

### scheduleOracleUpdate

Schedules an oracle update for the base/quote asset pair The update process is a two-step process: first, the new oracle data is set using this function; second, the update is committed using the commitOracleUpdate function

*MUST REVERT if not called by the authorized address*

*MUST REVERT if the oracle data is already scheduled for an update*

*MUST REVERT if the oracle data is the same as the current oracle*

```solidity
function scheduleOracleUpdate(address base, address quote, IOracle oracle) external;
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `base`   | `address` | Base asset address  |
| `quote`  | `address` | Quote asset address |
| `oracle` | `IOracle` | Oracle to schedule  |

### commitOracleUpdate

Commits the oracle update for the base/quote asset pair Can be called by anyone after the update process is initiated using `scheduleOracleUpdate` and the update delay has passed

*MUST REVERT if the update is not initiated*

*MUST REVERT if the update delay has not passed*

```solidity
function commitOracleUpdate(address base, address quote) external;
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

### cancelScheduledOracleUpdate

Cancels the scheduled update for the base/quote asset pair

*MUST REVERT if not called by the authorized address*

*MUST REVERT if the update is not initiated*

```solidity
function cancelScheduledOracleUpdate(address base, address quote) external;
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

### disableOracle

Disables the oracle for the base/quote asset pair

*Performs a soft delete to forbid calling `addOracle` with the same base and quote assets and avoid front-running attack*

*MUST REVERT if not called by the authorized address*

*MUST REVERT if the oracle data is not set*

*MUST REVERT if the oracle data is already disabled*

```solidity
function disableOracle(address base, address quote, IOracle oracle) external;
```

**Parameters**

| Name     | Type      | Description                   |
| -------- | --------- | ----------------------------- |
| `base`   | `address` | Base asset address            |
| `quote`  | `address` | Quote asset address           |
| `oracle` | `IOracle` | Oracle that is to be disabled |

### acceptPendingOracle

Allows a user to accept the pending oracle for a given base/quote pair during the delay period Can be called by the user to use the new oracle early

*MUST REVERT if the caller is not the user or its owner*

*MUST REVERT if the oracle is not set*

*MUST REVERT if current pending oracle doesn't match the oracle to be accepted*

```solidity
function acceptPendingOracle(address base, address quote, address user, IOracle oracle) external;
```

**Parameters**

| Name     | Type      | Description                                |
| -------- | --------- | ------------------------------------------ |
| `base`   | `address` | Base asset address                         |
| `quote`  | `address` | Quote asset address                        |
| `user`   | `address` | Vault that is accepting the pending oracle |
| `oracle` | `IOracle` | Oracle that is to be accepted              |

### removeOracleOverride

Allows a user to remove the oracle override for a given base/quote pair

*MUST REVERT if the caller is not the user or its owner*

```solidity
function removeOracleOverride(address base, address quote, address user) external;
```

**Parameters**

| Name    | Type      | Description                             |
| ------- | --------- | --------------------------------------- |
| `base`  | `address` | Base asset address                      |
| `quote` | `address` | Quote asset address                     |
| `user`  | `address` | The vault address removing the override |

### getQuoteForUser

Returns the value of the base asset in terms of the quote asset with using the provided oracle data for the provided user (respects user-specific overrides)

```solidity
function getQuoteForUser(uint256 baseAmount, address base, address quote, address user)
    external
    view
    returns (uint256);
```

**Parameters**

| Name         | Type      | Description          |
| ------------ | --------- | -------------------- |
| `baseAmount` | `uint256` | Amount of base asset |
| `base`       | `address` | Base asset address   |
| `quote`      | `address` | Quote asset address  |
| `user`       | `address` | Vault address        |

**Returns**

| Name     | Type      | Description                                         |
| -------- | --------- | --------------------------------------------------- |
| `<none>` | `uint256` | value of the base asset in terms of the quote asset |

### getOracleData

Return oracle metadata for base/quote

```solidity
function getOracleData(address base, address quote) external view returns (OracleData memory data);
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

**Returns**

| Name   | Type         | Description |
| ------ | ------------ | ----------- |
| `data` | `OracleData` | Oracle data |

## Events

### OracleSet

Emitted when an oracle is added

```solidity
event OracleSet(address indexed base, address indexed quote, IOracle indexed oracle);
```

**Parameters**

| Name     | Type      | Description         |
| -------- | --------- | ------------------- |
| `base`   | `address` | Base asset address  |
| `quote`  | `address` | Quote asset address |
| `oracle` | `IOracle` | Added oracle        |

### OracleScheduled

Emitted when an oracle update is scheduled

```solidity
event OracleScheduled(
    address indexed base, address indexed quote, IOracle indexed pendingOracle, uint32 commitTimestamp
);
```

**Parameters**

| Name              | Type      | Description                                        |
| ----------------- | --------- | -------------------------------------------------- |
| `base`            | `address` | Base asset address                                 |
| `quote`           | `address` | Quote asset address                                |
| `pendingOracle`   | `IOracle` | Pending oracle                                     |
| `commitTimestamp` | `uint32`  | The timestamp when the oracle data can be commited |

### OracleUpdateCancelled

Emitted when an oracle update is cancelled

```solidity
event OracleUpdateCancelled(address indexed base, address indexed quote);
```

**Parameters**

| Name    | Type      | Description         |
| ------- | --------- | ------------------- |
| `base`  | `address` | Base asset address  |
| `quote` | `address` | Quote asset address |

### OracleDisabled

Emitted when an oracle is disabled

```solidity
event OracleDisabled(address indexed base, address indexed quote, IOracle indexed oracle);
```

**Parameters**

| Name     | Type      | Description             |
| -------- | --------- | ----------------------- |
| `base`   | `address` | Base asset address      |
| `quote`  | `address` | Quote asset address     |
| `oracle` | `IOracle` | Oracle that is disabled |

### PendingOracleAccepted

Emitted when a user accepts an oracle update early

```solidity
event PendingOracleAccepted(address indexed user, address indexed base, address indexed quote, IOracle oracle);
```

**Parameters**

| Name     | Type      | Description                                        |
| -------- | --------- | -------------------------------------------------- |
| `user`   | `address` | Address of the user which accepted the oracle data |
| `base`   | `address` | Base asset address                                 |
| `quote`  | `address` | Quote asset address                                |
| `oracle` | `IOracle` | Oracle which was accepted                          |

### OracleOverrideRemoved

Emitted when an oracle override is removed

```solidity
event OracleOverrideRemoved(address indexed user, address indexed base, address indexed quote);
```

**Parameters**

| Name    | Type      | Description                                           |
| ------- | --------- | ----------------------------------------------------- |
| `user`  | `address` | Address of the user which removed the oracle override |
| `base`  | `address` | Base asset address                                    |
| `quote` | `address` | Quote asset address                                   |

## Errors

### AeraPeriphery\_\_CallerIsNotAuthorized

```solidity
error AeraPeriphery__CallerIsNotAuthorized();
```

### AeraPeriphery\_\_OracleMismatch

```solidity
error AeraPeriphery__OracleMismatch();
```

### AeraPeriphery\_\_CommitTimestampNotReached

```solidity
error AeraPeriphery__CommitTimestampNotReached();
```

### AeraPeriphery\_\_OracleUpdateDelayTooLong

```solidity
error AeraPeriphery__OracleUpdateDelayTooLong();
```

### AeraPeriphery\_\_OracleConvertsOneBaseTokenToZeroQuoteTokens

```solidity
error AeraPeriphery__OracleConvertsOneBaseTokenToZeroQuoteTokens(address base, address quote);
```

### AeraPeriphery\_\_NoPendingOracleUpdate

```solidity
error AeraPeriphery__NoPendingOracleUpdate();
```

### AeraPeriphery\_\_OracleIsDisabled

```solidity
error AeraPeriphery__OracleIsDisabled(address base, address quote, IOracle oracle);
```

### AeraPeriphery\_\_CannotScheduleOracleUpdateForTheSameOracle

```solidity
error AeraPeriphery__CannotScheduleOracleUpdateForTheSameOracle();
```

### AeraPeriphery\_\_OracleUpdateAlreadyScheduled

```solidity
error AeraPeriphery__OracleUpdateAlreadyScheduled();
```

### AeraPeriphery\_\_ZeroAddressOracle

```solidity
error AeraPeriphery__ZeroAddressOracle();
```

### AeraPeriphery\_\_OracleNotSet

```solidity
error AeraPeriphery__OracleNotSet();
```

### AeraPeriphery\_\_OracleAlreadySet

```solidity
error AeraPeriphery__OracleAlreadySet();
```

### AeraPeriphery\_\_OracleAlreadyDisabled

```solidity
error AeraPeriphery__OracleAlreadyDisabled();
```

### AeraPeriphery\_\_ZeroAddressOwner

```solidity
error AeraPeriphery__ZeroAddressOwner();
```


# HooksLibrary

Library to be used when building custom operation hooks

## Functions

### isCallBeforeHook

Check if the current hook call is a before hook call

```solidity
function isCallBeforeHook() internal view returns (bool);
```

**Returns**

| Name     | Type   | Description                                                          |
| -------- | ------ | -------------------------------------------------------------------- |
| `<none>` | `bool` | True if the current hook call is a before hook call, false otherwise |

### isCallAfterHook

Check if the current hook call is an after hook call

```solidity
function isCallAfterHook() internal view returns (bool);
```

**Returns**

| Name     | Type   | Description                                                          |
| -------- | ------ | -------------------------------------------------------------------- |
| `<none>` | `bool` | True if the current hook call is an after hook call, false otherwise |

### isBeforeHook

Check if the provided hook is a before hook

```solidity
function isBeforeHook(address hook) internal pure returns (bool);
```

**Parameters**

| Name   | Type      | Description                      |
| ------ | --------- | -------------------------------- |
| `hook` | `address` | The address of the hook to check |

**Returns**

| Name     | Type   | Description                                                 |
| -------- | ------ | ----------------------------------------------------------- |
| `<none>` | `bool` | True if the provided hook is a before hook, false otherwise |

### isAfterHook

Check if the provided hook is an after hook

```solidity
function isAfterHook(address hook) internal pure returns (bool);
```

**Parameters**

| Name   | Type      | Description                      |
| ------ | --------- | -------------------------------- |
| `hook` | `address` | The address of the hook to check |

**Returns**

| Name     | Type   | Description                                                 |
| -------- | ------ | ----------------------------------------------------------- |
| `<none>` | `bool` | True if the provided hook is an after hook, false otherwise |

### isBeforeAndAfterHook

Check if the provided hook is a before and after hook

```solidity
function isBeforeAndAfterHook(address hook) internal pure returns (bool);
```

**Parameters**

| Name   | Type      | Description                      |
| ------ | --------- | -------------------------------- |
| `hook` | `address` | The address of the hook to check |

**Returns**

| Name     | Type   | Description                                                           |
| -------- | ------ | --------------------------------------------------------------------- |
| `<none>` | `bool` | True if the provided hook is a before and after hook, false otherwise |

## Errors

### HookNotBefore

```solidity
error HookNotBefore();
```

### HookNotAfter

```solidity
error HookNotAfter();
```

### HookNotBeforeAndAfter

```solidity
error HookNotBeforeAndAfter();
```


# Integrating with gtUSDa

[gtUSDa](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5) is the ERC20 for our flagship stablecoin vault on Base which can be integrated into other DeFi applications. Here is a basic integration guide to incorporate gtUSDa into your protocol or app.

{% hint style="info" %}
**gtUSDa is a transferable ERC20 on Base**
{% endhint %}

All user interactions with the vault happen on Base. We also have a dedicated frontend for supplying into this vault at [app.gauntlet.xyz/vaults/gtusda](https://app.gauntlet.xyz/vaults/gtusda).

## Supplying via Contract Calls

Supplying into gtUSDa is a combination of 2 function calls: an `approve` call to spend the USDC, and a `requestDeposit` call.

First, retrieve the Provisioner and PriceAndFeeCalculator addresses via contract calls on the vault contract itself using the `.provisioner()` and `.feeCalculator()` methods, see:[#discovering-provisioner-and-priceandfeecalculator-addresses](#discovering-provisioner-and-priceandfeecalculator-addresses "mention")

1. call [`approve`](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#writeProxyContract#F1) on the USDC contract to allow the USDC amount to be spent by the provisioner contract&#x20;
   1. Note that this is not the vault contract itself, as the request runs through the provisioner to issue vault units asynchronously via a solving mechanism (see [Entry/Exit with Provisioner](/entry-exit-with-provisioner) for more details)
2. `requestDeposit` . `requestDeposit` is an asynchronous operation, the user will submit the USDC to the provisioner and after the request is solved gtUSDa units will be sent back to the users wallet directly. This will generally happen within 6 hours though can take up to as long as 3 days (or otherwise based on deadline). See [Entry/Exit with Provisioner](/entry-exit-with-provisioner) for more details
   1. token: `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` (USDC contract on base)
   2. tokensIn: USDC amount in (decimal adjusted value)
   3. minUnitsOut: This parameter needs to be based on the current price of gtUSDa. To calculate it refer to the PriceAndFeeCalculator contract, specifically the `convertTokenToUnits`  function call.&#x20;
      1. `convertTokenToUnits(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, tokensIn)` -> Returns the decimal adjusted gtUSDa units
      2. &#x20;Multiply the above value by 0.97 (some buffer in case there are price changes)
   4. solverTip: `0`
   5. deadline:  `block.timestamp + 259200` (3 days in seconds)
   6. maxPriceAge: `3600` (1 hour in seconds)
   7. isFixedPrice: `False`&#x20;

{% hint style="info" %}
`minUnitsOut` is technically not required for automatically priced orders if the vault price is trusted but is highly recommended for safety.\
\
Please do not include a `solverTip` as the solver will not solve these requests.\
\
Please do not include a large deadline as unfillable orders cannot be refunded ahead of the `deadline`.
{% endhint %}

### Example Request to deposit 1000 USDC

Call [`approve`](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#writeProxyContract#F1) on the Base USDC contract to allow the provisioner to spend

```
provisionerAddress = vaultContract.provisioner()

approve(
    provisionerAddress, // Provisioner Contract Address 
    1000000000 // USDC amount decimal adjusted
)
```

call `requestDeposit` on the provisioner, parameterizing this call is important

```
requestDeposit(
    0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, // USDC Contract on base
    1000000000, // USDC amount decimal adjusted
    0.97 * convertTokensToUnit(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, 1000000000), // minUnitsOut see above for more details
    0, // solverTip
    block.timestamp + 259200, // deadline: Set to at least 3 days, this is 3 days in seconds
    3600, // maxPriceAge: Set to 1 hour, 1 hour in seconds
    False // isFixedPrice
)
```

## Withdrawing via contract calls

To withdraw you similarly need to do an `approve` call followed by `requestRedeem` on the Provisioner Contract with the correct parameters.&#x20;

1. call [`approve`](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5#writeContract#F2) on the gtUSDa contract to allow the gtUSDa amount to be spent by the provisioner contract&#x20;
2. `requestRedeem` This is similarly an asynchronous call where the user provides vaultUnits back to the Provisioner contract and after the request is solved the user will receive USDC in their wallet
   1. token: `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` (USDC contract on base)
   2. unitsIn: The amount of vault units you wish to redeem, correctly decimal adjusted. If you want to calculate the `unitsIn` based on the USDC value for the user you can again use the `convertTokenToUnits` function on the PriceAndFeeCalculator contract as per the deposit call.
   3. minTokensOut: This parameter needs to be based on the current price of gtUSDa. To calculate it refer to the PriceAndFeeCalculator contract, specifically the `convertUnitsToToken` function.
      1. `convertUnitsToToken(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, unitsIn)` -> Returns the Decimal adjusted USDC value of the VaultUnits
      2. &#x20;Multiply the above value by 0.97 (some buffer in case there are price changes)
   4. solverTip: `0`
   5. deadline:  `block.timestamp + 259200` (3 days in seconds)
   6. maxPriceAge: `3600` (1 hour in seconds)
   7. isFixedPrice: `False`&#x20;

{% hint style="info" %}
`minTokensOut` is technically not required for automatically priced orders if the vault price is trusted but is highly recommended for safety.\
\
Please do not include a `solverTip` as the solver will not solve these requests.\
\
Please do not include a large deadline as unfillable orders cannot be refunded ahead of the `deadline`.
{% endhint %}

### Example Request to withdraw 1000 USDC

call [`approve`](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5#writeContract#F2) on the gtUSDa contract to allow the provisioner to spend

```
provisionerAddress = vaultContract.provisioner()

approve(
    provisionerAddress, // Provisioner Contract Address 
    convertTokensToUnit(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, 1000000000) // gtusda amount decimal adjusted
)
```

call `requestRedeem`  on the Provisioner, parameterizing this call is important

```
requestRedeem(
    0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, // token: USDC Contract on base
    convertTokensToUnit(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, 1000000000), // unitsIn: 1000 USDC via the PriceAndFee calculator
    0.97 * convertUnitsToToken(0x000000000001CdB57E58Fa75Fe420a0f4D6640D5, 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, unitsIn), // minTokensOut: unitsIn is the previous line response
    0, // solverTip
    block.timestamp + 259200, // deadline: Set to at least 3 days, this is 3 days in seconds
    3600, // maxPriceAge: Set to 1 hour days, this is 1 hour in seconds
    False // isFixedPrice
)
```

## Discovering Provisioner and PriceAndFeeCalculator Addresses

Note: These contracts are set as configured values on the main MultiDepositorVault contract for the vault and can change over time. To avoid any downtime or incorrect state when these addresses are rotated, **it's important to not hard-code these addresses as configuration in your applications**. Instead of hard-coding, the Provisioner and PriceAndFeeCalculator addresses should be fetched via contract calls on the vault contract:

```
provisionerAddress = vaultContract.provisioner()
priceAndFeeCalculatorAddress = vaultContract.feeCalculator()
```

## \[ADVANCED] Monitoring and refunding orders

**Tracking orders**

When an asynchronous order is placed, the user will have an active but unfilled order. To provide additional transparency to users, these orders can be monitored by tracking the following `DepositRequested` or `RedeemRequested` events:

```
/// @notice Emitted when a user creates a deposit request
/// @param user The address requesting the deposit
/// @param token The token being deposited
/// @param tokensIn The amount of tokens to deposit
/// @param minUnitsOut The minimum amount of units expected
/// @param solverTip The tip offered to the solver in deposit token terms
/// @param deadline Timestamp until which the request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
/// @param isFixedPrice Whether the request is a fixed price request
/// @param depositRequestHash The hash of the deposit request
event DepositRequested(
    address indexed user,
    IERC20 indexed token,
    uint256 tokensIn,
    uint256 minUnitsOut,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    bytes32 depositRequestHash
);

/// @notice Emitted when a user creates a redeem request
/// @param user The address requesting the redemption
/// @param token The token requested in return for units
/// @param minTokensOut The minimum amount of tokens the user expects to receive
/// @param unitsIn The amount of units being redeemed
/// @param solverTip The tip offered to the solver in redeem token terms
/// @param deadline The timestamp until which this request is valid
/// @param maxPriceAge Maximum age of price data that solver can use
/// @param isFixedPrice Whether the request is a fixed price request
/// @param redeemRequestHash The hash of the redeem request
event RedeemRequested(
    address indexed user,
    IERC20 indexed token,
    uint256 minTokensOut,
    uint256 unitsIn,
    uint256 solverTip,
    uint256 deadline,
    uint256 maxPriceAge,
    bool isFixedPrice,
    bytes32 redeemRequestHash
);
```

**Checking when orders are filled**

When a deposit or redeem is filled, one of the following events will be emitted in the Provisioner:

```
/// @notice Emitted when a deposit request is solved successfully
/// @param depositHash The unique identifier of the deposit request that was solved
event DepositSolved(bytes32 indexed depositHash);

/// @notice Emitted when a redeem request is solved successfully
/// @param redeemHash The unique identifier of the redeem request that was solved
event RedeemSolved(bytes32 indexed redeemHash);
```

**Refunding expired orders**

If the deadline passes but an order isn't solved (rare), the user has to claim back their USDC or gtUSDa tokens using the `refundRequest` function.

```
/// @notice Request parameters for deposits and redemptions
/// @dev
/// - For deposits:
///   - units: minimum units the user wants to receive (minUnitsOut)
///   - tokens: amount of tokens the user is providing (tokensIn)
/// - For redemptions:
///   - units: amount of units the user is redeeming (unitsIn)
///   - tokens: minimum tokens the user wants to receive (minTokensOut)
struct Request {
    /// @notice Request type(deposit/redeem + auto/fixed price)
    RequestType requestType;
    /// @notice User address making the request
    address user;
    /// @notice Amount of vault units
    uint256 units;
    /// @notice Amount of underlying tokens
    uint256 tokens;
    /// @notice Tip paid to solver, always in tokens
    uint256 solverTip;
    /// @notice Timestamp after which request expires
    uint256 deadline;
    /// @notice Maximum age of price data allowed
    uint256 maxPriceAge;
}

/// @notice Refund an expired deposit or redeem request
/// @param token The token involved in the request
/// @param request The request to refund
/// @dev Can only be called after request deadline has passed
function refundRequest(IERC20 token, Request calldata request) external;
```

## Getting the User's balance of gtUSDa

Simply call the [balanceOf](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5#readContract#F5) function on [gtUSDa](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5) with the user's address.

## Pricing gtUSDa units in USDC (and vice versa)

We provide simple price conversion utilities between gtUSDa and USDC via the PriceAndFeeCalculator contract.

Specifically there are two functions of relevance

* `convertTokensToUnits` -> Takes in USDC value and returns amount of vaultUnits at current price
  * vault: `0x000000000001CdB57E58Fa75Fe420a0f4D6640D5` (gtUSDa vault contract)
  * token: `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913`  (USDC contract on base)
  * tokenAmount: Decimal adjusted USDC value (USDC has [6 decimals](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#readProxyContract#F11))
* `convertUnitsToTokens` -> Takes in vaultUnits and returns USDC value at current price
  * vault: `0x000000000001CdB57E58Fa75Fe420a0f4D6640D5` (gtUSDa vault contract)
  * token: `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913`  (USDC contract on base)
  * unitsAmount: Decimal adjusted gtUSDa value (gtUSDa has [18 decimals](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5#readContract#F7))

## Fetching the APY of the vault

This one is a little trickier as of right now, but we aim to make this simpler in the future via an API. As of right now the best way to get the APY of the vault is to index the price of the vault units in USDC over a given time period and extrapolate this to a yearly APY number.

## Calculating the TVL of the vault

To get the total TVL of the vault use the `convertUnitsToTokens`  function on the PriceAndFeeCalculator and use the [`totalSupply`](https://basescan.org/address/0x000000000001CdB57E58Fa75Fe420a0f4D6640D5#readContract#F21) of gtUSDa as an input.


# Introduction

Aera is a treasury management protocol that attempts to address existing shortcomings with controlling treasury funds. Aera supports:

* **Robust Asset Selection** No need to plan strategies. Just pick an objective and assets you would like to include.
* **Efficient Purchasing** Remove bureaucracy from DAOs.
* **Decentralized Active Management** Market aware & tailored to your protocol.

**Read these next:**

{% content-ref url="/pages/Nelq55QvaxzhPDIYfjGZ" %}
[The Aera Approach](/v2-archive/the-aera-approach)
{% endcontent-ref %}

{% content-ref url="/pages/LTlwFCIZrbGmYb9eK9rJ" %}
[How Aera V2 Works](/v2-archive/how-aera-v2-works)
{% endcontent-ref %}

### Aera V2

*Aera V1 was launched and tested in 2022 as a way to adjust a 2-asset portfolio to mitigate risk exposure. This documentation describes Version 2, an updated iteration of the smart contracts to be launched in 2023. To read more about Aera V1, we recommend reading* [*the whitepaper*](https://uploads-ssl.webflow.com/62cd150e5e9efc960319c44d/6346c4525380f8fa6435c2b5_Aera_Whitepaper.pdf) *and the V1 archive below:*

{% content-ref url="/pages/OhCVXoNXMjYkTqkhUt9a" %}
[Aera Introduction](/v1-archive/aera-introduction)
{% endcontent-ref %}

### Guides

Learn about how to interact with Aera whether as a direct user (Treasury), Arbitrageur or to earn rewards as a Vault Guardian.

{% content-ref url="/pages/nq9IEYjURVeftSlJ0901" %}
[Treasury](/v2-archive/guides/treasury)
{% endcontent-ref %}

Use Aera to protect your users from shortfalls.

{% content-ref url="/pages/6pstqCPbqTm7WSMIzdCL" %}
[Vault Guardians](/v2-archive/guides/vault-guardians)
{% endcontent-ref %}

Become a Treasury Vault Guardian.

{% content-ref url="/pages/5lQKHe3yQtmtt8gUqZ5U" %}
[Fee Recipient](/v2-archive/guides/fee-recipient)
{% endcontent-ref %}

Receive fees from Aera vaults.

{% content-ref url="/pages/cNWSpb4pDAw9g8WmW456" %}
[Developers](/v2-archive/guides/developers)
{% endcontent-ref %}

Build on top of Aera.

### Key Concepts

Learn how the Aera system protects treasuries.

{% content-ref url="/pages/GNhyJ9tY5S65btpOsmpm" %}
[Vault Assets](/v2-archive/concepts/vault-assets)
{% endcontent-ref %}

{% content-ref url="/pages/ZuDGyQRdeNXwgjPGvOcY" %}
[Objective Function](/v2-archive/concepts/objective-function)
{% endcontent-ref %}

{% content-ref url="/pages/zusmyqbfqTNsZRzmf4fq" %}
[Vault Guardian](/v2-archive/concepts/vault-guardian)
{% endcontent-ref %}

{% content-ref url="/pages/nmXLzEtySEtvYQiKgXjI" %}
[Vault Operation](/v2-archive/concepts/vault-operation)
{% endcontent-ref %}


# The Aera Approach

While a full explanation of the Aera philosophy is available in the whitepaper, we will repeat some of the core design ideas of Aera as they will be referenced several times.

### **A custom objective function for each client and each use case**

Each Aera use case and client relationship is defined by a custom objective function. This objective function defines what “good asset management” looks like in a given context. Many existing asset management solutions presume that the objective function is some direct derivative function of asset returns (or APY). Aera is more flexible, with each client in control of their objective function. As one example, the Aera Volatility Targeted portfolio is able to track towards a target level of volatility.

Note: the objective function is currently not represented on-chain in V2. The objective function will still be transparently tracked in the Aera UI, but not explicitly embedded as part of the protocol. In future iterations of the protocol, we expect objective functions for various use cases to be directly embedded in Aera as a performance feedback mechanism.

### **Vault guardians and operations**.

Each Aera vault elects a set of guardians to submit operations (currently 1 guardian per vault is supported). In the current iteration, the guardian (off-chain) can be decomposed into two parts: allocation and execution. The allocation step decides on a target portfolio allocation. The execution steps decides how to use available routing options to get to the target portfolio distribution. The guardian submits these recommended operations and they are checked by a hooks module which protects the vault against instantaneous loss of value.

In the future, the execution part will be further decentralized, allowing multiple guardians to submit desired portfolios, which will be aggregated and translated into a final set of vault operations.

### **A growing asset universe**

The aim is for Aera to encompass a wide universe of vetted assets for treasury management. While we always recommend starting with a more narrow set of assets, the system is built from the ground up to support tremendous flexibility in both underlying assets and custom purchasing flows. An asset in Aera refers to an ERC20 compatible asset together with a pricing mechanism. V2 supports both pure ERC20 tokens (priced with a contract that implements the Chainlink oracle interface) and ERC4626 type tokens (priced using the built-in conversion rate). This covers a wide range of fungible assets.

### **Epochs**

The Aera vault operates on a pre-defined time duration called the epoch. The primary need for an epoch is to allow enough time for asynchronous execution. For example, ERC20 asset rebalancing is much more cost efficient when executed as a TWAP action rather than a direct trade on an AMM, for instance. The epoch frequency also provides a predictable clock for receiving and aggregating operations when Aera Vault will be generalized to multiple vault guardians.

Note: Epochs are not represented in the protocol level in V2.

### **Numeraire**

The numeraire asset is used for vault internal accounting. For instance, every ERC20 price oracle is used in reference to the numeraire asset.

### **Fee token**

The fee token is a designated asset in which fees can be paid. This may be different from the numeraire asset.

### **Fees**

The protocol can designated a fee recipient to receive fees if the fee rate is configured higher than 0. The fee recipient would accrue a given proportion of the overall vault over time paid out in a designated fee token. In the future, as objective functions become directly embedded on-chain, fees may be modified to include performance-linked components.

### **Modularity**

The Aera system is designed to be fully modular. Modules have multiple valid implementations. We expect vault owners to have more choices over time on how to compose their treasury management solution.


# How Aera V2 Works

#### Architecture

<figure><img src="/files/yy7llKYWMG7LETnoWwlE" alt=""><figcaption></figcaption></figure>

There are three core smart contracts involved in a given Aera deployment:

* A vault contract implementation conforming to the `IVault` interface,
* An asset registry contract implementation conforming to the `IAssetRegistry` interface,
* A hooks contract implementation conforming to the `IHooks` interface

Our default recommended deployment configuration is the following:

* Use our default vault contract implementation `AeraVaultV2`
* Use our default asset registry contract implementation `AeraVaultAssetRegistry` (which supports ERC20 and ERC4626 assets)
* Use our default hooks contract implementation `AeraVaultHooks` (which supports vault value/outgoing allowance checks and an allowlist for permitted target contract and function signature tuples)

**Aera Client (DAO) – Governance/Multisig**

The Aera client (treasury) control their Aera deployment through a single account which could either be a multisig or governance contract. This account is the `owner` account in the corresponding Aera vault, the asset registry and the hooks contract.

**Vault Guardian**

The vault guardian submits operations to the Aera vault through a single account. This account is the `guardian` account in the corresponding Aera vault.

NOTE: In future iterations of Aera, the guardian address will become a contract aggregating between multiple guardians.

**AeraVaultV2 – IVault**

The vault contract is the primary resting place for treasury assets which includes both ERC20 tokens and ERC4626 assets (yield-bearing tokens) that could either be wrappers over simple yield opportunities or custom purchasing flows (such as for options). This contract is referred to as “the Aera vault”.

Treasuries interact with the vault to deposit/withdraw treasury assets and manage other aspects of their deployment. The vault guardian also interacts directly with the vault contract to submit operations. The fee recipient (which may or may not be the same as some of the other roles already mentioned) interacts with the contract to claim any earned fees (if a positive fee is set).

A wide variety of operations can be executed by the vault as long as they are permitted (whitelisted) by the hooks module.

Default implementation: the default implementation of this vault stores assets directly in a security-minimized smart contract. Implementations using a custody solution like Gnosis Safe or other more complex mechanisms would also be viable.

**AeraVaultAssetRegistry – IAssetRegistry**

The asset registry module controls what assets are currently whitelisted to be used in the Aera vault. The main pieces of information currently specified in the asset registry are the following:

* The numeraire asset (vault value and oracle prices are measured in terms of this asset)
* The fee token (used to pay out fees)
* ERC20 assets and their preferred price oracles
* ERC4626 assets.

**AeraVaultHooks – IHooks**

The hooks module controls what happens before and after sensitive vault functions. The following 4 actions currently support `before` and `after` hooks:

* deposit
* withdraw
* submit
* finalize.

#### Key users and roles

The vault has an owner, guardian and fee recipient role.

The asset registry has an owner role (the vault role is used upon construction to check for errors in role assignment).

The hooks module has an owner role and a vault role (to authorize vault actions).

The owner of the vault, asset registry and hooks module will always be the client (governance contract or multisig) to provide easy access to funds and for security purposes.

They may elect other entities to manage the asset registry and/or hooks but it's important that there is no possible collusion with the guardian/fee recipient.

The hooks module's vault role will always point to the vault as the authorized smart contract that can execute the hooks.

#### How the guardian rebalances the vault

The role of vault guardians is to submit portfolio operations that will change the portfolio allocation in accordance with the constraints present in the hooks module to achieve the objective set by the Owner. The default hooks module constrains vault operations with the following invariants:

* it places upper bounds on how vault value can change
* it disallows any outgoing allowances for vault assets to remain after a submission
* it only allows transactions to whitelisted external contracts and function signatures pairs

#### Assets

The two types of supported assets in Aera vaults are ERC20 tokens and ERC4626 tokens (yield-bearing assets). ERC4626 tokens are implemented to support a variety of yield-strategies or more complex asset purchasing flows (such as purchasing options). The unified interface allows more assets to be supported without adding complexity or size to the primary Aera contracts.

#### Future directions

The architecture is flexible enough to support several potential future developments:

* Using other custody solutions in place of the Aera vault. For example, a Gnosis safe could be used as the custody layer by combining it with an adapter that conforms to the Aera vault interface `IVault`. Then Gnosis safe would interact directly with the corresponding asset and execution modules,
* Decentralizing the guardian
* Building more sophisticated hooks
* Adding new asset types.

#### Limitations

* **Upgradability.** For security purposes, we have not provided a built-in upgrade path. Nevertheless, V2 aims to make withdrawing all funds simple. The only exception is the hooks contract which can be upgraded live and individual assets in the asset registry.
* **Fixed numeraire and fee asset**. Aera currently does not support changing the numeraire and fee assets. We don't foresee this being an issue in practice as the best numeraire asset for each vault will usually be established upfront. We will evaluate depeg risk for candidate numeraire assets.


# Guides


# Treasury

#### Supported networks

Note that Aera is currently supported on any EVM-compatible network that has sufficient onchain liquidity and oracle availability for the assets that need to be traded. The Aera vault should be in a network co-located with the governance contract or multisig of a client protocol. In a circumstance where a client's protocol is not on a network with sufficient onchain liquidity for the assets they intend to have in the treasury, we recommend the creation of a separate multisig for the purposes of managing an Aera vault on Ethereum Mainnet or another suitable network if bridging of assets can be done in a safe manner.

{% content-ref url="/pages/phAjulbwkMMn8bg2LGDQ" %}
[Using the Factory](/v2-archive/guides/treasury/using-the-factory)
{% endcontent-ref %}

{% content-ref url="/pages/fpoVmjk2BlLxfKKzi1AT" %}
[Operating your Vault](/v2-archive/guides/treasury/operating-your-vault)
{% endcontent-ref %}

{% content-ref url="/pages/Txf6380h7xUdIBZthUk5" %}
[Managing the asset registry](/v2-archive/guides/treasury/managing-the-asset-registry)
{% endcontent-ref %}

{% content-ref url="/pages/Fk0eE1CtoeMVo4SSO0al" %}
[Managing hooks](/v2-archive/guides/treasury/managing-hooks)
{% endcontent-ref %}


# Using the Factory

#### Pre-requisites

Before deploying Aera, it's important to decide and agree on:

* Which network Aera will be deployed on
* Which entities and contracts will be responsible for the owner roles of the vault, the guardian role, the fee recipient (if relevant) and the owner (maintenance) roles for vault module and the hooks module
* Agree on specific versions of the vault / asset and hooks modules to deploy
* \[If applicable] Agree who will operate any required additional services
* The objective function used and the targeted epoch period (off-chain)
* The initial asset universe
* The starting configuration parameters of the vault, asset and hooks modules such as the price oracles that will be used for each asset
* Agree on what interfaces will be used to access and monitor Aera (e.g., the Aera front-end).

#### What if my treasury holdings are different from the assets I want in my Aera vault?

No rebalancing is cost-free and there are some situations where we would recommend exercising care. A common issue is if a protocol treasury consists entirely of their native token. In this case, while the native token could temporarily be supported in the Aera vault and used to rebalance into desired portfolio assets, the price impact of rebalancing a large amount of an illiquid native token could be large.

The Aera team is happy to advise on potential courses of action in these scenarios and we aim to support treasuries as broadly as possible.

#### Deployment

Aera deployment happens via a designated factory contract `AeraV2Factory` atomically in the following sequence:

* The asset registry is deployed
* The hooks contract is deployed
* The vault is deployed

We recommend that the deployment of the Aera vault is handled by the Aera team and we have prepared deployment scripts to do so and hand ownership over to the client.


# Operating your Vault

#### Emergency operations

Every vault can minimally be seen as a wallet. Specifically, the `execute` function can be used by the owner to directly execute a transaction. We do not recommend doing this outside of emergencies, however, as it can interfere with the restrictions that guardians have on the vault. We also recommend pausing the vault ahead of doing this to minimize follow-up or interfering transactions from the guardian.

For example, a vault owner could use `execute` to leave an open allowance which a malicious guardian could then exploit by transfering the funds.

#### Depositing funds

One of Aera's distinctive features is the ability to receive deposits in any combination of the supported assets. This means that clients can generally deposit whatever assets their treasury can most conveniently access and the Aera vault will be rebalanced by the guardian to ensure that the correct portfolio distribution of assets is achieved. Deposits support both ERC20 and ERC4626 assets.

Note: assets not currently registered in the asset module need to be registered before deposit.

#### Withdrawing funds

In similar fashion to deposits, assets can be withdrawn from the Aera vault. However, there is a small caveat: if enabled, some amount of vault fees could be owed to the fee recipient and need to be claimed.

{% hint style="warning" %}
**Retrieving accidentally sent funds**

Both depositing and withdrawing funds happen through the appropriate functions in the Aera vault. If a user accidentally sends ERC20 tokens directly to the vault, they can still be retrieved using the `execute` function which gives the owner full control over vault actions.
{% endhint %}

#### Adding an asset

Adding a new asset means registering it with the asset registry only. After the asset has been registered, it can be safely deposited into the vault.

#### Removing an asset

Removing an asset can be done simply by updating the asset module. However, the vault must have a 0 balance of this asset so a withdrawal is necessary first. It's important that an asset removal is communicated to the guardian.

#### Emergency actions

**Pausing execution**. The vault can be paused using `pause`, which will prevent further actions by the guardian.

**Changing the vault guardian**. The vault guardian (and the corresponding fee recipient) can be changed in circumstances where the original guardian is not trusted or the guardian needs to change their wallet address.

#### Updating the Hooks contract

The vault owner can safely update the hooks contract, which will trigger the existing hooks contract to be decommissioned. This will be a rare operation.

#### Finalizing the vault

In order to formally terminate an Aera vault or facilitate the urgent withdrawal of all assets, an Aera vault can be finalized. Finalization will:

* Stop any guardian actions
* Send all assets to the owner
* May trigger additional actions if embedded in `beforeFinalize` / `afterFinalize` hooks

#### Retrieving stuck assets

ERC20 tokens not part of the asset universe can be withdrawn using `execute`. Note that ERC4626 tokens double as ERC20 tokens and can also be withdrawn.

#### Upgrading the vault

There is no official upgrade path to Aera vaults for 2 reasons:

* Upgradability is a security risk
* Aera is a rapidly evolving product in a novel domain and we are focused on making substantial improvements first.

However, every client should be able to move to new versions of Aera vaults as they become available or make new deployments that use different modules (e.g., different asset registry) over time.

#### Soft upgrade

In this initial period, the objective function is still decided off-chain. It influences code that the guardians use off-chain to submit parameters to Aera vaults.

A client may coordinate an explicit change in the objective function which may not require any updates to smart contracts provided that:

* The new objective function is agreed by both client and guardian(s)
* The existing modules are compatible with meeting the objectives of this objective function (e.g., the execution approach is appropriate).

Changing the objective function is called a “soft upgrade”.

#### Hard upgrade

A hard upgrade requires a re-deployment of a new Aera vault. This could occur when:

* The client wishes to switch any of the core modules (vault, asset registry, hooks)
* The client wishes to upgrade to a new version of the Aera protocol

The three-step procedure to do this is as follows:

* Agree on closure protocol/timeline and upgrade target
* Finalize existing vault
* Follow on-boarding process for new vault.


# Vault operation via Gnosis Safe

Use this when the Owner of the Vault is a Gnosis Safe

**This guide is specifically for Vaults where the owner of the vault is a Gnosis Safe.** All other owners i.e. Metamask wallets, hardware wallets, or governance, will look very similar to this guide but will have differences in composing the transactions to execute.

## Overview

There are 4 main actions you can take now that your Aera vault is live.

* Withdrawal
* Pause the vault
* Deposit more funds
* Finalization

Almost all steps below will rely on you constructing transactions from your Gnosis Safe, and then submitting a tx batch from your gnosis safe to the Aera vault.&#x20;

Full documentation for the Aera protocol can be found at [docs.aera.finance](https://docs.aera.finance)

For all the below actions use the transaction builder in Gnosis Safe.

First in Gnosis Safe, click this link:

<figure><img src="/files/6XCnHYf6ZkUZBT6FMj0j" alt=""><figcaption></figcaption></figure>

Then use the transaction builder given the guidance in each section below:

<figure><img src="/files/mmOTUoHaIBVP0uNRtFLr" alt=""><figcaption></figcaption></figure>

The high-level flow will be:

1. Fetch the address of your vault
   1. This can be grabbed from the URL on the app. i.e. The Vault address for <https://app.aera.finance/1/vault/v2/0x9ecf0d8dcc0076dd153749bece0762acae1c9049> is [0x9ecf0d8dcc0076dd153749bece0762acae1c9049](https://etherscan.io/address/0x9ecf0d8dcc0076dd153749bece0762acae1c9049)

2. Construct the transaction from the Gnosis Safe that is the owner of the Aera vault using your vault address and the Transaction builder based on the action you want to take
   1\.

   ```
   <figure><img src="/files/qmUbYuPwifT3MINFeMyv" alt=""><figcaption><p>The ABI will automatically populate from your vault address</p></figcaption></figure>
   ```

   2\.

   ```
   <figure><img src="/files/fEaXAkZ3rGy35LUyOZoz" alt=""><figcaption><p>Select the relevant Contract method based on the action you are taking and hit Add Transaction</p></figcaption></figure>
   ```

   3\. Repeat the above step till you have added all relevant contract methods for the action you are taking

3. Click Create Batch in Transaction builder
   1\.

   ```
   <figure><img src="/files/J1QHDmxMJcc9WSVduhiZ" alt=""><figcaption><p>Once you have all the actions together hit Create Batch</p></figcaption></figure>
   ```

4. Click Simulate to validate that everything works as intended
   1\.

   ```
   <figure><img src="/files/D8KZcnwM8JWoBXsvzm8A" alt=""><figcaption><p>Review the Results by clicking the 'on Tenderly' link</p></figcaption></figure>
   ```

5. Send Batch then coordinate signers

## Withdrawal

This will allow you to withdraw funds from the vault and allow the vault to keep operating.

#### **Steps**

1. Add `pause` to the transaction batch
   1. This is not strictly required but adds safety in the case that you wish to change the vault objectives
2. Figure out addresses and max amounts by querying `holdings` in the Read Contract on Etherscan (example [holdings call](https://etherscan.io/address/0x9ecf0d8dcc0076dd153749bece0762acae1c9049#readContract#F8), please do this on your own vault)&#x20;

   * This will give you a tuple with the max amount of each token you can withdraw. It will look similar to this:

   <figure><img src="/files/6csZfYgW5J6Coopa1PX3" alt=""><figcaption><p>holdings tuple</p></figcaption></figure>

   **Note that the withdrawal amount must be less than the full holdings of the vault**
3. Add `withdraw` to the transaction batch using the tuple from above but editing the values to be the amounts you wish to withdraw.&#x20;
   * **Note that the amounts are all decimal based and you need to add the amounts with the correct amount of decimals for each asset**. As an example `wstETH` on mainnet has 18 decimals, so an amount in the tuple of `43757964133048721408` is actually `43.757... wstETH`, `USDC` on mainnet has only 6 decimals.  Please ensure you input the correct amount for a given assets decimals
   * You will need to put each element in quotes and delimit inner tuples with commas, as an example building on the above screenshot:&#x20;
     * ![](/files/bOWnVySHR85SKn6VqsxJ)
4.

```
<figure><img src="/files/0CZ5LerBonT0KhiEK371" alt=""><figcaption></figcaption></figure>
```

## Pause the vault

This action prevents trading from occurring in the vault. This has the following effects:

1. Guardian will no longer be able to send operations on the vault for execution, vault will now have the exact token amount as per when the vault was paused

Pausing the vault is used as part of the withdraw flow, and also can be used in emergency scenarios (i.e. a USDC depeg scenario).

#### Steps

1. Call and execute pause in the transaction builder
   1\.

   ```
   <figure><img src="/files/FCxefy9a7OxnRrp7m5bL" alt=""><figcaption><p>Add Pause then click Create Batch</p></figcaption></figure>
   ```

## Deposit more funds

This allows you to increase your allocation to the vault. Please reach out to your Guardian if you need assistance.

> Please do not directly send funds to the vault and instead use the instructions here to deposit more funds into the vault

> Please reach out to your guardian to make sure they are aware of the incoming deposit and can adjust strategies as necessary to accommodate

#### Steps

1. Call and execute `deposit` on the vault with the right `amounts` tuple

   1. This is very similar to setting up the withdrawal tuple. In particular you will need to structure a tuple like the withdrawal tuple: `[["token_address1", "decimal_amount1"], ["token_address2", "decimal_amount2"]...]`
   2. The assets and amounts you choose must be available in the Owner Gnosis Safe that this transaction will be executed from
   3. Only assets already in the Asset registry can be allowed into the vault (see [Managing the asset registry](/v2-archive/guides/treasury/managing-the-asset-registry) )
   4.

   ```
   <figure><img src="/files/fov9t8D37Lph6uXeqqeW" alt=""><figcaption></figcaption></figure>
   ```

## Finalization

This will permanently shut down the vault and fully return funds back to the Owner multisig.

> 🔌 Note that doing this will terminate the vault, subsequent deposits will need another vault to be spun up. If you wish to keep using this vault see the steps for Withdrawal.

#### **Steps**

1. Call and execute `finalize` by selecting it from the Contract Method Selector
   \*

   ```
   <figure><img src="/files/5Po1ISTLfY0mifql2QpN" alt=""><figcaption></figcaption></figure>
   ```


# Managing the asset registry

Aera is designed to place as little trust as possible in third parties like guardians and fee recipients. As a result, the client itself is the owner of the asset registry contract and is responsible for adding/removing assets as required.

It's critical that the owner independently evaluates the correctness of any calldata sent to the asset registry. Adding an asset is a sensitive operation that could needlessly empower the guardian if an unsafe oracle is used for an asset.


# Managing hooks

For similar reasons as the asset registry, the Aera client is responsible for managing the hooks contract.

This currently entails maintaining the target sighash allowlist which governs the type of contracts and operations on those contracts that the gurdian can submit for execution

It's critical that the client independently evaluates the correctness of any calldata sent to the hooks contract. Adding an target sighash pair is a sensitive operation that could needlessly empower the guardian.


# Vault Guardians

#### The objective of the vault guardian

The vault guardian for each vault aims to submit operations in a way that increases the objective function performance of the vault. While the objective function performance is not currently measured on-chain and tied to any form of performance fees, objective function performance is measured off chain and used to keep the vault guardian accountable.

The vault guardian should also strive to do this in a way that:

* **Mitigates risk**. Stability of objective function performance can be more important than taking highly volatile positions
* **Facilitates explainability** of their actions (which will build trust with the vault owner).

#### Submission flow

When a vault guardian makes a submission with `submit`, it goes through the following lifecycle of steps:

* The vault guardian submits a set of operations
* The `beforeSubmit` hook is executed
* The set of operations are executed 1-by-1 in order. If one of them reverts, the whole chain of transactions reverts.
* The `afterSubmit` hook is executed.

#### How oracles are used to price assets

**ERC20 tokens**

Each ERC20 token has a corresponding oracle that provides the price of that asset with respect to the numeraire. The price of the numeraire asset itself is trivially fixed at 1.

**ERC4626 tokens**

For ERC4626 tokens, the `convertToAssets` function is used to convert a given number of ERC4626 vault shares into an equivalent amount of underlying token. Note that this amount of underlying token may not be immediately withdrawable and represents an estimation. Each underlying token is also an ERC20 token in the vault and from there the oracle is used to further express the ERC4626 share value into numeraire terms.

More details on using convert functions as price oracles can be found in [the ERC4626 specification](https://eips.ethereum.org/EIPS/eip-4626).

#### How to submit operations

Operations can be initiated by calling `submit` on the vault. There are no other actions that the vault guardian is expected to take.

#### Off-chain intelligence

The vault guardian will be expected to develop appropriate off-chain intelligence to make submissions that maximize objective function value while mitigating execution costs. We highlight a few high-level capabilities we expect the vault guardian to develop:

* Objective function measurement / estimation, allowing the vault guardian to consider various scenarios and predict objective function evolution in each of the scenarios. Note that some important of the objective function such as short-term asset prices may not be as predictable as others
* Estimation of rebalancing costs
* Adding appropriate slippage bounds with off-chain price intelligence to protect the vault
* Breaking up strategies across multiple epochs to mitigate the price impact of large trades
* A deep understanding of custom strategies represented in the enabled ERC4626 vaults, their liquidity characteristics and risks
* Risk management monitoring techniques enabling reactions to adverse market circumstances, depeg scenarios, etc.

Please see the [Contact Us](/contact-us)to express interest being a Vault Guardian.


# Fee Recipient

#### The fee recipient

Whenever a guardian is changed, a fee recipient is designated. The fee recipient could be one of the following depending on the use case:

* The guardian
* A designated wallet by the guardian
* The Aera treasury
* etc.

The fee recipient is only relevant for vaults with a nonzero fee.

#### How to claim fees

The fee recipient can claim fees using `claim` at any point.

#### When to claim fees

Fees accumulate as a proportion of the vault holdings. Note that this proportion is not “marked” when the client withdraws or deposits to the treasury. Instead it will result in a forced claim. We recommend fee recipients to be claiming small accumulated fee amounts frequently for the following reasons:

* The client may decide to withdraw/deposit, forcing a claim at an inconvenient time
* At a large enough accumulated fee level the client may feel like you are using the vault to manage your own capital

#### How fees are calculated

* A fee rate is set upon vault creation and capped at 0.0000001% per second (3.1536% per year).
  * Note that the fee has a small amount of *negative compounding* in the event that vault value is constant. E.g., if vault value starts at $10M, some of that will accrue to the fee recipient and therefore the future fee amount will be derived from smaller vault `value` numbers
  * This works the opposite way as the vault grows or receives further inflows as more frequent recalculation will lead to the fee being calculated against a larger vault value amount
* Fees are recalculated based on the current vault `value()` and the per second fee rate. They are accrued incrementally during
  * Actions that (potentially) modify vault value: `withdraw`, `deposit`, `execute`, `submit`
  * Actions that terminate existing guardianship: `pause`, `finalize`, `setGuardianAndFeeRecipient`
  * During a `setHooks` update
  * Prior to fees being claimed
* **NOTE**: If an underlying oracle reverts, then the `value()` function will revert and fees will use the last measured vault value instead.

#### Trust model

It's assumed that the guardian trusts and is incentivized to help the fee recipient. In particular, the guardian will generally try to maintain sufficient reserves of the fee token so that the fee recipient can always claim what is earned.

It's assumed that the vault owner and fee recipient have understood how the fee rate may impact overall annual fee amount under different scenarios based on expected recalculation frequency and vault value evolution.


# Developers

Please [Contact Us](/contact-us) if you would like to build on top of Aera.


# Concepts


# Vault Assets

Aera Vaults are developed to hold and rebalance two or more ERC20/ERC4626 assets.

### ERC20 Assets

One of the ERC20 assets is designated as the numeraire asset.

One of the ERC20 assets is designated as the fee token (this could overlap with the numeraire asset)

Each ERC20 asset has to specify a Chainlink-compatible oracle contract for pricing purposes against the numeraire asset.

### ERC4626 Assets

ERC4626 assets are supported to incorporate yield strategies in Aera.

The built-in oracle is used to price these assets.

The underlying asset of an ERC4626 has to be a supported ERC20 asset in the vault.


# Objective Function

The Objective Function is a per-treasury per-Aera vault optimization target for the Aera vault. Objective Functions differentiate Aera from other treasury management solutions by allowing a diverse set of objectives to be configured for the treasury.

### How is the Objective Function used

Today, the Objective Function is used off-chain to select, evaluate and report on treasury management strategies. As Aera seeks to decentralize treasury management, the objective function will move on-chain as a way to assess and compare objective function outcomes from different parameter predictions.


# Vault Guardian

Vault Guardian is elected by the Vault Owner (treasury) to rebalance the underlying assets to optimize for the Objective Function. The Guardian can submit operations to the vault.

The Guardian cannot (among other things):

* Deposit or withdraw any funds/liquidity
* Add or remove asset types
* Execute instantaneous value-leaking operations (such as instantaneous weight changes)
* Delegate Guardian responsibilities.


# Vault Operation

Vault operations are a series of transaction sequences used by the guardian to rebalance the vault. These transaction sequences are specified in the relevant `submit` call and checked and executed by the vault.

### Constraints on vault operations

Both the asset registry and the hooks contract are used to limit what operations can be accepted. In particular, the current iteration of the hooks contract:

* Restricts any outgoing allowances for vault tokens to be created as a result of an operation
* Restricts the vault value from decreasing beyond a pre-specified threshold
* Restricts the types of operations and contracts that are called in those operations to a whitelist




---

[Next Page](/llms-full.txt/1)

