# Developers Documentation

Welcome to the Spectra Protocol Documentation!

{% hint style="info" %}
You are currently viewing the developer docs, which are technical in nature. For more easily accessible documentation (non-technical), see our [Concepts docs](https://docs.spectra.finance/).
{% endhint %}

The goal of this documentation is for developers to understand how the Spectra protocol works under the hood and understand how to build on top of it.

***

## MetaVaults

MetaVaults are cross-chain managed yield vaults. They primarily aggregate liquidity positions across multiple markets and chains, delegating allocation to permissioned curators while enforcing on-chain safety constraints via [Zodiac](https://www.gnosisguild.org/). MetaVaults use an asynchronous deposit/redeem model ([ERC-7540](https://eips.ethereum.org/EIPS/eip-7540)) with epoch-based settlement.

To get started, see the [MetaVaults Overview](/metavaults/metavaults).

***

If some of your questions remain unanswered by this guide or if you have spotted some outdated resources, please feel free to join our [Discord community](https://discord.com/invite/GJhRf3r3UU).


# Getting Started

Welcome to the Spectra Protocol Documentation!

**Spectra is building the DeFi interest rates derivatives layer.**

From speculating on the evolution of DeFi interest rates, hedging risk on passive revenue, or providing liquidity to the derivatives layer, Spectra enables a whole new host of applications and use cases in the DeFi ecosystem.

<figure><img src="/files/3ckW2vZHdMT0Xa1eVodE" alt=""><figcaption></figcaption></figure>

The basic building block in Spectra is yield tokenisation. What this means is that the yield generated on an asset can be separated from the underlying asset.&#x20;

For example, Alice deposits 100 USDC into Aave, generating a yield of 3% after 1 year (3 USDC), meaning at the end of 1 year they are able to withdraw 103 USDC. However instead of waiting 1 year to get access to the yield, Alice could instead use Spectra to tokenise the yield, enabling them to withdraw close to the 3% yield (3 USDC) without having to wait 1 year (known as maturity). Alice is able to walk away with \~100 USDC (the principal) along with the yield that has been tokenised.&#x20;

On the other end, Bob is confident that USDC yield will only increase over the next year, so they want to speculate by buying Alice's yield, which can be purchased for close to the value of 3% yield after 1 year (\~3 USDC). Bob spends \~3 USDC and after 1 year, is able to access the yield generated, even if it results in more than 3 USDC (due to increased yield over the year).

The above is a straight forward, basic example of what is possible. There are many other possibilities for different types of users, providing liquidity, splitting yield over periods, etc, which will be explained in the following pages.


# Tokenizing Yield

As mentioned in [Getting Started](/getting-started), the fundamental concept of tokenizing yield means that the future yield generated is separated from the underlying principal asset. So if a yield of 3% is generated on 100 USDC after 1 year, the 3% yield (3 USDC) is the yield, and 100 USDC is the principal.

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

## Step by step

### 1. Find a PrincipalToken

Each Principal Token is associated to an [Interest Bearing Token (IBT)](/glossary#ibt) and has a specific duration (a specific expiry date).&#x20;

* PTs are designed to operate with any ERC4626 Tokenized Vault token.&#x20;
* Each IBT is associated to an underlying. The underlying assets of the IBT are verified using [`ERC4626.asset()`](https://eips.ethereum.org/EIPS/eip-4626#asset)

{% hint style="info" %}
Available ERC4626 Vaults on the relevant network can be found at [https://erc4626.info](https://erc4626.info/). \
\
Additionally, new Principal Tokens for any Interest Bearing Token that complies with the ERC-4626 standard can be deployed without the need for special permissions, see[ how to deploy PT and LP Vault in a permisionless manner](/guides/deploy-pt-and-curve-pool).
{% endhint %}

{% hint style="success" %}
**Explore the APY of any ERC4626 with Vision.**&#x20;

Make informed decisions and visit [vision.perspective.fi](https://vision.perspective.fi/) to gain access to the Vision API and dashboard, and to explore the historical interest rates of any ERC4626.
{% endhint %}

To find a relevant PrincipalToken:

* All Principal Tokens deployed using the [Factory](/technical-reference/contract-functions/factory)  can be accessed through the  [Registry](/technical-reference/contract-functions/registry). The number of tokens deployed is determined using  [`Registry.pTCount()`](/technical-reference/contract-functions/registry#ptcount). The PT at each index from 0 to `ptCount` is retrievable using [`Registry.getPTAt()`](/technical-reference/contract-functions/registry#getptat).
* The correctness of the address for the Interest Bearing Token (IBT) tokenized in the PT contract is ensured by using the  [`getIBT()`](/technical-reference/contract-functions/principal-token#getibt) method. , and the yield rate is ascertained either by visiting [https://vision.perspective.fi](https://vision.perspective.fi/) or by examining the conversion rate over the last few blocks using [`IBT.convertToAssets()`](https://eips.ethereum.org/EIPS/eip-4626#converttoassets).
* The maturity and duration of the PT are checked using [`PT.maturity()`](/technical-reference/contract-functions/principal-token#maturity).

### 2. Deposit

Deposit either the underlying asset (e.g. USDC) or an [Interest Bearing Token](/glossary#ibt) of the underlying asset (e.g. aUSDC).

{% hint style="info" %}
The relevant allowance of the token must be approved for the associated PrincipalToken contract before depositing.
{% endhint %}

* If depositing the underlying asset, use PrincipalToken's [`deposit()`](/technical-reference/contract-functions/principal-token#deposit).
* If depositing the interesting bearing token, use [`depositIBT()`](/technical-reference/contract-functions/principal-token#depositwithibt).

### 3. Receive PT and YT

During the deposit, the depositor receives:

* **Principal Token (PT):** This represents the original amount deposited, i.e. the principal amount. This is also referred to as 'shares' of the PrincipalToken.
* **Yield Token (YT)**: Minted by the PrincipalToken contract, the Yield Tokens represents the generated yield from the deposited principal. YT holders can claim the yield generated by the corresponding deposited IBTs over time as they hold the token.

<figure><img src="/files/surGLc2YFBpcwv4ax7pj" alt=""><figcaption><p>Depositing IBTs which are then split into Principal Tokens (PT) and Yield Tokens (YT).</p></figcaption></figure>

### 4. Before expiry

Before the expiry/maturity, a YT/PT holder can:

* Claim the yield generated by the YT, see [`claimYield()`](/technical-reference/contract-functions/principal-token#claimyield)&#x20;
* Trade or sell YT to hedge interest rate risks.&#x20;
* Trade or sell PT, speculating on interest rates without exposure to the principal asset.
* Earn additional yield by providing liquidity to the IBT-PT pairs on AMMs. See [Providing Liquidity](/guides/providing-liquidity).
* Withdraw the equivalent assets in proportion to the amount of PT and YT they currently hold, i.e. convert their PT and YT to the underlying asset (e.g. USDC) and exit their position. See [`withdraw()`](/technical-reference/contract-functions/principal-token#withdraw) and [`redeem()`](/technical-reference/contract-functions/principal-token#redeem) .

{% hint style="info" %}
Before expiry a user needs the same amount of PT and YT to be able to[`withdraw()`](/technical-reference/contract-functions/principal-token#withdraw) or [`redeem()`](/technical-reference/contract-functions/principal-token#redeem).
{% endhint %}

### 5. After expiry

After expiry/maturity, the token holder could:

* Redeem the underlying assets of the PT, see [`redeem()`](/technical-reference/contract-functions/principal-token#redeem) and [`withdraw()`](/technical-reference/contract-functions/principal-token#withdraw)
* Claim the remaining yield of the YT see [claimYield()](/technical-reference/contract-functions/principal-token#claimyield).

{% hint style="info" %}
The token holder after expiry does not need to be the same as the depositor. Both PT and YT are transferable tokens.

E.g. they could have purchased the PT at a discount and waited for expiry, earning a fixed yield since all PT are redeemable to the underlying asset at expiry.
{% endhint %}


# Providing Liquidity

When a user tokenizes their yield, they may want access to a liquid form of the principal and yield immediately. To enable this, we use the DEX [Curve.finance](https://curve.fi/) to ensure a large amount of liquidity is always available to swap in/out of the associated PT.

{% hint style="info" %}
We're currently building something special for liquidity of Yield Tokens. Stay tuned! 👀
{% endhint %}

The liquidity on the Curve.finance pools comes from Liquidity Providers, who supply one or both sides of the pool with assets. By doing this, they can earn trading fees each time a swap is made on the Curve pool.

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

## Step by step

### 1. Find a PrincipalToken

See [Tokenizing Yield](/guides/tokenizing-yield) guide for more details.

### 2. Deposit

The easiest and quickest way to supply liquidity is to use our [Router](/technical-reference/contract-functions/router). The Spectra app for instance uses the router to execute all the depositing and liquidity provision in a single transaction.

See [Routing](/guides/routing) for more information on how to build such transactions.

<figure><img src="/files/bqZgYW67pq9zOlp3jZiC" alt=""><figcaption><p>An overview of zapping in with an IBT</p></figcaption></figure>

{% hint style="info" %}
The relevant allowance of the token must be approved for the [Router](/technical-reference/contract-functions/router) contract before depositing.
{% endhint %}

#### Alternative: Depositing directly via Curve

Alternatively, you can deposit directly into the associated Curve pools. However this is more difficult and may take longer as you calculate the optimal amount of IBT and PT to convert and deposit. See the official [Curve documentation here](https://resources.curve.fi/lp/depositing).


# Deploy PT and Curve Pool

Spectra's design philosophy revolves around composability and a permissionless approach. Users have the flexibility to deploy PT/YT and an associated [Curve Pool](https://curve.fi) for any token compliant with the ERC-4626 standard, allowing them to harness the full range of Spectra's features.

## Step by Step

### 1. Deploy Principal Token

Use [`deployPT()`](/technical-reference/contract-functions/factory#deploypt) to deploy a PT for the IBT and maturity duration of your choice.

### 2. Deploy Curve Pool

Use [`deployCurvePool()`](/technical-reference/contract-functions/factory#deploycurvepool) to deploy a Curve Pool for a given PT and its associated IBT. You have to specify the different Curve Pool Parameters.&#x20;

See the [Curve Pool Parameters guide](/technical-reference/spectras-automated-market-makers) for more information on the different parameters to choose when deploying a Curve Pool for a Principal Token.

### Alternative : Deploy all at once

Use [`deployAll()`](/technical-reference/contract-functions/factory#deployall) to deploy associated PT, Curve Pool  in a single transaction, for a given PT and maturity duration.


# Routing

Useful routes to perform protocol operations in a single transaction.

The Router can aggregate operations across ERC4626 protocol vaults, the Principal Token contract and the associated pools (e.g PT-IBT Curve Pools). This gives users access highly-flexible and personalised transactions.

The Router is inspired from the [Uniswap UniversalRouter](https://docs.uniswap.org/contracts/universal-router/technical-reference).

## How commands work ? <a href="#functions" id="functions"></a>

All transactions to the [`Router`](/technical-reference/contract-functions/router)  go through the `execute` functions:

* `execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline)`
* `execute(bytes calldata commands, bytes[] calldata inputs)`

The first of these functions checks for a deadline parameter. If the `block.timestamp` is after the `deadline` provided the transaction will revert. After that check, the 2 functions otherwise execute identically.

The `execute` functions takes a list of commands , and a list of encoded inputs for each command and execute them in the order specified.

## Available Commands <a href="#command-structure" id="command-structure"></a>

The flexible commands allow users to:

* Add liquidity in the Curve pools
* Remove liquidity from the Curve pools
* Deposit in Principal Tokens
* Deposit in Interest Bearing Tokens
* Wrap vault in ERC-4626 Wrapper
* Redeem from Principal Tokens
* Redeem from Interest Bearing tokens
* Unwrap vault from ERC-4626 Wrapper
* Transfer ERC-20 tokens from Router to any address
* Transfer ERC-20 tokens from `msg.sender` to Router
* Transfer ERC-20 tokens from `msg.sender` to Router with ERC-2612 permit. Compatible with [ERC-4626 IBTs](/glossary#ibt), [PTs](/technical-reference/contract-functions/principal-token) and [YTs](/technical-reference/contract-functions/yield-token).

The list of all the commands along with the accepted parameters can be found in the [Router guide](/technical-reference/contract-functions/router#dispatcher).

## Some Useful Routes

### Add liquidity

#### Add liquidity  with assets

This route exclusively uses underlying tokens as input and carries out all necessary deposits and computations to deposit in the PT-IBT pool at the current spot price of the pool, maintaining the same ratios.

**The steps involve:**

1. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer the underlying tokens from the depositor's wallet to the Router.
2. [**DEPOSIT\_ASSET\_IN\_IBT**](/technical-reference/contract-functions/router#deposit_asset_in_ibt-command): Deposit all underlying tokens in the specified ERC4626 IBT contract to receive IBT shares.
3. [**CURVE\_SPLIT\_IBT\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_split_ibt_liquidity-command): From the pool's balances, divide the liquidity between the IBT to be added to the pool and the IBT to be deposited in the Principal Token contract in exchange for PT and YT shares. Send the minted YT shares to the user.
4. [**CURVE\_ADD\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_add_liquidity-command): Add IBT and PT liquidity to the Curve Pool. Send the Curve LP token to the user.

**Example of how to use the router commands to create transactions in Solidity:**

```solidity
import {Commands} from "src/router/Commands.sol";
import {Constants} from "src/router/Constants.sol";

/* 
    ... 
*/

commands = abi.encodePacked(
    bytes1(uint8(Commands.TRANSFER_FROM)),
    bytes1(uint8(Commands.DEPOSIT_ASSET_IN_IBT)),
    bytes1(uint8(Commands.CURVE_SPLIT_IBT_LIQUIDITY)),
    bytes1(uint8(Commands.CURVE_ADD_LIQUIDITY))
);
inputs = new bytes[](4);
inputs[0] = abi.encode(underlying, assets);
inputs[1] = abi.encode(ibt, Constants.CONTRACT_BALANCE, Constants.ADDRESS_THIS);
inputs[2] = abi.encode(
    curvePool,
    Constants.CONTRACT_BALANCE,
    Constants.ADDRESS_THIS,
    receiver
);
inputs[3] = abi.encode(
    curvePool,
    [Constants.CONTRACT_BALANCE, Constants.CONTRACT_BALANCE],
    minMintAmount,
    receiver
);
router.execute(commands, inputs)
```

#### Add liquidity  with IBTs

This route directly takes IBT shares as input and performs all necessary deposits and computations to deposit in the PT-IBT pool at the current spot price of the pool, maintaining the same ratios.

**The steps involve:**

1. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer the IBTs from the depositor's wallet to the Router.
2. [**CURVE\_SPLIT\_IBT\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_split_ibt_liquidity-command): From the pool's balances, divide the liquidity between the IBT to add to the pool and the IBT to deposit in the Principal Token contract in exchange for PT and YT shares. Send the minted YT shares to the user.
3. [**CURVE\_ADD\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_add_liquidity-command): Add IBT and PT liquidity to the Curve Pool. Send the Curve LP token to the user.

### Remove liquidity

#### Remove liquidity for assets

This route withdraws liquidity from Curve, returning the redeemed underlying assets to the user.

**The steps involve:**

1. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer Curve LP tokens from the user's balance to the Router.
2. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer the required amount of YT tokens to the Router. This step is necessary only before expiry, as after expiry, redeeming shares from the PT contract does not require the user to burn the YT.

{% hint style="info" %}
This step is needed before expiry only as after expiry redeeming shares from the PT contract doesn't require the user to burn the YT.
{% endhint %}

3. [**CURVE\_REMOVE\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_remove_liquidity-command): Remove liquidity from Curve to retrieve IBT and PT.
4. [**REDEEM\_PT\_FOR\_ASSET**](/technical-reference/contract-functions/router#redeem_pt_for_asset-command): Redeem the PT and YT shares from the PT and send assets to the user.
5. [**REDEEM\_IBT\_FOR\_ASSET**](/technical-reference/contract-functions/router#redeem_ibt_for_asset-command): Redeem the IBT shares from the IBT and send assets to the user.
6. [**TRANSFER**](/technical-reference/contract-functions/router#transfer-command): Transfer any remaining PTs or YTs if not all PTs and YTs were burnt during the redemption process.

#### Remove liquidity for IBTs

This route withdraws liquidity from Curve, returning the IBTs to the user.

**The steps involve:**

1. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer Curve LP tokens from the user's balance to the Router.
2. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer the required amount of YT tokens to the Router. This step is necessary only before expiry, as after expiry, redeeming shares from the PT contract does not require the user to burn the YT.

{% hint style="info" %}
This step is needed before expiry only as after expiry redeeming shares from the PT contract doesn't require the user to burn the YT.
{% endhint %}

3. [**CURVE\_REMOVE\_LIQUIDITY**](/technical-reference/contract-functions/router#curve_remove_liquidity-command): Remove liquidity from Curve to retrieve IBT and PT.
4. [**REDEEM\_PT\_FOR\_IBT**](/technical-reference/contract-functions/router#redeem_pt_for_ibt-command): Redeem the PT and YT shares from the PT and send IBTs to the user.
5. [**TRANSFER**](/technical-reference/contract-functions/router#transfer-command): Transfer any remaining PTs or YTs if not all PTs and YTs were burnt during the redemption process.

### Exchange your Yield Tokens

#### Exchange IBT for YT

This route is used to exchange IBTs for YTs and involves a flash loan of IBT from the Principal Token contract to borrow IBTs.

**The steps involve:**

1. [**FLASH\_LOAN**](/technical-reference/contract-functions/router#flash_loan-command): Perform the flash loan in the PT. The flashloan borrower being the Router that will execute the commands an inputs specified in the flash loan. The flashloan hence involves another nested router execution:
   1. [**DEPOSIT\_IBT\_IN\_PT**](/technical-reference/contract-functions/router#deposit_ibt_in_pt-command): Deposit the borrowed IBT in the PT contract
   2. [**CURVE\_SWAP**](/technical-reference/contract-functions/router#curve_swap-command): Swap the received PT from the deposit for IBTs

{% hint style="info" %}
Note that the Router's [`onFlashLoan`](/technical-reference/contract-functions/router#onflashloan) function is responsible for calling back to execute upon receiving flash loan funds. This function is also tasked with returning the borrowed amount along with the flash loan fees, using a `safeTransferFrom` from the caller's balance.
{% endhint %}

2. [**TRANSFER**](/technical-reference/contract-functions/router#transfer-command): Transfer all the YTs to the user

#### Exchange YT for IBT

This route is used to exchange YTs for IBTs and involves a flash loan of IBT from the Principal Token contract to borrow IBTs.

**The steps involve:**

1. [**FLASH\_LOAN**](/technical-reference/contract-functions/router#flash_loan-command): Perform the flash loan in the PT. The flashloan borrower being the Router that will execute the commands an inputs specified in the flash loan. The flashloan hence involves another nested router execution:
   1. [**CURVE\_SWAP**](/technical-reference/contract-functions/router#curve_swap-command): Swap the IBT  for PTs to get the same amount of PT than YTs to be able to redeem.
   2. [**TRANSFER\_FROM**](/technical-reference/contract-functions/router#transfer_from-command): Transfer the YTs fro the users balance to the router
   3. [**REDEEM\_PT\_FOR\_IBT**](/technical-reference/contract-functions/router#redeem_pt_for_ibt-command): Redeem PT and YTs for IBTs

{% hint style="info" %}
Note that the Router's `onFlashLoan` function is responsible for calling back to execute upon receiving flash loan funds. This function is also tasked with returning the borrowed amount along with the flash loan fees, using a `safeTransferFrom` from the caller's balance.
{% endhint %}

2. [**TRANSFER**](/technical-reference/contract-functions/router#transfer-command): Transfer all the IBTs to the user

## Previewing execution

Router also includes a preview functionnality, where a sequence of commands, along with encoded imputs can be simulated to preview the resulting rate, i.e. the amount of output token obtained at the end of execution for each wei of input token spent at the start of execution (multiplied by 1 ray unit).

* `function previewRate( bytes calldata _commands, bytes[] calldata _inputs ) external view returns (uint256)`
* `function previewSpotRate( bytes calldata _commands, bytes[] calldata _inputs ) external view returns (uint256)`

For ease of use, these functions take similar parameters than the `execute()` function.

{% hint style="warning" %}
Though `previewRate` and `previewSpotRate` mirror input parameters of `execute()`, some commands are not supported for preview. See the [Router guide](/technical-reference/contract-functions/router#previewrate) for more details.
{% endhint %}


# IBT Additional Rewards

This page describes the reward proxy mechanism on the Principal Token contract.

Some [ERC4626](https://eips.ethereum.org/EIPS/eip-4626) [Interest Bearing Tokens](/glossary#ibt) may be integrated into a reward program (on any protocol), and holding these IBTs makes the holder eligible to claim rewards. As the PT holds all the IBTs deposited by the users who[ tokenized their yield](/guides/tokenizing-yield), the PT would need to claim these rewards on behalf of the depositors.

For such eligible IBTs, the Spectra protocol can deploy a rewards proxy on top of the associated Principal Token and add the logic to claim these rewards. These rewards would be sent to a Spectra DAO address and redistributed via governance.


# Locking APW for veAPW

## What is veAPW

As mentionned in [Spectra General Documentation](https://docs.spectra.finance/tokenomics/veapw-token), Vote-escrowed APW (veAPW) is based on Curve's Vote-Escrowed CRV (veCRV) and serves as a vault where users can lock their APW for up to 2 years in order to gain voting power and participate in Spectra DAO governance.

#### veAPW gives users access to:

* Proposal voting power (participation in the protocol decisions - 1 veAPW = 104 votes)
* Gauge weight power (liquidity mining redirection)
* Yield (70% of the protool fees + a small inflation until june 2024)
* Boosting power (boost your rewards up to 2.5x)

#### The longer APW is locked, the more voting power the user receives:

* 1 APW locked for 1 week = 0.009615 veAPW = 1 vote
* 1 APW locked for 6 months = 0.25 veAPW = 26 votes
* 1 APW locked for 1 year = 0.5 veAPW = 52 votes
* 1 APW locked for 2 years = 1 veAPW = 104 votes

{% hint style="warning" %}
The weight associated with the veAPW gradually decreases as the escrowed tokens approach their lock expiry.
{% endhint %}

{% hint style="info" %}
At the lock expiry, 1 veAPW is redeemable for 1 APW.
{% endhint %}

## How to create a veAPW lock

1. Ensure that the user has a sufficient balance of APW tokens for the transaction.
2. Call veAPW's `create_lock()` function, specifying the desired amount of APW to lock, as well as the lock duration.

## How to increase a veAPW lock amount

1. Ensure that the user has an existing, non-expired veAPW lock.
2. Call veAPW's `increase_amount()` function, specifying the desired amount of APW to add to the existing lock.

{% hint style="info" %}
For an existing, non-expired lock, the lock amount can also be increased on behalf of its owner, using `deposit_for()`.
{% endhint %}

## How to increase a veAPW unlock time

1. Ensure that the user has an existing, non-expired veAPW lock.
2. Call veAPW's `increase_unlock_time()` function, specifying the new lock expiry.

{% hint style="warning" %}
The new lock expiration date must be later than the current one, and the overall lock duration cannot exceed 2 years.
{% endhint %}

## How to withdraw APW from an expired veAPW lock

1. Ensure that the user has an existing, expired veAPW lock.
2. Call veAPW's `withdraw()` function, specifying the new lock expiry.

## How to claim locking rewards

* Call `FeeDistributor`'s [`claim()`](/technical-reference/contract-functions/feedistributor#claim)function.

{% hint style="info" %}
Multiple claims can be performed in a single call with [`claim_many()`](/technical-reference/contract-functions/feedistributor#claim_many)`.`
{% endhint %}


# Voting and Earning Rewards

This guide describes the process of voting for pools and claiming voting rewards

The ve(3,3) model in the Spectra protocol promotes long-term engagement by letting users lock APW for veAPW, which provide governance power over protocol decisions, including voting for liquidity pools.

The voting mechanism enables efficient allocation of liquidity, rewarding both liquidity providers for their contributions and voters with a say in reward distribution. It results in a more stable protocol, motivated participants, and optimized rewards for committed stakeholders.

## Step by Step

### 1. Lock APW for veAPW

Create a veAPW lock for an APW amount of your choice and a duration of up to 2 years. Refer to [Locking APW for veAPW](/guides/locking-apw-for-veapw) guide for more details.

### 2. Find a pool to vote for

1. Pools are each identified by a unique ID, that can be obtained from `GovernanceRegistry`'s [`getPoolId()`](/technical-reference/contract-functions/governanceregistry#getpoolid) function.
2. Get the list of all dentifiers of pools that have associated voting rewards, using `Voter`'s [`getAllPoolIds()`](/technical-reference/contract-functions/voter#getallpoolids).
3. Verify that voting is not currently banned for a given `_poolId` using `Voter`'s [`isVoteAuthorized(_poolId)`](/technical-reference/contract-functions/voter#isvoteauthorized).
4. Vote for the desired pools, specifying the weights, using `Voter`'s [`vote()`](/technical-reference/contract-functions/voter#vote).

{% hint style="info" %}
If the identifier of a pool is not returned by [`getAllPoolIds()`](/technical-reference/contract-functions/voter#getallpoolids), voting rewards can be deployed for it. See Voter's [`createVotingRewards()`](/technical-reference/contract-functions/voter#createvotingrewards).
{% endhint %}

{% hint style="info" %}
User voting weights do not automatically update when voting power increases or decays. They can be updated using `Voter`'s [`poke()`](/technical-reference/contract-functions/voter#poke).
{% endhint %}

{% hint style="info" %}
User vote can be reset using `Voter`'s [`reset()`](/technical-reference/contract-functions/voter#reset)`.`
{% endhint %}

### 3. Find out about available rewards tokens for each pool

Fees and Bribes voting rewards can vary among pools.

1. `FeesVotingReward` and `BribeVotingReward` contracts associated with a given pool can be fetched respectively using `Voter`'s [`poolToFees()`](/technical-reference/contract-functions/voter#pooltofees) and [`poolToBribe()`](/technical-reference/contract-functions/voter#pooltobribe).
2. The list of all reward tokens for a given `FeeVotingReward` or `BribeVotingReward` can be fetched using [`getAllRewards()`](/technical-reference/contract-functions/votingreward#getallrewards) getter, which takes an index comprised between 0 and `VotingReward`'s [`rewardsListLength()`](/technical-reference/contract-functions/votingreward#rewardslistlength).
3. Earned rewards for a given user and a specific reward token can be fetched using `VotingReward`'s [`earned()`](/technical-reference/contract-functions/votingreward#earned).

### 4. Claim voting rewards

Each week, voting rewards for the previous week can be claimed.

Claim voting rewards for a specific list of pools and specific lists of bribes and fees reward token, using `Voter's` [`claimPoolsVotingRewards()`](/technical-reference/contract-functions/voter#claimpoolsvotingrewards).

{% hint style="info" %}
Bribes and fees rewards can also be claimed separately using [`claimBribes()`](/technical-reference/contract-functions/voter#claimbribes) and [`claimFees()`](/technical-reference/contract-functions/voter#claimfees).
{% endhint %}


# Deployed Contracts

{% hint style="info" %}
All deployed contracts are verified on their respective block explorer.
{% endhint %}

{% tabs %}
{% tab title="Ethereum" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://etherscan.io/address/0xDbbfc051D200438dd5847b093B22484B842de9E7">0xDbbfc051D200438dd5847b093B22484B842de9E7</a></td></tr><tr><td>Registry</td><td><a href="https://etherscan.io/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr><tr><td>Factory</td><td><a href="https://hyperevmscan.io/address/0xd187cb71fe8201935e6676ff872239fff552d4a5">0xd187cb71fe8201935e6676ff872239fff552d4a5</a></td></tr><tr><td>Router</td><td><a href="https://etherscan.io/address/0xC03309DE321A4D3df734F5609B80cC731ae28e6D">0xC03309DE321A4D3df734F5609B80cC731ae28e6D</a></td></tr><tr><td>RouterUtil</td><td><a href="https://etherscan.io/address/0x22aF2e9e8633e687acC8412FA9A90Ae9EE95F69C">0x22aF2e9e8633e687acC8412FA9A90Ae9EE95F69C</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://etherscan.io/address/0x6a89228055c7c28430692e342f149f37462b478b">0x6a89228055c7c28430692e342f149f37462b478b</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://etherscan.io/address/0xE9888a305946ceDd6972b7A16D6fd1CCf19b696c">0xE9888a305946ceDd6972b7A16D6fd1CCf19b696c</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://etherscan.io/address/0x63D5ddb89E3d69Fa34c2A3fe633EdB5bF729EAdE">0x63D5ddb89E3d69Fa34c2A3fe633EdB5bF729EAdE</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://etherscan.io/address/0xd68839c0Cb4bE12bdF645BC2B448Acd55d2B28A3">0xd68839c0Cb4bE12bdF645BC2B448Acd55d2B28A3</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://etherscan.io/address/0x46500Eb791fB63559A53e577e9f4b2794fca7e9f">0x46500Eb791fB63559A53e577e9f4b2794fca7e9f</a></td></tr><tr><td>Access Manager</td><td><a href="https://etherscan.io/address/0x7EA3097E2AF59eA705398544e0f58EdDb7bd1852">0x7EA3097E2AF59eA705398544e0f58EdDb7bd1852</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Arbitrum" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://arbiscan.io/address/0x417c5997fc9f1fD341742C71dE9b1908028Fe381">0x417c5997fc9f1fD341742C71dE9b1908028Fe381</a></td></tr><tr><td>Registry</td><td><a href="https://arbiscan.io/address/0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378">0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378</a></td></tr><tr><td>Factory</td><td><a href="https://arbiscan.io/address/0x9055eBE4E01040c0c4a6D9Bb84a13188981c62D4">0x9055eBE4E01040c0c4a6D9Bb84a13188981c62D4</a></td></tr><tr><td>Router</td><td><a href="https://arbiscan.io/address/0x38b9B4884a5581E96eD3882AA2f7449BC321786C">0x38b9B4884a5581E96eD3882AA2f7449BC321786C</a></td></tr><tr><td>RouterUtil</td><td><a href="https://arbiscan.io/address/0x7256EFDadF266C0ed10ebb77C47790eC5E961AAC">0x7256EFDadF266C0ed10ebb77C47790eC5E961AAC</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://arbiscan.io/address/0x64fcc3a02eeeba05ef701b7eed066c6ebd5d4e51">0x64fcc3a02eeeba05ef701b7eed066c6ebd5d4e51</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://arbiscan.io/address/0x4baB31D6c557F8285eccB5167095147a36D9BaFa">0x4baB31D6c557F8285eccB5167095147a36D9BaFa</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://arbiscan.io/address/0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7">0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://arbiscan.io/address/0x51C002aBe20bD7C5072cf96Ba979562E42700F20">0x51C002aBe20bD7C5072cf96Ba979562E42700F20</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://arbiscan.io/address/0x4baB31D6c557F8285eccB5167095147a36D9BaFa">0x4baB31D6c557F8285eccB5167095147a36D9BaFa</a></td></tr><tr><td>Access Manager</td><td><a href="https://arbiscan.io/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Optimism" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://optimistic.etherscan.io/address/0xF8576B3830FA131bBd39dA1E97Fbb24864156470">0xF8576B3830FA131bBd39dA1E97Fbb24864156470</a></td></tr><tr><td>Registry</td><td><a href="https://optimistic.etherscan.io/address/0x0458c078fCf527DA293Ec9e813a0DcAF9F949EB1">0x0458c078fCf527DA293Ec9e813a0DcAF9F949EB1</a></td></tr><tr><td>Factory</td><td><a href="https://optimistic.etherscan.io/address/0x3945ce79F528906c232c6834D00c8F6A218b8BF5">0x3945ce79F528906c232c6834D00c8F6A218b8BF5</a></td></tr><tr><td>Router</td><td><a href="http://optimistic.etherscan.io/address/0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119">0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119</a></td></tr><tr><td>RouterUtil</td><td><a href="https://optimistic.etherscan.io/address/0x2811B38F354d317716C0D35C3Cfb9825b6BAC642">0x2811B38F354d317716C0D35C3Cfb9825b6BAC642</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://optimistic.etherscan.io/address/0x248f43b622ce2f35a14db3fc528284730b619cd5?__cf_chl_rt_tk=nd.ODDffEq5xFHO6pQNyhi7hif9UMwr0HgijJsgQKiY-1746430229-1.0.1.1-_GLf6gcs6g4e5q28vfrZzfwOk._EMDNbGRzdg4V.vvA">0x248f43b622ce2f35a14db3fc528284730b619cd5</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://optimistic.etherscan.io/address/0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7">0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://optimistic.etherscan.io/address/0xD733e545C65d539f588d7c3793147B497403F0d2">0xD733e545C65d539f588d7c3793147B497403F0d2</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://optimistic.etherscan.io/address/0x3EdfaC40e3EE7a26D03393ac44918c53e7f90bad">0x3EdfaC40e3EE7a26D03393ac44918c53e7f90bad</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://optimistic.etherscan.io/address/0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7">0x4eaFef6149C5B0c3E42fF444F79675B3E3125cb7</a></td></tr><tr><td>Access Manager</td><td><a href="https://optimistic.etherscan.io/address/0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378">0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Base" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://basescan.org/address/0xe59d75C87ED608E4f5F22c9f9AFFb7b6fd02cc7C">0xe59d75C87ED608E4f5F22c9f9AFFb7b6fd02cc7C</a></td></tr><tr><td>Registry</td><td><a href="https://basescan.org/address/0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378">0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378</a></td></tr><tr><td>Factory</td><td><a href="https://basescan.org/address/0xDBE5B6AaC70EeA77C5b59B6c54D8F21DffAA8D84">0xDBE5B6AaC70EeA77C5b59B6c54D8F21DffAA8D84</a></td></tr><tr><td>Router</td><td><a href="http://basescan.org/address/0xC03309DE321A4D3df734F5609B80cC731ae28e6D">0xC03309DE321A4D3df734F5609B80cC731ae28e6D</a></td></tr><tr><td>RouterUtil</td><td><a href="https://basescan.org/address/0x22aF2e9e8633e687acC8412FA9A90Ae9EE95F69C">0x22aF2e9e8633e687acC8412FA9A90Ae9EE95F69C</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://basescan.org/address/0x64fcc3a02eeeba05ef701b7eed066c6ebd5d4e51">0x64fcc3a02eeeba05ef701b7eed066c6ebd5d4e51</a></td></tr><tr><td>veSPECTRA</td><td><a href="https://basescan.org/address/0x6a89228055C7C28430692E342F149f37462B478B">0x6a89228055C7C28430692E342F149f37462B478B</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://basescan.org/address/0xbb024f4C04B21BdC43582395f050D4ea00adC7a0">0xbb024f4C04B21BdC43582395f050D4ea00adC7a0</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://basescan.org/address/0xA9c6A044c5Af071ca9321F94B1f5F5971Cd9c3e6">0xA9c6A044c5Af071ca9321F94B1f5F5971Cd9c3e6</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://basescan.org/address/0x63D5ddb89E3d69Fa34c2A3fe633EdB5bF729EAdE">0x63D5ddb89E3d69Fa34c2A3fe633EdB5bF729EAdE</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://basescan.org/address/0xbb024f4C04B21BdC43582395f050D4ea00adC7a0">0xbb024f4C04B21BdC43582395f050D4ea00adC7a0</a></td></tr><tr><td>Access Manager</td><td><a href="https://basescan.org/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Sonic" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://sonicscan.org/address/0x589269998df4D7D16351AA2Ff996486aEc1DB6C2">0x589269998df4D7D16351AA2Ff996486aEc1DB6C2</a></td></tr><tr><td>Registry</td><td><a href="https://sonicscan.org/address/0xcb671f588c85E1403ecB9B4f6dA0dff0d1e9D3FB">0xcb671f588c85E1403ecB9B4f6dA0dff0d1e9D3FB</a></td></tr><tr><td>Factory</td><td><a href="https://sonicscan.org/address/0x100F22121d8c86367B14bA67968DCA8001C9FA79">0x100F22121d8c86367B14bA67968DCA8001C9FA79</a></td></tr><tr><td>Router</td><td><a href="https://sonicscan.org/address/0x3322664deE30345024F62066145427A8e4e67703">0x3322664deE30345024F62066145427A8e4e67703</a></td></tr><tr><td>RouterUtil</td><td><a href="https://sonicscan.org/address/0xBE6271FA207D2cD29C7F9efa90FC725C18560bff">0xBE6271FA207D2cD29C7F9efa90FC725C18560bff</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://sonicscan.org/address/0xb827e91c5cd4d6aca2fc0cd93a07db61896af40b">0xb827e91c5cd4d6aca2fc0cd93a07db61896af40b</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://sonicscan.org/address/0x55076E45a3ab31c4D9f92F63f0b7801c74B986f8">0x55076E45a3ab31c4D9f92F63f0b7801c74B986f8</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://sonicscan.org/address/0x1F98F0EB72505e6A319431507Aa7F05F6322f88B">0x1F98F0EB72505e6A319431507Aa7F05F6322f88B</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://sonicscan.org/address/0x938D4948dFE59fb36667E1cd0dc41BBC076707c1">0x938D4948dFE59fb36667E1cd0dc41BBC076707c1</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://sonicscan.org/address/0x55076E45a3ab31c4D9f92F63f0b7801c74B986f8">0x55076E45a3ab31c4D9f92F63f0b7801c74B986f8</a></td></tr><tr><td>Access Manager</td><td><a href="https://sonicscan.org/address/0x8f2cE1f5B4811b97b3AD3c8721F0A4676002c3B8">0x8f2cE1f5B4811b97b3AD3c8721F0A4676002c3B8</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Hemi" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://explorer.hemi.xyz/address/0x6f1C9528d22189CDbC6838ef51E3f5aB6Aa3A093">0x6f1C9528d22189CDbC6838ef51E3f5aB6Aa3A093</a></td></tr><tr><td>Registry</td><td><a href="https://explorer.hemi.xyz/address/0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378">0x786Da12e9836a9ff9b7d92e8bac1C849e2ACe378</a></td></tr><tr><td>Factory</td><td><a href="https://explorer.hemi.xyz/address/0x2c06c9D02a3455F1B22B9365EB76Bf558dB1B947">0x2c06c9D02a3455F1B22B9365EB76Bf558dB1B947</a></td></tr><tr><td>Router</td><td><a href="https://explorer.hemi.xyz/address/0xa3eeA13183421c9A8BDA0BDEe191B70De8CA445D">0xa3eeA13183421c9A8BDA0BDEe191B70De8CA445D</a></td></tr><tr><td>RouterUtil</td><td><a href="https://explorer.hemi.xyz/address/0xB77f1A8CB126D8567f226f990F84e2f698Cc30F8">0xB77f1A8CB126D8567f226f990F84e2f698Cc30F8</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://explorer.hemi.xyz/address/0x392fca63e58C1870fBeC04Eb6518A75703Dd2954">0x392fca63e58C1870fBeC04Eb6518A75703Dd2954</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://explorer.hemi.xyz/address/0x938d4948dfe59fb36667e1cd0dc41bbc076707c1">0x938d4948dfe59fb36667e1cd0dc41bbc076707c1</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://explorer.hemi.xyz/address/0x6a89228055C7C28430692E342F149f37462B478B">0x6a89228055C7C28430692E342F149f37462B478B</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://explorer.hemi.xyz/address/0x9055eBE4E01040c0c4a6D9Bb84a13188981c62D4">0x9055eBE4E01040c0c4a6D9Bb84a13188981c62D4</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://explorer.hemi.xyz/address/0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1">0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1</a></td></tr><tr><td>Access Manager</td><td><a href="https://explorer.hemi.xyz/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr></tbody></table>
{% endtab %}

{% tab title="HyperEVM" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>Registry</td><td><a href="https://hyperevmscan.io/address/0xba4f8ef69d6d5cb48efb9149bf33ea43d6b66ccd#code">0xBA4F8ef69D6d5CB48efB9149BF33Ea43d6b66CCd</a></td></tr><tr><td>Factory</td><td><a href="http://etherscan.io/address/0xDBE5B6AaC70EeA77C5b59B6c54D8F21DffAA8D84">0xDBE5B6AaC70EeA77C5b59B6c54D8F21DffAA8D84</a></td></tr><tr><td>Router</td><td><a href="https://hyperevmscan.io/address/0xb827e91c5cd4d6aca2fc0cd93a07db61896af40b">0xb827E91C5cd4d6aCa2FC0cD93A07dB61896Af40B</a></td></tr><tr><td>RouterUtil</td><td><a href="https://hyperevmscan.io/address/0xf19eaaab5432086eeedc7e5e24007202da2b5420">0xF19EaaAB5432086EeeDc7e5E24007202dA2B5420</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://hyperevmscan.io/address/0x6bd93ee39bcc7b9baba122c2ba65246e4347bbf9">0x6BD93eE39bcC7B9Baba122c2Ba65246E4347BBF9</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://hyperevmscan.io/address/0x8a92294ffcfe469a3df4a85c76a0b0d2b3292119">0x8a92294ffcfe469a3df4a85c76a0b0d2b3292119</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://hyperevmscan.io/address/0x765883c6be92ec43b319d0886f3504e4fa81cce1">0x765883c6Be92eC43b319d0886F3504E4fA81CCe1</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://hyperevmscan.io/address/0x35726a51982d3d89efa89d437bfd5603dc59f46d">0x35726a51982D3D89EFa89D437bfd5603DC59f46D</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://hyperevmscan.io/address/0x8a92294ffcfe469a3df4a85c76a0b0d2b3292119">0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119</a></td></tr><tr><td>Access Manager</td><td><a href="https://hyperevmscan.io/address/0x071350a9ee4d153c272fc75ea9557372eb6ce0a0">0x071350a9ee4d153c272fc75ea9557372eb6ce0a0</a></td></tr></tbody></table>

{% endtab %}

{% tab title="Katana" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>DAO</td><td><a href="https://katanascan.com/address/0xe59d75c87ed608e4f5f22c9f9affb7b6fd02cc7c">0xe59d75c87ed608e4f5f22c9f9affb7b6fd02cc7c</a></td></tr><tr><td>Registry</td><td><a href="https://katanascan.com/address/0x09176eacaa413cc0722aa5ad716820e8f19682b7">0x09176eacaa413cc0722aa5ad716820e8f19682b7</a></td></tr><tr><td>Factory</td><td><a href="https://katanascan.com/address/0xac61e44816160028474fd9f5baae492b5620f370">0xac61e44816160028474fd9f5baae492b5620f370</a></td></tr><tr><td>Router</td><td><a href="https://katanascan.com/address/0x15e4f4d96263a237111e9b418efd5af66a303bc7">0x15e4f4d96263a237111e9b418efd5af66a303bc7</a></td></tr><tr><td>RouterUtil</td><td><a href="https://katanascan.com/address/0x4425779f145f6599cfceaa9443b497a7a2dfdb17">0x4425779f145f6599cfceaa9443b497a7a2dfdb17</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://katanascan.com/address/0xb77f1a8cb126d8567f226f990f84e2f698cc30f8">0xb77f1a8cb126d8567f226f990f84e2f698cc30f8</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://katanascan.com/address/0x22cdf5fd02b76339126f79cc601a1be6fe9c2701">0x22cdf5fd02b76339126f79cc601a1be6fe9c2701</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://katanascan.com/address/0x6cba8213deeafe86ffb38f295edd5625cae4dd05">0x6cba8213deeafe86ffb38f295edd5625cae4dd05</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://katanascan.com/address/0x90f584a7afa70eca0cf073082ab0ec95e5efe38a">0x90f584a7afa70eca0cf073082ab0ec95e5efe38a</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://katanascan.com/address/0x2c06c9d02a3455f1b22b9365eb76bf558db1b947">0x2c06c9d02a3455f1b22b9365eb76bf558db1b947</a></td></tr><tr><td>Access Manager</td><td><a href="https://explorer.katanarpc.com/address/0x5291Af3124a7Be15f4C1a3fe22548E7Ba8c16653">0x5291af3124a7be15f4c1a3fe22548e7ba8c16653</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Avalanche" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>Registry</td><td><a href="https://snowscan.xyz/address/0x248f43B622cE2F35A14dB3FC528284730b619cD5">0x248f43B622cE2F35A14dB3FC528284730b619cD5</a></td></tr><tr><td>Factory</td><td><a href="https://snowscan.xyz/address/0x64FCC3A02eeEba05Ef701b7eed066c6ebD5d4E51">0x64FCC3A02eeEba05Ef701b7eed066c6ebD5d4E51</a></td></tr><tr><td>Router</td><td><a href="https://snowscan.xyz/address/0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119">0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119</a></td></tr><tr><td>RouterUtil</td><td><a href="https://snowscan.xyz/address/0x2811B38F354d317716C0D35C3Cfb9825b6BAC642">0x2811B38F354d317716C0D35C3Cfb9825b6BAC642</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://snowscan.xyz/address/0x4baB31D6c557F8285eccB5167095147a36D9BaFa">0x4baB31D6c557F8285eccB5167095147a36D9BaFa</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://snowscan.xyz/address/0xD733e545C65d539f588d7c3793147B497403F0d2">0xD733e545C65d539f588d7c3793147B497403F0d2</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://snowscan.xyz/address/0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1">0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://snowscan.xyz/address/0x63a642dcd91ab4D579Ec45181945DF1e1e95d6b4">0x63a642dcd91ab4D579Ec45181945DF1e1e95d6b4</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://snowscan.xyz/address/0xD733e545C65d539f588d7c3793147B497403F0d2">0xD733e545C65d539f588d7c3793147B497403F0d2</a></td></tr><tr><td>Access Manager</td><td><a href="https://snowscan.xyz/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr></tbody></table>
{% endtab %}

{% tab title="BNB" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th></th></tr></thead><tbody><tr><td>Registry</td><td><a href="https://bscscan.com/address/0x248f43B622cE2F35A14dB3FC528284730b619cD5">0x248f43B622cE2F35A14dB3FC528284730b619cD5</a></td></tr><tr><td>Factory</td><td><a href="https://bscscan.com/address/0x64FCC3A02eeEba05Ef701b7eed066c6ebD5d4E51">0x64FCC3A02eeEba05Ef701b7eed066c6ebD5d4E51</a></td></tr><tr><td>Router</td><td><a href="https://bscscan.com/address/0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119">0x8A92294ffCFe469a3DF4A85c76a0B0d2B3292119</a></td></tr><tr><td>RouterUtil</td><td><a href="https://bscscan.com/address/0x2811B38F354d317716C0D35C3Cfb9825b6BAC642">0x2811B38F354d317716C0D35C3Cfb9825b6BAC642</a></td></tr><tr><td>SPECTRA Token</td><td><a href="https://bscscan.com/address/0x4baB31D6c557F8285eccB5167095147a36D9BaFa">0x4baB31D6c557F8285eccB5167095147a36D9BaFa</a></td></tr><tr><td>spectraPriceOracleFactoryProxy</td><td><a href="https://bscscan.com/address/0xD733e545C65d539f588d7c3793147B497403F0d2">0xD733e545C65d539f588d7c3793147B497403F0d2</a></td></tr><tr><td>zeroCouponDiscountModelProxy</td><td><a href="https://bscscan.com/address/0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1">0xb385afdF3A033ebD06dC182D83CAf794bDEa2Ce1</a></td></tr><tr><td>linearDiscountModelProxy</td><td><a href="https://bscscan.com/address/0x63a642dcd91ab4D579Ec45181945DF1e1e95d6b4">0x63a642dcd91ab4D579Ec45181945DF1e1e95d6b4</a></td></tr><tr><td>oracleFactoryProxy</td><td><a href="https://bscscan.com/address/0xD733e545C65d539f588d7c3793147B497403F0d2">0xD733e545C65d539f588d7c3793147B497403F0d2</a></td></tr><tr><td>Access Manager</td><td><a href="https://bscscan.com/address/0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e">0x4973b53b300d64ab72147EFF8C9d962f6b1dA02e</a></td></tr></tbody></table>
{% endtab %}
{% endtabs %}


# Contract Functions

The following pages delve deeper into the smart contracts that power Spectra.

All code is open source and [available on GitHub](https://github.com/APWine/core-v2).

**\[insert image of contract flow chart]**


# Principal Token

The Principal Token is the main contract of Spectra. It is EIP [5095](https://eips.ethereum.org/EIPS/eip-5095), and [2612](https://eips.ethereum.org/EIPS/eip-2612) compliant.&#x20;

Users deposit the [IBT](/glossary#ibt) (or underlying token of the IBT) and receive the Principal Token (PT) and the [Yield Token](/glossary#yield-token) (YT) in return. For example, user deposits aDAI (or DAI) and receives the PT and YT of the aDAI position with a certain expiry.

The user can also [withdraw](#withdraw) and [redeem](#redeem) after and before expiry. For more information, see the [Tokenising Yield](/guides/tokenizing-yield) guide.

Code for PrincipalToken.sol can be found on [GitHub](https://github.com/perspectivefi/spectra-core/blob/main/src/tokens/PrincipalToken.sol).

## Methods

### deposit

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

Deposits the amount of the underlying `assets` (e.g. DAI or USDC etc) and mints an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and [Yield Tokens](/glossary#yield-token) for the `receiver`.

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the underlying asset before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td><p>The amount of the underlying assets to deposit. </p><p>See also <a href="#asset"><code>asset()</code></a></p></td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a> and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">YT</a>.<br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### deposit

```solidity
function deposit(
        uint256 assets,
        address ptReceiver,
        address ytReceiver
) external returns (uint256 shares);
```

Deposits amount of `assets` in the PT vault specifying the PT receiver (`ptReceiver`) and the YT receiver (`ytReceiver`).

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the underlying asset before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td><p>The amount of the underlying assets to deposit. </p><p>See also <a href="#asset"><code>asset()</code></a></p></td></tr><tr><td><code>ptReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>ytReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/5dOBrIC1j37RFhQGMWuS">YT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### deposit

```solidity
function deposit(
    uint256 assets,
    address ptReceiver,
    address ytReceiver,
    uint256 minShares
) external returns (uint256 shares);
```

Deposits amount of `assets` with a lower bound on shares received (`minShares`).

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the underlying asset before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td><p>The amount of the underlying assets to deposit. </p><p>See also <a href="#asset"><code>asset()</code></a></p></td></tr><tr><td><code>ptReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>ytReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/5dOBrIC1j37RFhQGMWuS">YT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>minShares</code></td><td>uint256</td><td>The minimum amount of shares the caller expect to receive from the posit</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### depositIBT

```solidity
function depositIBT(
    uint256 ibts,
    address receiver
) public returns (uint256 shares)
```

Deposits the `ibts` of the [Interest Bearing Token](/glossary#ibt) (e.g. aDAI or aUSDC etc) and mints an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and [Yield Tokens](/glossary#yield-token) for the `receiver`.

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the IBT before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td><p>The amount of the PrincipalToken's IBT assets to deposit. </p><p>See also <a href="#getibt">getIBT()</a></p></td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a> and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">YT</a>.<br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### depositIBT

```solidity
function depositIBT(
        uint256 ibts,
        address ptReceiver,
        address ytReceiver
) external returns (uint256 shares);
```

Deposits the `ibts` of the [Interest Bearing Token](/glossary#ibt) (e.g. aDAI or aUSDC etc) and mints an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) for the `ptReceiver` and [Yield Tokens](/glossary#yield-token) for the `ytReceiver`.

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the IBT before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td><p>The amount of the PrincipalToken's IBT assets to deposit. </p><p>See also <a href="#getibt">getIBT()</a></p></td></tr><tr><td><code>ptReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>ytReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/5dOBrIC1j37RFhQGMWuS">YT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### depositIBT

```solidity
function depositIBT(
        uint256 ibts,
        address ptReceiver,
        address ytReceiver,
        uint256 minShares
) external returns (uint256 shares);
```

Deposits the `ibts` of the [Interest Bearing Token](/glossary#ibt) (e.g. aDAI or aUSDC etc) and mints an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) for the `ptReceiver` and [Yield Tokens](/glossary#yield-token) for the `ytReceiver`.

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the IBT before calling this method.
{% endhint %}

{% hint style="warning" %}
`deposit` must be called before the [`expiry`](#maturity) of the Principal Token.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td><p>The amount of the PrincipalToken's IBT assets to deposit. </p><p>See also <a href="#getibt">getIBT()</a></p></td></tr><tr><td><code>ptReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">PT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>ytReceiver</code></td><td>address</td><td>The address that will receive the minted <a href="/pages/5dOBrIC1j37RFhQGMWuS">YT</a><br>Note: This can be different from <code>msg.sender</code> if depositing on behalf of another address.</td></tr><tr><td><code>minShares</code></td><td>uint256</td><td>The minimum amount of shares the caller expect to receive from the posit</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) and <a href="/pages/4zYR05giizVJ8mW5t1Ht#yield-token">Yield Tokens</a> that were minted for the <code>receiver</code>.</td></tr></tbody></table>

### redeem

```solidity
function redeem(
    uint256 shares,
    address receiver,
    address owner
) returns (uint256 assets)
```

Redeems (by burning tokens) the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) from `owner`, redeeming an amount of `assets` of the underlying [asset](#asset).

{% hint style="warning" %}
The first caller to Redeem after expiry will make an implicit call to [`StoreRatesAtExpiry()`](#storeratesatexpiry)to store the rates after expiry. This is done only once for all.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that the user wants to redeem/burn.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed <a href="#asset">assets</a>.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be redeemed.<br>This must be the same as <code>msg.sender</code>.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>The amount of underlying <a href="#asset">assets</a> that are redeemed.</td></tr></tbody></table>

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### redeem

```solidity
function redeem(
        uint256 shares,
        address receiver,
        address owner,
        uint256 minAssets
) external returns (uint256 assets);
```

Redeems (by burning tokens) the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) from `owner`, redeeming an amount of `assets` of the underlying [asset](#asset), while also specifying the minimum asset (`minAssets`) amount that the caller expects the `receiver` to receive.

I.e. converts PT to the underlying asset, using the number of PT to burn, after expiry.

{% hint style="warning" %}
The first caller to Redeem after expiry will make an implicit call to [`StoreRatesAtExpiry()`](#storeratesatexpiry)to store the rates after expiry. This is done only once for all.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that the user wants to redeem/burn.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed <a href="#asset">assets</a>.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be redeemed.<br>This must be the same as <code>msg.sender</code>.</td></tr><tr><td><code>minAssets</code></td><td>uint256</td><td>The minimum asset amount that the caller expects the <code>receiver</code> to receive.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>The amount of underlying <a href="#asset">assets</a> that are redeemed.</td></tr></tbody></table>

### redeemForIBT

```solidity
function redeemForIBT(
    uint256 shares,
    address receiver,
    address owner
) returns (uint256 ibts)
```

Redeems (by burning tokens) the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) from `owner`, redeeming an amount of `ibts` of the [IBT](/glossary#ibt).

{% hint style="warning" %}
The first caller to Redeem after expiry will make an implicit call to [`StoreRatesAtExpiry()`](#storeratesatexpiry)to store the rates after expiry. This is done only once for all.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that the user wants to redeem/burn.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed tokens.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be redeemed.<br>This must be the same as <code>msg.sender</code>.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a>s that are redeemed.</td></tr></tbody></table>

### redeemForIBT

```solidity
function redeemForIBT(
        uint256 shares,
        address receiver,
        address owner,
        uint256 minIbts
) external returns (uint256 ibts);
```

Redeems (by burning tokens) the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) from `owner`, redeeming an amount of `ibts` of the [IBT](/glossary#ibt), while also specifying the minimum asset (`minIbts`) amount that the caller expects the `receiver` to receive.

{% hint style="warning" %}
The first caller to Redeem after expiry will make an implicit call to [`StoreRatesAtExpiry()`](#storeratesatexpiry)to store the rates after expiry. This is done only once for all.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that the user wants to redeem/burn.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed tokens.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be redeemed.<br>This must be the same as <code>msg.sender</code>.</td></tr><tr><td><code>minIbts</code></td><td>uint256</td><td>The minimum asset amount that the caller expects the <code>receiver</code> to receive.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a>s that are redeemed.</td></tr></tbody></table>

### withdraw

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

Withdraws the amount of underlying `assets` of a position of `owner`, withdrawing those `assets` to `receiver`.  This is done by burning `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and remaining [Yield Tokens](/glossary#yield-token) of `owner`.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>The amount of <a href="#asset">assets</a> that the user wants to withdraw.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed <a href="#asset">assets</a>.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be withdrawn/burned.<br>This must be the same as <code>msg.sender</code>.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of the <code>owner</code> shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that were burned from the withdrawal.</td></tr></tbody></table>

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### withdraw

```solidity
function withdraw(
        uint256 assets,
        address receiver,
        address owner,
        uint256 maxShares
) external returns (uint256 shares)
```

Withdraws the amount of underlying `assets` of a position of `owner`, withdrawing those `assets` to `receiver`.  This is done by burning `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and remaining [Yield Tokens](/glossary#yield-token) of `owner`, while specifying the maximum amount (`maxShares`) of shares to withdraw.

I.e. converts PT and YT to the underlying asset, using the number of underlying assets to withdraw, before expiry.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>The amount of <a href="#asset">assets</a> that the user wants to withdraw.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the redeemed <a href="#asset">assets</a>.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be withdrawn/burned.<br>This must be the same as <code>msg.sender</code>.</td></tr><tr><td><code>maxShares</code></td><td>uint256</td><td>The maximum amount of shares allowed to be burnt</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of the <code>owner</code> shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that were burned from the withdrawal.</td></tr></tbody></table>

### withdrawIBT

```solidity
function withdrawIBT(
    uint256 ibts,
    address receiver,
    address owner
) returns (uint256 shares)
```

Withdraws the amount of [IBT](/glossary#ibt)s of a position of `owner`, withdrawing those `ibts` to `receiver`.  This is done by burning `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and remaining [Yield Tokens](/glossary#yield-token) of `owner`.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a>s that the user wants to withdraw.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the withdrawn tokens.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be withdrawn/burned.<br>This must be the same as <code>msg.sender</code>.</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of the <code>owner</code> shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that were burned from the withdrawal.</td></tr></tbody></table>

### withdrawIBT

```solidity
function withdrawIBT(
        uint256 ibts,
        address receiver,
        address owner,
        uint256 maxShares
) external returns (uint256 shares)
```

Withdraws the amount of [IBT](/glossary#ibt)s of a position of `owner`, withdrawing those `ibts` to `receiver`.  This is done by burning `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and remaining [Yield Tokens](/glossary#yield-token) of `owner`, while specifying the maximum amount (`maxShares`) of shares to withdraw.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a>s that the user wants to withdraw.</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address that will receive the withdrawn tokens.</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the <code>shares</code> (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that are to be withdrawn/burned.<br>This must be the same as <code>msg.sender</code>.</td></tr><tr><td><code>maxShares</code></td><td>uint256</td><td>The maximum amount of shares allowed to be burnt</td></tr></tbody></table>

<table><thead><tr><th width="197.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of the <code>owner</code> shares (i.e. <a href="/pages/4zYR05giizVJ8mW5t1Ht#principal-token">Principal Tokens</a>) that were burned from the withdrawal.</td></tr></tbody></table>

### claimYield

```solidity
function claimYield(address _receiver) public returns (uint256)
```

Claims the yield of `msg.sender` based on their current balance of YT. Send the yield as underlying tokens.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_receiver</code></td><td>address</td><td>The address that will receive the yield.</td></tr></tbody></table>

<table><thead><tr><th width="214.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>yieldInAsset</code></td><td>uint256</td><td>The amount of yield claimed in the underlying <a href="#asset">asset</a>.</td></tr></tbody></table>

### claimYieldInIBT

```solidity
function claimYieldInIBT(address _receiver) public returns (uint256)
```

Claims the yield of `msg.sender` based on their current balance of YT. Send the yield as [IBT](/glossary#ibt) tokens.

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_receiver</code></td><td>address</td><td>The address that will receive the yield.</td></tr></tbody></table>

<table><thead><tr><th width="214.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>yieldInIBT</code></td><td>uint256</td><td>The amount of yield claimed (in <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a>).</td></tr></tbody></table>

### updateYield

```solidity
function updateYield(address _user) external returns (uint256 updatedUserYieldInRay)
```

Updates `_user` yield with the latest yield in IBT generated since the previous update.&#x20;

{% hint style="info" %}
This method should not be used by users. This method is called implicitely before each action to deposit, redeem, withdraw and sending receiving YT tokens.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="124">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address of the user to compute the yield</td></tr></tbody></table>

<table><thead><tr><th width="274.3333333333333">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>updatedUserYieldInRay</code></td><td>uint256</td><td>The unclaimed yield (amount of <a href="/pages/4zYR05giizVJ8mW5t1Ht#ibt">IBT</a> tokens) of the <code>_user</code> in Ray decimals. The user can get this yield by calling <a href="#claimyield"><code>claimYield()</code></a> .</td></tr></tbody></table>

### flashLoan

```solidity
function flashLoan(
        IERC3156FlashBorrower _receiver,
        address _token,
        uint256 _amount,
        bytes calldata _data
    ) external override returns (bool)
```

The flashLoan method is used to swap PT for YT tokens by borrowing the IBT of the PT contract to swap PT for YTs.

{% hint style="warning" %}
The transaction reverts if the trade is not profitable and the borrower is unable to repay the loan.
{% endhint %}

<table><thead><tr><th width="195.33333333333331">Input Parameter</th><th width="227">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_receiver</code></td><td>IERC3156FlashBorrower</td><td>The receiver of the tokens in the loan, and the receiver of the callback.</td></tr><tr><td><code>_token</code></td><td>address</td><td>The loan currency.</td></tr><tr><td><code>_amount</code></td><td>uint256</td><td>The amount of tokens lent.</td></tr><tr><td><code>_data</code></td><td>bytes calldata</td><td>Arbitrary data structure, intended to contain user-defined parameters.</td></tr></tbody></table>

<table><thead><tr><th width="214.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td></td><td>bool</td><td>If successful, <code>flashLoan</code> return <code>true</code>.</td></tr></tbody></table>

*Conforms to* [*EIP-3156*](https://eips.ethereum.org/EIPS/eip-3156) *standards.*

### StoreRatesAtExpiry

Store the rates at the time of expiry.&#x20;

{% hint style="warning" %}
This can only be called **after** [expiry](#maturity)[/maturity](#maturity) of the position.&#x20;
{% endhint %}

```solidity
function storeRatesAtExpiry() external
```

## View Methods

### previewDeposit

```solidity
function previewDeposit(uint256 assets) public view returns (uint256)
```

Calculates the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and [Yield Tokens](/glossary#yield-token) that would be minted for [depositing](#deposit) amount of `assets.`Only available if position is not [expired/reached maturity.](#maturity)

### maxDeposit

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

This function returns the maximum amount of underlying assets that can be deposited in a single [`deposit`](#deposit) call by the `receiver`.

### previewDepositIBT

```solidity
function previewDepositIBT(uint256 ibts) public view returns (uint256)
```

Calculates the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) and [Yield Tokens](/glossary#yield-token) that would be minted for [depositing](#deposit) amount of `ibts.`Only available if position is not [expired/reached maturity.](#maturity)

### previewWithdraw

```solidity
function previewWithdraw(uint256 assets) public view returns (uint256)
```

Calculates the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) that would be burned for [withdrawing](#withdraw) an amount of `assets`.

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### previewWithdrawIBT

```solidity
function previewWithdrawInIBT(uint256 ibts) public view returns (uint256)
```

Calculates the amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)) that would be burned for [withdrawing](#withdraw) an amount of `ibts`.

### maxWithdraw

```solidity
function maxWithdraw(address owner) public returns (uint256)
```

Calculates the maximum amount of underlying [assets](#asset) that can [withdrawn](#withdraw) by the `owner`.

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### maxWithdrawIBT

```solidity
function maxWithdraw(address owner) public returns (uint256)
```

Calculates the maximum amount of [IBT](/glossary#ibt)s that can [withdrawn](#withdraw) by the `owner`.

### previewRedeem

```solidity
function previewRedeem(uint256 shares) public view returns (uint256)
```

Calculates the amount of [assets](#asset) that would be [redeemed](#redeem) for an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)).

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### previewRedeemForIBT

```solidity
function previewRedeemForIBT(uint256 shares) public view returns (uint256)
```

Calculates the amount of IBTs that would be [redeemed](#redeem) for an amount of `shares` (i.e. [Principal Tokens](/glossary#principal-token)).

### maxRedeem

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

Calculations the maximum amount of shares (i.e. [Principal Tokens](/glossary#principal-token)) that the `owner` can currently [redeem](#redeem)/burn.

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standards.*

### convertToPrincipal

```solidity
function convertToPrincipal(uint256 underlyingAmount) public view returns (uint256 principalAmount)
```

Calculates the amount of principal tokens that are equivalent to the amount of `underlyingAmount`.&#x20;

*Conforms to* [*EIP-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standard.*

### convertToUnderlying

```solidity
function convertToUnderlying(uint256 principalAmount) public view returns (uint256)
```

Same as [`convertToAssets()`](#converttoassets).

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standard.*

### underlying

```solidity
function underlying() public view virtual override returns (address)
```

Returns the address of the underlying ERC20 or ERC777 asset.&#x20;

E.g. if the IBT is aDAI, then the asset is DAI. If the IBT is cUSDC, then the asset is USDC.

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standard.*

### totalAssets

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

Returns the total balance of the underlying assets in the [Principal Token](/glossary#principal-token). Useful for TVL calculations.

### decimals

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

Returns the decimals of the [IBT](#ibt).

### maturity

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

Returns the expiry/maturity of the position in unix timestamp, at or after which the [Principal Tokens](/glossary#principal-token) can be redeemed for their underlying [asset](#asset).

*Conforms to* [*ERC-5095*](https://eips.ethereum.org/EIPS/eip-5095) *standard.*

### getDuration

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

Returns the total duration of the principal token period in seconds.

### getIBT

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

Returns the address of the IBT ([Interest Bearing Token](/glossary#ibt)).

E.g. if the underlying asset is DAI, the IBT could be aDAI (for Aave deposited DAI) or cDAI (for Compound deposited DAI), etc.

### getYT

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

Returns the address of the YT ([Yield Token](/glossary#yield-token)).

### getIBTRate

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

Returns the ibtRate.

### getPTRate

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

Returns the ptRate.

### getIBTUnit

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

Returns the number used for one unit (10^decimals) of the [IBT](#ibt).

### getAssetUnit

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

Returns the number used for one unit (10^decimals) of the [asset](#asset).

### getIBTRateAtExpiry

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

Returns the IBT rate at expiry. Only valid after [maturity](#maturity).

### getPTRateAtExpiry

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

Returns the PT rate at expiry. Only valid after [maturity](#maturity).

### getCurrentYieldOfUserInIBT

```solidity
function getCurrentYieldOfUserInIBT(address _user) public view returns (uint256 _yieldOfUserInIBT)
```

Returns the yield of `_user` in [IBT](/glossary#ibt) tokens.

## Internal Methods

### \_computeYield

```solidity
function _computeYield(
    address _user,
    uint256 _oldIBTRate,
    uint256 _ibtRate,
    uint256 _oldPTRate,
    uint256 _ptRate
) internal view returns (uint256)
```

Computes the yield generated by a user given old and new rates. For more information, see the [Yield Calculations](/technical-reference/yield-calculations) page.


# Yield Token

The YT ([Yield Token](/glossary#yield-token)) represents the yield that is accrued by an [IBT](/glossary#ibt) (Interest Bearing Token). It is created when an IBT or an underlying asset is deposited into a [PrincipalToken](/technical-reference/contract-functions/principal-token), and the interest bearing token is split into [PT](/glossary#principal-token) and [YT](/glossary#yield-token).

The YT can be traded with other users to hedge or speculate on yields. A user holding a YT can claim his yield by calling [`claimYield`](/technical-reference/contract-functions/principal-token#claimyield) or [`claimYieldInIBT`](/technical-reference/contract-functions/principal-token#claimyieldinibt) on the Principal Token contract.

For more information, see [Tokenising Yield](/guides/tokenizing-yield).

YT.sol code can be found on [GitHub](https://github.com/perspectivefi/spectra-core/blob/main/src/tokens/YieldToken.sol).

## Methods

### transfer

```solidity
function transfer(
    address to,
    uint256 value
) public returns (bool success)
```

Standard ERC20 `transfer` function with a pre-transfer yield update.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code></td><td>address</td><td>The address to send the YT <code>to</code></td></tr><tr><td><code>value</code></td><td>uint256</td><td>The amount of the YT to send</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>success</code></td><td>bool</td><td>A boolean value indicating whether the transfer was successful</td></tr></tbody></table>

### transferFrom

```solidity
function transferFrom(
    address from,
    address to,
    uint256 value
) public returns (bool success)
```

Standard ERC20 `transfer` function with a pre-transfer yield update.&#x20;

{% hint style="warning" %}
`from` must have previously approved `msg.sender` to transfer `value` using the standard ERC20 approval process.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>from</code></td><td>address</td><td>The address to send the YT <code>from</code></td></tr><tr><td><code>to</code></td><td>address</td><td>The address to send the YT <code>to</code></td></tr><tr><td><code>value</code></td><td>uint256</td><td>The amount of the YT to send</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>success</code></td><td>bool</td><td>A boolean value indicating whether the transfer was successful</td></tr></tbody></table>

## View Methods

### getPT

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

Get the [Principal Token](/technical-reference/contract-functions/principal-token) associated with the yield token.

### decimals

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

Returns the number of decimals of the YT (same as the PrincipalToken's decimals). See [`decimals()`](/technical-reference/contract-functions/principal-token#decimals)

### balanceOf

```solidity
function balanceOf(address account) public view returns (uint256)
```

Returns the  balance of YT before[ expiry of the principal token](/technical-reference/contract-functions/principal-token#maturity) and return 0 after the expiry of the principal token.&#x20;

### actualBalanceOf

```solidity
function actualBalanceOf(address account) public view returns (uint256)
```

Returns the real balance of YT as defined in the balanceOf from ERC20 standard&#x20;

i.e the sum of incoming YT to the `account` minus the outgoing balance of YT tokens.&#x20;


# Registry

The Registry contract keeps a record of all valid contract addresses used by the protocol.

Registry.sol code can be [found on Github](https://github.com/APWine/core-v2/blob/main/src/Registry.sol).

## View Methods

### getFactory

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

Returns the address of the associated [Factory](/technical-reference/contract-functions/factory).

### getRouter

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

Returns the address of the associated [Router](/technical-reference/contract-functions/router).

### getPTBeacon

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

Returns the address of the PT Beacon that allows to upgrade the[ Principal Token](/technical-reference/contract-functions/principal-token) implementation.

### getYTBeacon

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

Returns the address of the YT Beacon that allows to upgrade the[ Yield Token](/technical-reference/contract-functions/principal-token) implementation.

### getTokenizationFee

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

Return the tokenisation fee taken on deposited [IBTs](/glossary#ibt) in the [Principal Token](/technical-reference/contract-functions/principal-token) contract.

### getYieldFee

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

Return the value of the fee taken on generated yield in the [Principal Token](/technical-reference/contract-functions/principal-token) contract.

### getPTFlashLoanFee

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

Return the value of the fee taken for realizing a flash loan in the [Router](/technical-reference/contract-functions/router) contract.

### getFeeCollector

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

Return the address allowed to claim the fees from the [PT](/technical-reference/contract-functions/principal-token) contracts.

### getFeeReduction

```solidity
function getFeeReduction(address _pt, address _user) external view returns (uint256)
```

Get the fee reduction of the given `_user` for the given  `_pt` .

### isRegisteredPT

```solidity
function isRegisteredPT(address _future) external view returns (bool)
```

Returns true if an address is registered as a [PrincipalToken](/technical-reference/contract-functions/principal-token).

### getPTAt

```solidity
function getPTAt(uint256 _index) external view returns (address)
```

Returns the address of the [PrincipalToken](/technical-reference/contract-functions/principal-token) at `_index`.

### pTCount

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

Returns the number of [PrincipalToken](/technical-reference/contract-functions/principal-token) that are registered.


# RateOracle

The Rate Oracle is used to track the rate of the [IBT](/glossary#ibt) contract and stores the rate once a day.

RateOracle.sol code can be [found on GitHub](https://github.com/APWine/core-v2/blob/main/src/oracle/RateOracle.sol).

## Methods

### pokeRate

```solidity
function pokeRate(IERC4626Upgradeable vault) external returns (uint256)
```

Stores the current rate of the underlying asset of `vault`.&#x20;

Note: Rates are stored once a day. If this method is called when the rate has already been stored, then the current rate for that day will be returned, saving gas.

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>vault</code></td><td>address</td><td>The address of the <a href="/pages/AOndMDqZNL2aHaFR2EG9">PrincipalToken</a></td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td></td><td>uint256</td><td>The current rate that was set</td></tr></tbody></table>

## View Methods

### getRateOfVaultOnDate

```solidity
function getRateOfVaultOnDate(address vault, uint256 date) public view returns (uint256)
```

Returns the rate of the underlying asset of `vault` at `date`.

### getLastPokedDateOfVault

```solidity
function getLastPokedDateOfVault(address vault) public view returns (uint256)
```

Returns the last date that [`pokeRate()`](#pokerate) was executed for `vault`.


# Factory

The Factory is used to deploy any [PrincipalToken](/technical-reference/contract-functions/principal-token) and Curve pools permissionlessly.

Factory.sol code can be [found on GitHub](https://github.com/perspectivefi/core-v2/blob/main/src/factory/Factory.sol).&#x20;

## Methods

### deployPT

```solidity
function deployPT(address _ibt, uint256 _duration) external returns (address pt)
```

Deploys the PrincipalToken contract implementation.

See also [PrincipalToken](/technical-reference/contract-functions/principal-token).

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td>_<code>ibt</code></td><td>address</td><td>The <a href="/pages/AOndMDqZNL2aHaFR2EG9#ibt">IBT</a> of the PrincipalToken to be deployed</td></tr><tr><td><code>duration</code></td><td>uint256</td><td>The duration of the pt</td></tr></tbody></table>

<table><thead><tr><th width="222.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the <a href="/pages/AOndMDqZNL2aHaFR2EG9">PrincipalToken</a> that was deployed</td></tr></tbody></table>

### deployCurvePool

```solidity
function deployCurvePool(
        address _pt
        CurvePoolParams calldata curvePoolParams
) external returns (address curvePoolAddr)
```

Deploy a curve pool for the specific `_pt` instance with the specified `curvePoolParams`

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="213">Type</th><th>Description</th></tr></thead><tbody><tr><td>_<code>ibt</code></td><td>address</td><td>The <a href="/pages/AOndMDqZNL2aHaFR2EG9#ibt">IBT</a> of the PrincipalToken to be deployed</td></tr><tr><td><code>curvePoolParams</code></td><td><a href="#curvepoolparams"><code>CurvePoolParams</code></a></td><td>The parameters of the curve pool to be deployed</td></tr></tbody></table>

<table><thead><tr><th width="222.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>curvePoolAddr</code></td><td>address</td><td>The address of the curve pool that was deployed</td></tr></tbody></table>

#### CurvePoolParams

The curve pool parameters as defined in the [curve pool documentation](https://resources.curve.fi/factory-pools/creating-a-cryptoswap-pool/#parameters) is a struct that hold the curve pool parameters.

<table><thead><tr><th width="264.3333333333333">Struct Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>A</code></td><td>uint256</td><td>Amplification Parameter [4,000 to 4,000,000,000] Larger values of A make the curve better resemble a straight line in the center (when pool is near balance). Highly volatile assets should use a lower value, while assets that are closer together may be best with a higher value.</td></tr><tr><td><code>gamma</code></td><td>uint256</td><td>The gamma parameter can further adjust the shape of the curve. Default values recommend .000145 for volatile assets and .0001 for less volatile assets.</td></tr><tr><td><code>mid_fee</code></td><td>uint256</td><td>[.005% to 1%] Percentage. Fee when the pool is maximally balanced. This is the minimum fee. The fee is calculated as <code>mid_fee * f + out_fee * (10^18 - f)</code></td></tr><tr><td><code>out_fee</code></td><td>uint256</td><td>[Mid Fee to 1%] Fee when the pool is imbalanced. Must be larger than the Mid Fee and represents the maximum fee.</td></tr><tr><td><code>allowed_extra_profit</code></td><td>uint256</td><td>[0 to .01] As the pool takes profit, the allowed extra profit parameter allows for greater values. Recommended 0.000002 for volatile assets and 0.00000001 for less volatile assets.</td></tr><tr><td><code>fee_gamma</code></td><td>uint256</td><td>[0 to 1] Adjusts how fast the fee increases from Mid Fee to Out Fee. Lower values cause fees to increase faster with imbalance. Recommended value of .0023 for volatile assets and .005 for less volatile assets.</td></tr><tr><td><code>adjustment_step</code></td><td>uint256</td><td>[0 to 1] As the pool rebalances, it will must do so in units larger than the adjustment step size. Volatile assets are suggested to use larger values (0.000146), while less volatile assets do not move as frequently and may use smaller step sizes (default 0.0000055)</td></tr><tr><td><code>admin_fee</code></td><td>uint256</td><td></td></tr><tr><td><code>ma_half_time</code></td><td>uint256</td><td>[0 to 604,800] In seconds -- the price oracle uses an exponential moving average to dampen the effect of changes. This parameter adjusts the half life used.</td></tr><tr><td><code>initial_price</code></td><td>uint256</td><td>The price of coin0 with respect to coin1. This price set the initial liquidity concentration</td></tr></tbody></table>

### deployAll

```solidity
function deployAll(
        address _ibt,
        uint256 _duration,
        CurvePoolParams calldata curvePoolParams
    ) external returns (address pt, address curvePoolAddr, address lpv)
```

Deploy a [Principal Token](/technical-reference/contract-functions/principal-token), a curve pool and a [LP Vault](broken://pages/bxPg6Ybj5iX2oZb3ltmj) associated with the PT and the deployed curve pool in a single transaction.

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td>_<code>ibt</code></td><td>address</td><td>The <a href="/pages/AOndMDqZNL2aHaFR2EG9#ibt">IBT</a> of the PrincipalToken to be deployed</td></tr><tr><td><code>_duration</code></td><td>uint256</td><td>The duration of the <a href="/pages/AOndMDqZNL2aHaFR2EG9">PT</a> contract to deploy</td></tr><tr><td><code>curvePoolParams</code></td><td><a href="#curvepoolparams"><code>CurvePoolParam</code></a></td><td>The parameters of the curve pool to be deployed</td></tr></tbody></table>

<table><thead><tr><th width="222.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the <a href="/pages/AOndMDqZNL2aHaFR2EG9">PT</a> that was deployed</td></tr><tr><td><code>curvePoolAddr</code></td><td>address</td><td>The address of the curve pool that was deployed</td></tr><tr><td><code>lpv</code></td><td>address</td><td>The address of the LP Vault that was deployed</td></tr></tbody></table>

## View Methods

### getRegistry

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

Returns the address of [Registry](/technical-reference/contract-functions/registry) contract.

### getCurveAddressProvider

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

Returns the address of the [Curve address provider](https://curve.readthedocs.io/registry-address-provider.html).

### getCurveFactory

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

Returns the address of the Curve Factory.

### getInitialPrice

```solidity
function getInitialPrice(address curvePool) external view returns (uint256)
```

Return the[ initial\_price parameter](#curvepoolparams) of the `curvePool`.

### getCurveFactoryAddress

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

Returns the address of the associated [Curve](https://curve.fi/) Factory.

###


# Access Manager

We use the [Openzepellin 5.0](https://blog.openzeppelin.com/introducing-openzeppelin-contracts-5.0) Access Manager contract to manage the access rights of the protocol.&#x20;

{% hint style="info" %}
To learn more about openZeppelin access control using an Access Manager follow[ this link](https://docs.openzeppelin.com/contracts/5.x/access-control#using_accessmanager)
{% endhint %}

## Roles

<table><thead><tr><th width="265.3333333333333">Role Name</th><th width="92">ID</th><th>Description</th></tr></thead><tbody><tr><td>ADMIN_ROLE</td><td>0</td><td>The default admin role of the <a href="https://docs.openzeppelin.com/contracts/5.x/api/access#AccessManager">Access Manager</a>. This role allows to create new roles and assign users to roles.</td></tr><tr><td>UPGRADE_ROLE</td><td>1</td><td>Grant the permission to upgrade the protocol contract implementations</td></tr><tr><td>PAUSER_ROLE</td><td>2</td><td>Grant the permission to pause the PT and LP Vault  in case of emergency</td></tr><tr><td>FEE_SETTER_ROLE</td><td>3</td><td>Grant the permission to change the fees of the protocol in the <a href="/pages/rTKgghFh2t0fOKSsFxnZ">registry contract</a>.</td></tr><tr><td>REGISTRY_ROLE</td><td>4</td><td>Grant the permission to change the addresses in the <a href="/pages/rTKgghFh2t0fOKSsFxnZ">registry contracts</a>.</td></tr><tr><td>REWARDS_HARVESTER_ROLE</td><td>5</td><td>Grant the permission to <a href="/pages/c4OVd0StMcFMVakIvw5K">claim rewards</a> on a PT rewards proxy.</td></tr><tr><td>REWARDS_PROXY_SETTER_ROLE</td><td>6</td><td>Grant the permission to add a <a href="/pages/c4OVd0StMcFMVakIvw5K">rewards proxy </a>on a PT.</td></tr><tr><td>VOTER_GOVERNOR_ROLE</td><td>7</td><td>Grant the permission to perform actions reserved to governor of the tokenomics contracts.</td></tr><tr><td>VOTER_EMERGENCY_COUNCIL_ROLE</td><td>8</td><td>Grant the permission to ban and reauthorise voting for specific pools.</td></tr><tr><td>VOTER_ROLE</td><td>9</td><td>Granted to <a href="/pages/1GluF75U4NROgtAwvE0c"><code>Voter.sol</code></a> to perform contracts operations.</td></tr><tr><td>FEES_VOTING_REWARDS_DISTRIBUTOR_ROLE</td><td>10</td><td>Grant the permission to create create fees rewards.</td></tr></tbody></table>


# RouterUtil

## Overview

`RouterUtil.sol`  provides miscellaneous utils and preview functions related to Router executions.

### spotExchangeRate

Gives the spot exchange rate of token i in terms of token j.

*For example `spotExchangeRate(any, 1, 0)` gives you the PT/IBT quote (how much IBT is worth one PT).*

*in IBT while `spotExchangeRate(any, 0, 1)` gives you the IBT/PT  quote (how much PT is worth one IBT).*

{% hint style="info" %}
For the IBT-PT curve pools deployed with the factory, the coin at index 0 is the [Interest Bearing Token](/glossary#ibt) and the coin at index 1 is the [Principal Token](/technical-reference/contract-functions/principal-token).
{% endhint %}

{% hint style="info" %}
&#x20;Exchange rate is in 18 decimals
{% endhint %}

```solidity
function spotExchangeRate(
        address _curvePool,
        uint256 _i,
        uint256 _j
    ) public view returns (uint256)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_i</code></td><td>uint256</td><td>The input token index </td></tr><tr><td><code>_j</code></td><td>uin256</td><td>The output token index </td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td></td><td>uint256</td><td>The quote for the exchange rate of <code>_</code><em><code>i</code> for <code>_</code></em><code>j</code></td></tr></tbody></table>

### convertIBTToYTSpot

Returns the maximal amount of YT one can obtain with a given amount of IBT (i.e without fees or slippage).

{% hint style="warning" %}
This function is used for setting initial guess for the root finding algorithms used when performing [flash swaps](/guides/routing). This method should not be used to evaluate a precise YT price.
{% endhint %}

```solidity
function convertIBTToYTSpot(
        uint256 _inputIBTAmount,
        address _curvePool
    ) public view returns (uint256)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_inputIBTAmount</code></td><td>address</td><td>The amount of IBT to convert</td></tr><tr><td><code>_curvePool</code></td><td>uint256</td><td>The address of the curve pool contract</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ytAmount</code></td><td>uint256</td><td>An upper bound on the YT amount expected</td></tr></tbody></table>

### previewFlashSwapIBTToExactYT

Estimates the amount of IBT required to perform a flash swap to obtain a specific amount of YT.

```solidity
function previewFlashSwapIBTToExactYT(
        address _curvePool,
        uint256 _outputYTAmount
    ) public view returns (uint256, uint256)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_outputYTAmount</code></td><td>uint256</td><td>The desired YT amount</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>inputIBTAmount</code></td><td>uint256</td><td>The estimated IBT amount required for the swap</td></tr><tr><td><code>borrowedIBTAmount</code></td><td>uint256</td><td>The calculated IBT amount to be borrowed in the flash swap.</td></tr></tbody></table>

### previewFlashSwapExactIBTToYT

Estimates the amount of YT that can be obtained by providing a specific amount of IBT in a flash swap.

```solidity
function previewFlashSwapExactIBTToYT(
        address _curvePool,
        uint256 _inputIBTAmount
    ) public view returns (uint256, uint256) 
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_inputIBTAmount</code></td><td>uint256</td><td>IBT amount for the swap.</td></tr></tbody></table>

<table><thead><tr><th width="275.3333333333333">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minGuessOutputYTAmount</code></td><td>uint256</td><td>The minimum estimated YT obtained from the swap.</td></tr><tr><td><code>maxGuessOutputYTAmount</code></td><td>uint256</td><td>The maximum estimated YT obtained from the swap.</td></tr><tr><td><code>borrowedIBTAmount</code></td><td>uint256</td><td>The calculated IBT amount to be borrowed in the flash swap.</td></tr></tbody></table>

### previewFlashSwapExactYTToIBT

Estimates the amount of IBT that can be obtained by swapping a specific amount of YT.

```solidity
function previewFlashSwapExactYTToIBT(
        address _curvePool,
        uint256 _inputYTAmount
    ) public view returns (uint256, uint256) 
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool to trade in</td></tr><tr><td><code>_inputYTAmount</code></td><td>uint256</td><td>The YT amount provided for the swap.</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>outputIBTAmount</code></td><td>uint256</td><td>Estimated IBT amount obtainable from the swap.</td></tr><tr><td><code>borrowedIBTAmount</code></td><td>uint256</td><td>The calculated IBT amount to be borrowed in the flash swap.</td></tr></tbody></table>

### getDx

Estimate the required input amount of a token (`dx`) for a Curve pool swap, given a target output amount (`targetDy`).

```solidity
function getDx(
        ICurvePool curvePool,
        uint256 i,
        uint256 j,
        uint256 targetDy
    ) public view returns (uint256)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>i</code></td><td>uint256</td><td>The index of the input token in the Curve pool.</td></tr><tr><td><code>j</code></td><td>uint256</td><td>The index of the output token in the Curve pool.</td></tr><tr><td><code>targetDy</code></td><td>uint256</td><td>The desired output amount in the swap.</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>guess</code></td><td>uint256</td><td>The estimated input amount (<code>dx</code>) that should be provided to the Curve pool to obtain the <code>targetDy</code>.</td></tr></tbody></table>

### previewAddLiquidityWithAsset

Estimate the amount of LP tokens the user will get by routing assets for LP tokens ([see Routing](/guides/routing)).

```solidity
function previewAddLiquidityWithAsset(
        address _curvePool,
        uint256 _assets
    ) public view returns (uint256 minMintAmount)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_assets</code></td><td>uint256</td><td>The amount of assets to deposit as total liquidity.</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minMintAmount</code></td><td>uint256</td><td>The estimated amount of Curve LP tokens</td></tr></tbody></table>

### previewAddLiquidityWithIBT

Estimate the amount of LP tokens the user will get by routing IBTs for LP tokens ([see Routing](/guides/routing)).

```solidity
function previewAddLiquidityWithIBT(
        address _curvePool,
        uint256 _ibts
    ) public view returns (uint256 minMintAmount)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_ibts</code></td><td>uint256</td><td>The amount of ibts to deposit as total liquidity.</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minMintAmount</code></td><td>uint256</td><td>The estimated amount of Curve LP tokens</td></tr></tbody></table>

### previewAddLiquidity

Estimate the amount of LP tokens the user will get by adding `amounts` liquidity. `amounts` is an array of the amount for token 0, the [IBT](/glossary#ibt) and for token 1, the [Principal Token](/technical-reference/contract-functions/principal-token).

```solidity
function previewAddLiquidity(
        address _curvePool,
        uint256[2] memory _amounts
    ) public view returns (uint256 minMintAmount)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td>_curvePool</td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td>_amounts</td><td>uint256[2]</td><td>The amounts of token 0 (IBT) and token 1 (PT) to provide as liquidity</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minMintAmount</code></td><td>uint256</td><td>The estimated amount of Curve LP tokens</td></tr></tbody></table>

### previewRemoveLiquidityForAsset

Estimate the amount of underlying token (assets) a user will get for burning `_lpAmount` LP tokens and redeeming PT and IBT for underlying. ([see Routing](/guides/routing)).

```solidity
function previewRemoveLiquidityForAsset(
        address _curvePool,
        uint256 _lpAmount
    ) public view returns (uint256 assets)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_lpAmount</code></td><td>uint256</td><td>The amount of lp tokens to burn</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>assets</code></td><td>uint256</td><td>The estimated assets withdrawn from the pool.</td></tr></tbody></table>

### previewRemoveLiquidityForIBT

Estimate the amount of IBTs a user will get for burning `_lpAmount` LP tokens and redeeming PT for IBT. ([see Routing](/guides/routing)).

<pre class="language-solidity"><code class="lang-solidity"><strong>function previewRemoveLiquidityForIBT(
</strong>        address _curvePool,
        uint256 _lpAmount
    ) public view returns (uint256 ibts)
</code></pre>

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_lpAmount</code></td><td>uint256</td><td>The amount of lp tokens to burn</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibts</code></td><td>uint256</td><td>The estimated IBTs withdrawn from the pool.</td></tr></tbody></table>

### previewRemoveLiquidity

Estimate the amounts of IBT and PT received for burning `_lpAmount` LP tokens.

```solidity
function previewRemoveLiquidity(
        address _curvePool,
        uint256 _lpAmount
    ) public view returns (uint256[2] memory minAmounts)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_lpAmount</code></td><td>uint256</td><td>The amount of lp tokens to burn</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minAmounts</code></td><td>uint256[2]</td><td>The estimated amounts of IBTs and PTs withdrawn from the pool.</td></tr></tbody></table>

### previewRemoveLiquidityOneCoin

Estimate the amounts of either IBT or PT received for burning `_lpAmount` LP tokens.  `_i=0` for IBT and `_i=1` for PT.

```solidity
function previewRemoveLiquidityOneCoin(
        address _curvePool,
        uint256 _lpAmount,
        uint256 _i
    ) public view returns (uint256 minAmount)
```

<table><thead><tr><th width="231.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_curvePool</code></td><td>address</td><td>The address of the curve pool contract</td></tr><tr><td><code>_lpAmount</code></td><td>uint256</td><td>The amount of LP tokens to burn</td></tr><tr><td><code>_i</code></td><td>uin256</td><td>The index of the token to withdraw</td></tr></tbody></table>

<table><thead><tr><th width="231.33333333333331">Return Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>minAmounts</code></td><td>uint256</td><td>The estimated amounts of token withdrawn</td></tr></tbody></table>


# Router

## Overview

The `Router.sol`  allows for complex transaction sequences to be executed in a single transaction. It inherits from [`Dispatcher.sol`](#dispatcher) and `IERC3156FlashBorrower`. It primarily handles the execution of a sequence of commands, including flash loans within Spectra [Principal Tokens](/technical-reference/contract-functions/principal-token).&#x20;

## Router Methods

### execute

Handles the execution of command sequence.

```solidity
function execute(
        bytes calldata commands,
        bytes[] calldata inputs
) external payable
```

<table><thead><tr><th width="185.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>commands</code></td><td>bytes calldata</td><td>A set of concatenated commands, each 1 byte in length</td></tr><tr><td><code>inputs</code></td><td>bytes[] calldata</td><td>An array of byte strings containing ABI encoded inputs for each command</td></tr></tbody></table>

### execute

Processes a batch of `commands` before a specified `deadline`. This method checks for deadline and then forwards the call to [`execute()`](#execute-1).

```solidity
function execute(
        bytes calldata commands,
        bytes[] calldata inputs,
        uint256 deadline
) external payable
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>commands</code></td><td>bytes calldata</td><td>A set of concatenated commands, each 1 byte in length</td></tr><tr><td><code>inputs</code></td><td>bytes[] calldata</td><td>An array of byte strings containing ABI encoded inputs for each command</td></tr><tr><td><code>deadline</code></td><td>uint256</td><td>The deadline by which the transaction must be executed</td></tr></tbody></table>

## View Methods

### previewRate

Simulates encoded commands along with provided inputs, and return the resulting rate.

```solidity
function previewRate(
        bytes calldata commands,
        bytes[] calldata inputs
) external view returns (uint256 output)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>commands</code></td><td>bytes calldata</td><td>A set of concatenated commands, each 1 byte in length</td></tr><tr><td><code>inputs</code></td><td>bytes[] calldata</td><td>An array of byte strings containing ABI encoded inputs for each command</td></tr><tr><td><code>outputRate</code></td><td>uint256</td><td>The preview rate value, which represents the amount of output token obtained at the end of execution for each wei of input token spent at the start of execution, multiplied by 1 ray unit.</td></tr></tbody></table>

{% hint style="warning" %}
The following commands are not supported by `previewRate():`

* `FLASH_LOAN`
* `CURVE_SPLIT_IBT_LIQUIDITY`
* `CURVE_ADD_LIQUIDITY`
* `CURVE_REMOVE_LIQUIDITY`
* `CURVE_REMOVE_LIQUIDITY_ONE_COIN`
  {% endhint %}

### previewSpotRate

Simulates encoded commands along with provided inputs, and return the resulting spot rate.

```solidity
function previewSpotRate(
        bytes calldata commands,
        bytes[] calldata inputs
    ) external view returns (uint256 output)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>commands</code></td><td>bytes calldata</td><td>A set of concatenated commands, each 1 byte in length</td></tr><tr><td><code>inputs</code></td><td>bytes[] calldata</td><td>An array of byte strings containing ABI encoded inputs for each command</td></tr><tr><td>outputSpotRate</td><td>uint256</td><td>The preview spot rate value, which represents the amount of output token obtained at the end of execution for each wei of input token spent at the start of execution, multiplied by 1 ray unit.</td></tr></tbody></table>

{% hint style="info" %}
As opposed to `previewRate`, spot exchange rates will be used for swaps. Additionally for all commands, input amounts are disregarded, and one unit of the token of interest is used instead.
{% endhint %}

{% hint style="warning" %}
Same than for `previewRate()`, the following commands are not supported by `previewSpotRate():`

* `FLASH_LOAN`
* `CURVE_SPLIT_IBT_LIQUIDITY`
* `CURVE_ADD_LIQUIDITY`
* `CURVE_REMOVE_LIQUIDITY`
* `CURVE_REMOVE_LIQUIDITY_ONE_COIN`
  {% endhint %}

### onFlashLoan

Implements the flash loan callback, handling the loan repayment and potential shortfall from the original sender.

```solidity
function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>initiator</code></td><td>address</td><td>The initiator of the loan.</td></tr><tr><td><code>token</code></td><td>address</td><td>The loan currency.</td></tr><tr><td><code>amount</code></td><td>uint256</td><td>The amount of tokens to borrow.</td></tr><tr><td><code>fee</code></td><td>uint256</td><td>The additional amount of tokens to repay.</td></tr><tr><td><code>data</code></td><td>bytes calldata</td><td>Arbitrary data structure, intended to contain user-defined parameters.</td></tr></tbody></table>

<table><thead><tr><th width="214.33333333333331">Return Parameter</th><th width="125">Type</th><th>Description</th></tr></thead><tbody><tr><td></td><td>bool</td><td>If successful, onFlashLoan return the keccak256 hash of “ERC3156FlashBorrower.onFlashLoan”.</td></tr></tbody></table>

*Conforms to* [*EIP-3156*](https://eips.ethereum.org/EIPS/eip-3156) *standards.*

## Dispatcher

The `Dispatcher.sol` is an abstract contract that facilitates a variety of financial operations, including token transfers, swaps, and flash loans. Each command has specific input requirements and functionalities.

### `TRANSFER_FROM` Command

Transfers the specified amount of the ERC20 token from the message sender (`msgSender`) to the contract itself. It's crucial for security that the transfer originates only from the message sender.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td>address</td><td>The address of the token to transfer</td></tr><tr><td><code>value</code></td><td>uint256</td><td>The amount of token to transfer</td></tr></tbody></table>

### `TRANSFER_FROM_WITH_PERMIT` Command

Transfers the specified amount of the ERC20 token from the message sender (`msgSender`) to the contract itself using the [EIP-2612 signed approvals](https://eips.ethereum.org/EIPS/eip-2612).

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>address</code></td><td>address</td><td>The address of the token to transfer</td></tr><tr><td><code>value</code></td><td>uint256</td><td>The amount of token to transfer</td></tr><tr><td><code>deadline</code></td><td>uint256</td><td>The deadline for the transaction</td></tr><tr><td><code>v</code></td><td>uint8</td><td>Signature</td></tr><tr><td><code>r</code></td><td>bytes32</td><td>Signature</td></tr><tr><td><code>s</code></td><td>bytes32</td><td>Signature</td></tr></tbody></table>

### `TRANSFER` Command

Transfers a specified amount of the ERC20 token from the contract to the provided recipient address. If a special value indicating the contract's entire balance is provided, it transfers the entire token balance of the contract to the recipient.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>token</code></td><td>address</td><td>The address of the token to transfer</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the transfer receiver</td></tr><tr><td><code>value</code></td><td>uint256</td><td>The amount of token to transfer</td></tr></tbody></table>

### `CURVE_SWAP` Command

Executes a token swap using a Curve finance pool. The command involves specifying the tokens within the pool to swap, the amount to swap, and the minimum acceptable amount for the output token, ensuring slippage protection.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pool</code></td><td>address</td><td>The address of the pool</td></tr><tr><td><code>i</code></td><td>uint256</td><td>The token index to input from the swap </td></tr><tr><td><code>j</code></td><td>uint256</td><td>The token index to output from the swap </td></tr><tr><td><code>amountIn</code></td><td>uint256</td><td>The amount of token in</td></tr><tr><td><code>minAmountOut</code></td><td>uint256</td><td>The minimum amount of token out (slippage protection)</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the swap recipient</td></tr></tbody></table>

### `WRAP_VAULT_IN_4626_ADAPTER` Command

Wraps shares of an interest-bearing vault into an ERC4626 compliant wrapper (Spectra4626Wrapper.wrap()). The operation deposits the interest-bearing vault shares into a Spectra4626Wrapper instance and transfers the resulting wrapper shares to the specified recipient.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>wrapper</code></td><td>address</td><td>The address of the Spectra4626Wrapper</td></tr><tr><td><code>vaultShares</code></td><td>uint256</td><td>The amount of vaults shares to wrap</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The receiver of wrapper shares</td></tr></tbody></table>

### `UNWRAP_VAULT_FROM_4626_ADAPTER` Command

Unwraps shares of an interest-bearing vault from an ERC4626 wrapper (Spectra4626Wrapper.unwrap()). The operation redeems shares of an Spectra4626Wrapper instance and transfers the resulting vault shares to the specified recipient.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>wrapper</code></td><td>address</td><td>The address of the Spectra4626Wrapper</td></tr><tr><td><code>wrapperShares</code></td><td>uint256</td><td>The amount of wrapper shares to redeem</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The receiver of vault shares</td></tr></tbody></table>

### `DEPOSIT_ASSET_IN_IBT` Command

Deposits the specified amount of an underlying ERC20 token into an ERC4626 compliant tokenized vault  ([`ERC4626.deposit()`](https://eips.ethereum.org/EIPS/eip-4626#deposit)). The operation deposits the underlying token into the 4626 vault and transfers the resulting vault shares to the specified recipient.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>ibt</code></td><td>address</td><td>The address of the IBT token</td></tr><tr><td><code>assets</code></td><td>uint256</td><td>The amount of token to deposit</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the transfer receiver</td></tr></tbody></table>

### `DEPOSIT_ASSET_IN_PT` Command

Deposits the specified amount of an underlying ERC20 token in the PT ([`deposit()`](/technical-reference/contract-functions/principal-token#deposit)).&#x20;

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the PT token</td></tr><tr><td><code>assets</code></td><td>uint256</td><td>The amount of token to deposit</td></tr><tr><td><code>ptRecipient</code></td><td>address</td><td>The address of the receiver of PTs</td></tr><tr><td><code>ytRecipient</code></td><td>address</td><td>The address of the receiver of YTs</td></tr><tr><td><code>minShares</code></td><td>uint256</td><td>The minimum amount of minted shares from this deposit</td></tr></tbody></table>

### `DEPOSIT_IBT_IN_PT` Command

Deposits the specified amount of an IBT token in the PT ([`depositIBT()`](/technical-reference/contract-functions/principal-token#depositibt)).&#x20;

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the PT token</td></tr><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of token to deposit</td></tr><tr><td><code>ptRecipient</code></td><td>address</td><td>The address of the receiver of PTs</td></tr><tr><td><code>ytRecipient</code></td><td>address</td><td>The address of the receiver of YTs</td></tr><tr><td><code>minShares</code></td><td>uint256</td><td>The minimum amount of minted shares from this deposit</td></tr></tbody></table>

### `REDEEM_IBT_FOR_ASSET` Command

Redeems the specified amount of the ERC4626 vault shares for the underlying token ([`ERC4626.redeem()`](https://eips.ethereum.org/EIPS/eip-4626#redeem)). The withdrawn tokens are then transferred to the specified recipient.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>token</code></td><td>address</td><td>The address of the token</td></tr><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares to burn</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the transfer receiver</td></tr></tbody></table>

### `REDEEM_PT_FOR_ASSET` Command

&#x20;Redeems the specified amount of PT shares for the underlying token ([`redeem()`](/technical-reference/contract-functions/principal-token#redeem)).

{% hint style="info" %}
Before expiry, the PT shares amount is both the PT and YT amount. After expiry, only the PT shares are burnt.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the PT</td></tr><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares to burn</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the transfer receiver</td></tr><tr><td><code>minAssets</code></td><td>uint256</td><td>The minimum amount of asset to be returned to the user</td></tr></tbody></table>

### `REDEEM_PT_FOR_IBT` Command

Redeems the specified amount of PT share for the associated IBT token ([`redeemForIBT()`](/technical-reference/contract-functions/principal-token#redeemforibt)).

{% hint style="info" %}
Before expiry, the PT shares amount is both the PT and YT amount. After expiry, only the PT shares are burnt.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pt</code></td><td>address</td><td>The address of the PT</td></tr><tr><td><code>shares</code></td><td>uint256</td><td>The amount of shares to burn</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the transfer receiver</td></tr><tr><td><code>minIbts</code></td><td>uint256</td><td>The minimum amount of IBT to be returned to the user</td></tr></tbody></table>

### `FLASH_LOAN` Command

Facilitates a flash loan transaction. It enables borrowing of a specified amount of an ERC20 token from a lender, with the requirement that it is returned within the same transaction, along with any agreed-upon fees.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>lender</code></td><td>address</td><td>The address of the flash loan lender</td></tr><tr><td><code>token</code></td><td>address</td><td>The address of the token to borrow</td></tr><tr><td><code>amount</code></td><td>uint256</td><td>The amount of token to borrow</td></tr><tr><td><code>data</code></td><td>bytes</td><td>Additional data for the flash loan</td></tr></tbody></table>

### `ASSERT_MIN_BALANCE` Command

Checks if the specified token balance of an owner's address is at least the provided minimum value. If the balance is below the minimum, the command reverts the entire transaction.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>token</code></td><td>address</td><td>The address of the token</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the token owner to check the balance from</td></tr><tr><td><code>minValue</code></td><td>uin256</td><td>The minimum amount of token required</td></tr></tbody></table>

### `CURVE_SPLIT_IBT_LIQUIDITY` Command

&#x20;Split the input IBT amount into IBT for the pool and to deposit in the Principal Token. Add liquidity to the pool with the resulting PT and the split IBT amounts.

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pool</code></td><td>address</td><td>The address of the Curve pool</td></tr><tr><td><code>ibts</code></td><td>uint256</td><td>The amount of IBT to deposit</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the LP token recipient</td></tr><tr><td><code>ytRecipient</code></td><td>address</td><td>The address of the YT recipient</td></tr><tr><td><code>minPTShares</code></td><td>uint256</td><td>The minimum amount of minted PT/YT shares from the portion of IBT deposited in the PT contract.</td></tr></tbody></table>

### `CURVE_ADD_LIQUIDITY` Command

Add liquidity to the Curve `pool` specifying each token amount in input and the minimum amount of LP token to mint (slippage protection).

<table><thead><tr><th width="207.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pool</code></td><td>address</td><td>The address of the Curve pool</td></tr><tr><td><code>amounts</code></td><td>uint256[2]</td><td>The amount of IBT/PT to deposit</td></tr><tr><td><code>min_mint_amount</code></td><td>uint256</td><td>The minimum amount of LP token to mint</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the LP token recipient</td></tr></tbody></table>

### `CURVE_REMOVE_LIQUIDITY` Command

Remove liquidity from Curve `pool` specifying the amount `lps` of LP tokens to burn and the minum amount of liquidity to withdraw for each token (slippage protection).

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pool</code></td><td>address</td><td>The address of the Curve pool</td></tr><tr><td><code>lps</code></td><td>uint256</td><td>The amount of LP tokens to burn</td></tr><tr><td><code>min_amounts</code></td><td>uint256[2]</td><td>The minimum amount of assets to receive</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the tokens recipient</td></tr></tbody></table>

### `CURVE_REMOVE_LIQUIDITY_ONE_COIN` Command

Remove liquidity from Curve `pool` by withdrawing a single coin. The user specify the amount `lps` of LP tokens to burn and the minimum amount of liquidity to withdraw for the chosen token (slippage protection).

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pool</code></td><td>address</td><td>The address of the Curve pool</td></tr><tr><td><code>lps</code></td><td>uint256</td><td>The amount of LP tokens to burn</td></tr><tr><td><code>i</code></td><td>uint256</td><td>The index of the token to withdraw from the pool</td></tr><tr><td><code>min_amount</code></td><td>uint256</td><td>The minimum amount of assets to receive</td></tr><tr><td><code>recipient</code></td><td>address</td><td>The address of the token recipient</td></tr></tbody></table>


# GovernanceRegistry

## Overview

The `GovernanceRegistry` keeps a record of miscellaneous data used by Spectra's governance contracts.

## GovernanceRegistry Methods

### votingRewardsFactory

Returns the address of `VotingRewardsFactory`.

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

### poolsData

Returns the pool data.

```solidity
function poolsData(uint160 _poolId) external view returns (address, uint256, bool)
```

<table><thead><tr><th width="246.33333333333331">Return Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_pool</code></td><td>address</td><td>The pool address.</td></tr><tr><td><code>_chainId</code></td><td>uint256</td><td>The chainId of the network on which the pool is deployed.</td></tr><tr><td><code>_isRegistered</code></td><td>bool</td><td>True if the pool is registered, false otherwise.</td></tr></tbody></table>

### getPoolId

Returns the pool ID for the given pool.

{% hint style="info" %}
`poolId = uint160(poolAddress)` $$\oplus$$ `uint160(chainId)`
{% endhint %}

```solidity
function getPoolId(address _pool, uint256 _chainId) external view returns (uint160)
```

### isPoolRegistered

Returns wether the given pool is registered for deployment of voting rewards.

```solidity
function isPoolRegistered(uint160 _poolId) external view returns (bool)
```

```solidity
function isPoolRegistered(address _pool, uint256 _chainId) external view returns (bool)
```


# Voter

## Overview

The `Voter` contract allows to vote for the proportion of weekly emissions that go to each pool LPs, as well as and voting rewards creation. Votes can be cast once per epoch, and earn voters both bribes and fees from the pool they voted for.

{% hint style="info" %}
Given that votes for pools across all supported networks are managed on Ethereum, each pool is identified by a unique ID, in order to prevent address collusion, which may occur if two pools on different networks happen to share the same address.

Pool IDs can be obtained from `GovernanceRegistry`'s [`getPoolId()`](/technical-reference/contract-functions/governanceregistry#getpoolid) function.
{% endhint %}

## Voter Methods

### vote

Handles the execution of command sequence.

```solidity
function vote(
    address _user,
    uint160[] calldata _poolVote,
    uint256[] calldata _weights
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address of the user voting.</td></tr><tr><td><code>_poolVote</code></td><td>uint160[] calldata</td><td>The array of identifiers of pools that are voted for.</td></tr><tr><td><code>_weights</code></td><td>uint256[] calldata</td><td>The weights of pools.</td></tr></tbody></table>

### poke

Called by users to update voting balances in voting rewards contracts.

```solidity
function poke(
    address _user
) external
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>Address of the user whose balance are to be updated.</td></tr></tbody></table>

### batchVote

Called by approved address to vote for multiple users for pools. For each user, votes are distributed proportionally based on weights.

```solidity
function batchVote(
    address[] calldata _users,
    uint160[] calldata _poolVote,
    uint256[] calldata _weights
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_users</code></td><td>address[] calldata</td><td>The addresses of the users voting.</td></tr><tr><td><code>_poolVote</code></td><td>uint160[] calldata</td><td>The array of identifiers of pools that are voted for.</td></tr><tr><td><code>_weights</code></td><td>uint256[] calldata</td><td>The weights of pools.</td></tr></tbody></table>

{% hint style="info" %}
Each user can only vote once per epoch.
{% endhint %}

{% hint style="info" %}
Users can only vote for registered pools with deployed voting rewards.
{% endhint %}

{% hint style="info" %}
Weights are distributed proportionally to the sum of the weights in the array.
{% endhint %}

{% hint style="warning" %}
Throws if length of \_poolVote and \_weights do not match.
{% endhint %}

### batchPoke

Called by approved address to update voting balances for multiple users in voting rewards contracts.

```solidity
function batchPoke(
    address[] calldata _users
) external
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_users</code></td><td>address[] calldata</td><td>The addresses of the users whose balance are to be updated.</td></tr></tbody></table>

### reset

Called by users to reset voting state.

```solidity
function reset(
    address _user
) external
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>Address of the user reseting.</td></tr></tbody></table>

{% hint style="info" %}
Can vote again after reset.
{% endhint %}

{% hint style="warning" %}
Cannot reset in the same epoch that you voted in.
{% endhint %}

### claimBribes

Bulk claim bribes for a given user address.

```solidity
claimBribes(
    address[] calldata _bribes,
    address[][] calldata _tokens,
    address _user
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="213">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_bribes</code></td><td>address[] calldata</td><td>The array of BribeVotingReward contracts to collect from.</td></tr><tr><td><code>_tokens</code></td><td>address[][] calldata</td><td>The array of tokens that are used as bribes.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user to claim bribe rewards for.</td></tr></tbody></table>

### claimFees

Bulk claim fees for a given user address.

```solidity
claimFees(
    address[] calldata _fees,
    address[][] calldata _tokens,
    address _user
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="214">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_fees</code></td><td>address[] calldata</td><td>The array of FeesVotingReward contracts to collect from.</td></tr><tr><td><code>_tokens</code></td><td>address[][] calldata</td><td>The array of tokens that are used as bribes.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user to claim fees rewards for.</td></tr></tbody></table>

### claimPoolsVotingRewards

Bulk claim bribes and fees for a given user address.

<pre class="language-solidity"><code class="lang-solidity">function claimPoolsVotingRewards(
    uint160[] calldata _poolIds,
<strong>    address[][] calldata _bribeTokens,
</strong>    address[][] calldata _feeTokens,
    address _user
) external
</code></pre>

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="214">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_poolIds</code></td><td>uint160[] calldata</td><td>The array of pool identifiers to collect associated bribes and fees from.</td></tr><tr><td><code>_bribeTokens</code></td><td>address[][] calldata</td><td>The array of list of tokens that are used as bribes for each pool.</td></tr><tr><td><code>_feeTokens</code></td><td>address[][] calldata</td><td>The array of list of tokens that are used as fees rewards for each pool.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user to claim bribe and fees rewards for.</td></tr></tbody></table>

### createVotingRewards

Create voting rewards for a pool (unpermissioned).

```solidity
function createVotingRewards(
    address _poolId
) external returns (address fees, address bribe)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_poolId</code></td><td>uint160</td><td>The identifier of the pool.</td></tr></tbody></table>

<table><thead><tr><th width="191.33333333333331">Return Parameter</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>fees</code></td><td>address</td><td>The address of created FeesVotingReward.</td></tr><tr><td><code>bribe</code></td><td>address</td><td>The address of created BribeVotingReward.</td></tr></tbody></table>

{% hint style="warning" %}
Only one pair of voting rewards can be created for any pool. See `Voter`'s [`hasVotingRewards()`](/technical-reference/contract-functions/voter#hasvotingrewards).
{% endhint %}

{% hint style="warning" %}
Pool needs to be registered in governance registry. See `GovernanceRegistry`'s [`isPoolRegistered()`](/technical-reference/contract-functions/governanceregistry#ispoolregistered).
{% endhint %}

### setApprovalForAll

Give or take back approval for an operator to manage voting and rewards claiming on behalf of sender.

```solidity
function setApprovalForAll(
    address _operator,
    bool _approved
) external
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="190">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_operator</code></td><td>address</td><td>The address to set approval for.</td></tr><tr><td><code>_approved</code></td><td>bool</td><td>True to approve operator, false otherwise.</td></tr></tbody></table>

## View Methods

### dao

Return the address of DAO.

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

### forwarder

Return the address of trusted forwarder.

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

### ve

Return the address of the ve token that governs these contracts.

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

### governanceRegistry

Return the address of GovernanceRegistry.

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

### totalWeight

Return the total voting weight.

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

### maxVotingNum

Return the max number of pools one voter can vote for at once.

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

### defaultFeesRewardsDaoFee

Return the default share of fees voting rewards to be sent to the DAO, in basis points.

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

### defaultBribeRewardsDaoFee

Return the default share of bribe voting rewards to be sent to the DAO, in basis points.

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

### poolIds

Return the pool identifier stored at the given index in the list of identifiers of pools supporting voting rewards.

```solidity
function poolIds(address _index) external view returns (uint160)
```

### length

Return the number of pools with associated voting rewards.

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

### getAllPoolIds

Return the list of all pool identifiers for pools with associated voting rewards.

```solidity
function getAllPoolIds() external view returns (uint160[])
```

### hasVotingRewards

Return wether voting rewards are deployed for a given pool.

```solidity
function hasVotingRewards(uint160 _poolId) external view returns (bool)
```

### poolToFees

Return the FeesVotingRewards contract associated with given pool.

<pre class="language-solidity"><code class="lang-solidity"><strong>function poolToFees(uint160 _poolId) external view returns (address)
</strong></code></pre>

### poolToBribe

Return the BribeVotingRewards contract associated with given pool.

```solidity
function poolToBribe(uint160 _poolId) external view returns (address)
```

### weights

Return the total voting weight for a given pool.

```solidity
function weights(uint160 _poolId) external view returns (uint256)
```

### votes

Return the voting weight attributed by a given user to a given pool.&#x20;

```solidity
function votes(address _user, uint160 _poolId) external view returns (uint256)
```

### usedWeights

Return the total used voting weight of a given user.

```solidity
function usedWeights(address _user) external view returns (uint256)
```

### lastVoted

Return the timestamp of last votefor a given user.

```solidity
function lastVoted(address _user) external view returns (uint256)
```

### isWhitelistedBribeToken

Return wether a token is whitelisted as a possible bribe reward.

```solidity
function isWhitelistedBribeToken(address token) external view returns (bool)
```

### isWhitelistedUser

Return wether a user is whitelisted to vote outside of voting periods.

```solidity
function isWhitelistedUser(address _user) external view returns (bool)
```

### isVoteAuthorized

Return wether voting authorized for a given pool.

```solidity
function isVoteAuthorized(uint160 _poolId) external view returns (bool)
```

### voted

Return wether a given user currently has a voting position.

```solidity
function voted(address _user) external view returns (bool)
```

### restricted

Return wether a given user is restricted from voting.

<pre class="language-solidity"><code class="lang-solidity"><strong>function restricted(address _user) external view returns (bool)
</strong></code></pre>


# VotingReward

## Overview

The `VotingReward` contract used by `FeesVotingReward` and `BribeVotingReward` allows to distribute rewards to users, earned through voting for a pool.

## VotingReward Methods

### getReward

Claim the rewards earned by a voter for a given token.

```solidity
function getReward(
    address _user,
    address _token
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address of the user voting.</td></tr><tr><td><code>_token</code></td><td>address</td><td>The token to claim rewards of.</td></tr></tbody></table>

### getReward

Claim the rewards earned by a voter for a given token.

```solidity
function getReward(
    address _user,
    address[] calldata _tokens
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address of the user voting.</td></tr><tr><td><code>_tokens</code></td><td>address[] calldata</td><td>The array of tokens to claim rewards of.</td></tr></tbody></table>

### \_deposit

Deposit an amount into the rewards contract to earn future rewards.

{% hint style="warning" %}
Internal notation used as only callable internally by `Voter`.
{% endhint %}

```solidity
function _deposit(
    uint256 _amount,
    address _user
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_amount</code></td><td>uint256</td><td>The amount deposited for the user.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user.</td></tr></tbody></table>

### \_withdraw

Withdraw an amount from the rewards contract associated to a voter.

{% hint style="warning" %}
Internal notation used as only callable internally by `Voter`.
{% endhint %}

```solidity
function _withdraw(
    uint256 _amount,
    address _user
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_amount</code></td><td>uint256</td><td>The amount withdrawn for the user.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user.</td></tr></tbody></table>

## View Methods

### dao

Return the address of DAO.

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

### DURATION

Return the epoch duration constant (7 days).

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

### voter

Return the address of `Voter.sol`.

<pre class="language-solidity"><code class="lang-solidity"><strong>function voter() external view returns (address)
</strong></code></pre>

### poolId

Return the identifier of the pool this contrat is associated with.

```solidity
function poolId() external view returns (uint160)
```

### totalSupply

Return the total amount currently deposited via \_deposit().

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

### balanceOf

Return the current amount deposited by given user.

```solidity
function balanceOf(address _user) external view returns (uint256)
```

### tokenRewardsPerEpoch

Return the amount of tokens to reward depositors for a given epoch.

```solidity
function tokenRewardsPerEpoch(
    address _token,
    uint256 _epochStart
) external view returns (uint256)
```

### lastEarn

Return the most recent timestamp a user has claimed their rewards for given tokens.

```solidity
function lastEarn(
    address _token,
    address _user
) external view returns (uint256)
```

### rewards

Return the token address stored at the given index in the list of rewards token.

```solidity
function rewards(uint256 _index) external view returns (address)
```

### rewardsListLength

Return the number of rewards tokens.

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

### getAllRewards

Return the list of rewards tokens.

```solidity
function getAllRewards() external view returns (address[])
```

### isReward

Return wether a token is or has been an active reward token.

```solidity
function isReward(address _token) external view returns (bool)
```

### daoFee

Return the share of rewards to be sent to the DAO, in basis points.

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

### numCheckpoints

Return the number of checkpoints for each user address.

```solidity
function numCheckpoints(address _user) external view returns (uint256)
```

### supplyNumCheckpoints

Return the total number of checkpoints.

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

### getPriorBalanceIndex

Return the prior balance for an account as of a block number.

```solidity
function getPriorBalanceIndex(
    address _user,
    uint256 _timestamp
) external view returns (uint256);
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address of the user.</td></tr><tr><td><code>_timestamp</code></td><td>uint256</td><td>The timestamp to get the balance at.</td></tr></tbody></table>

{% hint style="warning" %}
Block number must be a finalized block or else this function will revert to prevent misinformation.
{% endhint %}

### getPriorSupplyIndex

Return the prior index of supply staked by a given timestamp.

```solidity
function getPriorSupplyIndex(uint256 _timestamp) external view returns (uint256)
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_timestamp</code></td><td>uint256</td><td>The timestamp to get the index at.</td></tr></tbody></table>

{% hint style="warning" %}
Timestamp must be <= current timestamp
{% endhint %}

### earned

Return how much in rewards are earned for a specific token and voter.

<pre class="language-solidity"><code class="lang-solidity">function earned(
<strong>    address _token,
</strong><strong>    address _user
</strong><strong>) external view returns (uint256)
</strong></code></pre>

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_token</code></td><td>address</td><td>The token to fetch rewards of.</td></tr><tr><td><code>_user</code></td><td>address</td><td>The address of the user to check.</td></tr></tbody></table>


# BribeVotingReward

## Overview

The `BribeVotingReward` contract allows to distribute bribe rewards to users, earned through voting for a associated pool.

## BribeVotingReward Methods

### notifyRewardAmount

Add rewards for voters to earn.

```solidity
function notifyRewardAmount(
    address _token,
    uint256 _amount
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_token</code></td><td>address</td><td>The address of token to reward.</td></tr><tr><td><code>_amount</code></td><td>uint256</td><td>The amount of token to transfer to rewards</td></tr></tbody></table>

### notifyRewardAmountETH

Add ETH rewards for voters to earn.

```solidity
function notifyRewardAmountETH() external
```

{% hint style="info" %}
msg.value should be set the desired amount to add.
{% endhint %}


# FeesVotingReward

## Overview

The `FeesVotingReward` contract allows to distribute pool fees rewards to users, earned through voting for the pool in question.

## FeesVotingReward Methods

### notifyRewardAmount

Add rewards for voters to earn.

```solidity
function notifyRewardAmount(
    address _token,
    uint256 _amount
) external
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_token</code></td><td>address</td><td>The address of token to reward.</td></tr><tr><td><code>_amount</code></td><td>uint256</td><td>The amount of token to transfer to rewards</td></tr></tbody></table>

{% hint style="info" %}
Only callable with `FEES_VOTING_REWARDS_DISTRIBUTOR_ROLE` role.
{% endhint %}

### notifyRewardAmountETH

Add ETH rewards for voters to earn.

```solidity
function notifyRewardAmountETH() external
```

{% hint style="info" %}
msg.value should be set the desired amount to add.
{% endhint %}

{% hint style="info" %}
Only callable with `FEES_VOTING_REWARDS_DISTRIBUTOR_ROLE` role.
{% endhint %}


# FeeDistributor

## Overview

The `FeeDistributor.vy` contract allows users to claim APW locking rewards.

## FeeDistributor Methods

### claim

Claim fees for `_addr.`

```solidity
def claim(_addr: address = msg.sender) -> uint256
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_addr</code></td><td>address</td><td>The user address to claim for.</td></tr></tbody></table>

<table><thead><tr><th width="246.33333333333331">Return Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>amount</code></td><td>uint256</td><td>The amount claimed.</td></tr></tbody></table>

{% hint style="info" %}
Each call to claim look at a maximum of 50 user veAPW points. For accounts with many veAPW related actions, this function may need to be called more than once to claim all available fees. In the `Claimed` event that fires, if `claim_epoch` is less than `max_epoch`, the account may claim again.
{% endhint %}

### claim\_many

Make multiple fee claims in a single call.

```solidity
def claim_many(_receivers: address[20]) -> bool
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_receivers</code></td><td>address[20]</td><td>List of addresses to claim for. Claiming terminates at the first <code>ZERO_ADDRESS.</code></td></tr></tbody></table>

<table><thead><tr><th width="246.33333333333331">Return Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>success</code></td><td>bool</td><td>True if execution succeeded.</td></tr></tbody></table>

{% hint style="info" %}
Used to claim for many accounts at once, or to make multiple claims for the same address when that address has significant veAPW history.
{% endhint %}

### checkpoint\_token

Updates the token checkpoint.

```solidity
def checkpoint_token()
```

{% hint style="info" %}
Calculates the total number of tokens to be distributed in a given week. During setup for the initial distribution this function is only callable by the contract owner. Beyond initial distro, it can be enabled for anyone to call.
{% endhint %}

### checkpoint\_total\_supply

Update the veAPW total supply checkpoint.

```solidity
def checkpoint_total_supply()
```

{% hint style="info" %}
The checkpoint is also updated by the first claimant each new epoch week. This function may be called independently of a claim, to reduce claiming gas costs.
{% endhint %}

## View Methods

### ve\_for\_at

Get the veAPW balance for `_user` at `_timestamp`

```solidity
def ve_for_at(_user: address, _timestamp: uint256) -> uint256
```

<table><thead><tr><th width="246.33333333333331">Input Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_user</code></td><td>address</td><td>The address to query balance for.</td></tr><tr><td><code>_timestamp</code></td><td>uint256</td><td>The epoch time to query balance at.</td></tr></tbody></table>

<table><thead><tr><th width="246.33333333333331">Return Parameter</th><th width="188">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>balance</code></td><td>uint256</td><td>The veAPW balance.</td></tr></tbody></table>


# Spectra4626Wrapper

The `Spectra4626Wrapper` contract implements a wrapper to facilitate compliance of an interest-bearing vault with the ERC-4626 standard, making it compatible for deploying a Spectra Principal Token.

## ERC-4626

This contract is compatible with the [ERC-4626 standard](https://eips.ethereum.org/EIPS/eip-4626) and implements its interface. However, depending on the functionalities supported by the wrapped vault, some functions may revert by default (e.g. redeem() / withdraw()).

## Methods

### wrap

Deposits vault shares into the wrapper.

```solidity
function wrap(uint256 vaultShares, address receiver) external returns (uint256)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>vaultShares</code></td><td>uint256</td><td>The amount of vault shares to deposit</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address to receive the wrapper shares</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="117">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>wrapperShares</code></td><td>uint256</td><td>The amount of minted wrapper shares</td></tr></tbody></table>

### wrap

Deposits vault shares into the wrapper, with support for slippage protection.

```solidity
function wrap(
    uint256 vaultShares,
    address receiver,
    uint256 minShares
) external returns (uint256)
```

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>vaultShares</code></td><td>uint256</td><td>The amount of vault shares to deposit</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address to receive the wrapper shares</td></tr><tr><td><code>minShares</code></td><td>uint256</td><td>The minimum allowed wrapper shares from this deposit</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of minted wrapper shares</td></tr></tbody></table>

### unwrap

Withdraws vault shares from the wrapper.

```solidity
function unwrap(uint256 shares, address receiver, address owner) external returns (uint256)
```

{% hint style="warning" %}
`msg.sender` must approve the relevant allowance of the vault token before calling this method.
{% endhint %}

<table><thead><tr><th width="190.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of wrapper shares to redeem</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address to receive the vault shares</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the wrapper shares</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="117">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>vaultShares</code></td><td>uint256</td><td>The amount of withdrawn vault shares</td></tr></tbody></table>

### unwrap

Withdraws vault shares from the wrapper, with support for slippage protection.

```solidity
function unwrap(
    uint256 shares,
    address receiver,
    address owner,
    uint256 minVaultShares
) external returns (uint256)
```

<table><thead><tr><th width="192.33333333333331">Input Parameter</th><th width="115">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>shares</code></td><td>uint256</td><td>The amount of wrapper shares to redeem</td></tr><tr><td><code>receiver</code></td><td>address</td><td>The address to receive the vault shares</td></tr><tr><td><code>owner</code></td><td>address</td><td>The address of the owner of the wrapper shares</td></tr><tr><td><code>minVaultShares</code></td><td>uint256</td><td>The minimum vault shares that should be returned</td></tr></tbody></table>

<table><thead><tr><th width="189.33333333333331">Return Parameter</th><th width="117">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>vaultShares</code></td><td>uint256</td><td>The amount of withdrawn vault shares</td></tr></tbody></table>

## View Methods

### previewWrap

Calculates the amount of minted wrapper shares for a given amount of deposited vault shares.

```solidity
function previewWrap(uint256 vaultShares) external view returns (uint256)
```

### previewUnwrap

Calculates the amount of withdrawn vault shares for a given amount of redeemed wrapper shares.

```solidity
function previewUnwrap(uint256 shares) external view returns (uint256)
```

### vault

Returns the address of the wrapped vault.

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

### totalVaultShares

Returns the vault balance of the wrapper.

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


# Yield Calculations

To understand how yield is calculated within the protocol, it is important to understand the following:

* The Principal Token effectively acts as collateral.
* The value of the Principal Token is the same for every user and determined by the `ptRate`, i.e. the price of the Principal Token in the underlying asset.
* The `ptRate` can only decrease.
* The `ptRate` starts as 1 and decreases if the `ibtRate` (i.e. the price of the IBT in the underlying asset) decreases.

## If `ibtRate` is strictly increasing

In this scenario, the yield attribution per user is simple. The Principal Token value in the underlying asset never de-pegs (i.e. 1 PT = 1 underlying asset).

In simple terms, the yield calculation is:

$$
(ibtRate\_{new} - ibtRate\_{old}) \* ytBalance\_{user}
$$

## If `ibtRate` is not strictly increasing

If the `ibtRate` alternates between increasing and decreasing, then the calculation is more complex.

The reasons for the added complexity is that:

* the `ptRate` is the same for every user,
* when any user interacts with the protocol, it triggers a `ptRate` update.

This results in the Principal Token's price in the underlying asset to change for every user.

The following example will illustrate the challenge:

* If Alice and Bob both deposit 10 underlying tokens at time *t0* when the `ibtRate = ptRate = 1`, then both will receive 10 PT and 10 YT.
  * At time *t1*, the `ibtRate` decreases to 0.5, and Alice decides to withdraw all of her funds. This will trigger a `ptRate` decrease of 50%, i.e. `ptRate = 0.5`. Alice will then receive 5 underlying tokens, which is expected.
  * At time *t2*, the `ibtRate` increases to 0.75, and Bob decides to withdraw all his funds. Intuitively, Bob should get 7.5 underlying tokens back.&#x20;
    * However, since the `ptRate` can only decrease, the `ptRate` still remains at 0.5.&#x20;
    * From Bob's perspective, the `ibtRate` decreased from 1 to 0.75, so there has been no positive rate for Bob.
    * Bob was impacted by the decrease in yield from 1 to 0.5, but wasn't rewarded for the increase in yield from 0.5 to 0.75.
    * Therefore the protocol needs to balance the loss in Principal Tokens value with appropriate positive yield.

This balancing between negative and positive yield is performed in the internal [`_computeYield`](/technical-reference/contract-functions/principal-token#_computeyield) function. It works by calculating how much a user should have gained or lost compared to what has actually been lost in Principal Tokens value (via`ptRate` decrease). The yield attributed to the user is thus:

* `actualLoss - expectedLoss` if the `ibtRate` decreased from the user's perspective (e.g. the example above), or&#x20;
* `actualLoss + expectedProfit` if the `ibtRate` increased from the user's perspective (e.g. if in the example above at time *t2* the `ibtRate` had increased to more than 1).

Note that the `actualLoss` above is due to the `ptRate` decrease and `expectedLoss < actualLoss`.


# Spectra's Automated Market Makers

The Spectra AMMs are used to create a market for pricing the future implied rate. A market consists in a liquidity pool, where a Principal Token is paired with its corresponding Interest Bearing Token. It is therefore time-bound, trading being enabled between the Principal Token's deployment and expiry dates.&#x20;

A given liquidity pool allows for exchanging an Interest Bearing Token for a Principal Token, as well as exchanging the former for its corresponding Yield Token, via flash-swaps.

In the following, we dive into how Spectra's AMMs are designed.&#x20;


# Rate Adjusted StableSwap pools

In the following, we consider an Interest Bearing Token integrated in Spectra. We assume all instruments start at $$t=0$$ and expire at $$t=T$$.&#x20;

## StableSwap pools

### Overview

StableSwap pools, developed by Curve Finance, are an AMM (Automated Market Maker) designed for trading pegged assets. They elaborate on the constant sum invariant to support an infinite price range by interpolating it with the constant sum invariant, while maintaining price impact minimal on almost all of the pool's range. For further information, we refer to the [StableSwap Whitepaper](https://curve.fi/files/stableswap-paper.pdf).&#x20;

### Rate Adjusted StableSwap for PT/IBT market making

Using a raw StableSwap pool for exchanging Interest Bearing Tokens for Principal Tokens will not be efficient, since they are not necessarily pegged.&#x20;

Indeed, we make take as an example Interest Bearing assets with external cash flows, such as rewards or points. The implied rate will take the external cash flows into account, while the value of the Interest Bearing token in underlying asset will not grow as quickly.&#x20;

The oracle adjusts the internal prices of the AMM with rate oracles by converting the balances into underlying asset, both for the Principal Token and the Interest Bearing token, so that the pair behaves as a pegged pair inside of the AMM. This is achieved through the [rate adjustment mechanism inside of Curve StableSwap](https://docs.curve.fi/factory/stableswap-ng/overview/#asset_types), as described below.

The AMM's bonding Curve is exclusively used to incorporate implied interest rate volatility, delegating the predictable market drift's accounting to the rate oracles. This drastically improves liquidity usage for trading such a pair, compared to a classical AMM.&#x20;

## Rate Adjustment Oracles

### Interest Bearing Token Oracle

Interest Bearing Tokens are `ERC4626` compliant vaults. These vaults consist of an underlying asset reserve and a rate at which this reserve evolves. One can convert between shares of the vault and the underlying asset using the `convertToAssets()`  method.&#x20;

In mathematical terms, if $$(r\_t)\_{t \in \[0,T]}$$ denotes the instantaneous rate process, then the price of an Interest Bearing Token since deployment is given by

$$
B(t) = \exp(\int\_0^t r\_u du )
$$

Hence, to virtualise the Interest Bearing Token balance of the pool to its underlying value, we have to multiply that balance by the above rate.&#x20;

This is achieved in practice by flagging the token at index 0 as an asset of type 3, i.e as an `ERC4626`  token, so that the StableSwap AMM knows it has to adjust the balances with the vault's rate.&#x20;

### Principal Tokens pricing

Principal Tokens are time bound instruments corresponding to a unit of underlying discounted by the implied interest rate over the term it is deployed on. They are the equivalent of zero-coupon bonds. They are redeemable for a unit of underlying at maturity when no negative interest rate has been registered in the underlying Interest Bearing Token, or else for a unit of underlying adjusted by the cumulative loss incurred by the negative interest. In Spectra, the variable that tracks the negative rate in the PT is referred to as the `ptRate` .  In mathematical terms, the `ptRate` can be expressed as

$$
\alpha(t) = \exp(\int\_0^t r\_u \chi\_{{r\_u < 0}}du )
$$

Given an implied APY $$R$$, the fair value of the Principal Token in underlying asset is given by

$$
P(t,T,R) = \frac{\text{PT.previewRedeem(1)}}{(1+R)^{\frac{T-t}{\text{year}}}}
$$

the $$\text{previewRedeem}$$ function will diminish the par value proportionally to the amount of negative interest.&#x20;

Let $$\bar{R}$$ be the expected implied APY. The Principal Token oracle will report $$P(t,T,\bar{R})$$. If $$X(t)$$ denotes the internal price of the pool at time $$t$$, we have that

$$
X(t) \frac{P(t,T,\bar{R})}{B(t)} = \frac{P(t,T,R)}{B(t)}
$$

Rearranging, we get

$$
X(t) = \frac{P(t,T,R)}{P(t,T,\bar{R})}
$$

or

$$
X(t) = \left (1 + \frac{R - \bar{R}}{1+\bar{R}}\right )^{\frac{T-t}{\text{year}}}
$$

Therefore, with such choice of oracle for the Principal Token, the AMM prices the deviation of the implied rate from the expected rate over the given term.

In practice, this is achieved by flagging the token at index 1 as asset of type 1, i.e an asset whose price with respect to an underlying asset depends on an oracle. The oracle contract is `RateAdjustmentOracle.sol` and its value is fetched by the StableSwap algorithm before every state change.&#x20;

## Adjusting liquidity concentration

Holding $$R-\bar{R}$$ constant, if $$R-\bar{R} > 0$$, then $$X$$ is strictly decreasing and if $$R-\bar{R} < 0$$, then $$X$$ is strictly increasing. That means that assuming a constant maximal deviation of the implied rate from the expected implied rate, the price range in which liquidity is concentrated should get smaller the closer we get to maturity, to achieve a constant liquidity concentration profile in the implied interest rate space.&#x20;

Let $$(A(t))\_{t \in \[0,T]}$$ be the time-dependent amplification factor and $$F\_t(x,y,A)$$ be the StableSwap invariant, where $$x,y$$ represent the rate adjusted balances of Interest Bearing Token and Principal Token, respectively. Assume we want to support a maximal deviation of a factor $$\beta$$ of the implied APY, that is

$$
R\_{\text{max}} = (1+\beta)\bar{R}
$$

achievable at IBT/PT ratio of  $$l < 0.5$$.  Assume that $$\frac{x}{y} > l$$ (the other case is analogue). Then $$A(t)$$ is defined implicitly by

$$
\frac{D\_yF\_t(x-\Delta x,y,A(t))}{D\_xF\_t(x,y+\varphi(\Delta x),A(t))} - \left (1 + \frac{\beta \bar{R}}{1+\bar{R}}\right )^{\frac{T-t}{\text{year}}} = 0
$$

where $$\varphi$$ is the trading function implicitly defined by the invariant and $$\Delta x$$ is chosen such that

$$
\frac{x - \Delta x}{y + \varphi(\Delta y))} = l
$$

## Adjusting the fees

The fees are adjusted such that the base fee , taken on the implied APY, stays constant over the term.


# Glossary

## APW

APW is the governance token of Spectra Finance. It is used for governing the protocol. More information will be announced shortly.

## APWine

APWine is the previous name of the Spectra protocol. There may still be some references to the old name in the contracts and documentation.

## AMM

AMM stands for Automated Market Maker. It is a type of decentralised exchange (DEX) where users can easily swap certain assets to other assets. Liquidity Providers (LPs) can also provide those assets as liquidity in the AMM, earning swap fees for their services. Examples include Curve Finance and Uniswap.

## IBT

IBT stands for Interest Bearing Token. It is a token that represents a deposit in a yield generating protocol such as Aave, Compound, or Yearn finance. For example, a user deposits 100 USDC into Aave, receiving 100 aUSDC in return. The 100 aUSDC would be the IBT, since it is accruing yield generated from Aave.

The IBT can then be deposited into the [Principal Token](/technical-reference/contract-functions/principal-token) to split the principal and yield, creating interest rate derivatives. For more information, see [Tokenizing Yield](/guides/tokenizing-yield).

## Principal Token

The Principal Token (PT) is the token that represents the original (principal) amount that was deposited into the Spectra protocol. For example, if a user deposits 100 aUSDC, they will receive the principal tokens that represent 100 USDC, since the interest earned is split into the [Yield Token](#yield-token).

For more information, see [Tokenizing Yield](/guides/tokenizing-yield)

## Shares (in relation to PrincipalToken)

Shares represent the user's share of a vault. In the case of the PrincipalToken, it represents the original deposit amount, i.e. the principal amount. Therefore when a user deposits 100 aUSDC, they will receive [principal tokens ](#principal-token)representing 100 USDC and the associated [yield tokens](#yield-token). The principal tokens are their 'share' of the vault, in relation to all other user deposits in the same vault.

In the contracts it is used interchangeably with [Principal Token](#principal-token).

## Tokenisation

Tokenisation refers to the operation of converting something, usually an asset, into a token. This is innovative and powerful as it enables the resulting token to be used throughout DeFi, in many use cases that the original designers may not have even imagined. It also allows composability between protocols, giving more freedom and flexibility to end users.

## Vault

A vault is a contract that stores multiple user assets and follows certain guidelines to ensure the safety of those user assets. All vaults are [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) compliant.

## veAPW

veAPW stands for Vote Escrowed APW token. It is used in governance, as detailed in the [voting](broken://pages/beQ4uXXXHEFJem0XQpb7) section. They are APW tokens that have been 'locked' for a certain period to enable voting power in the governance system.

## Yield

Yield is the interest generated from a productive asset. In the context of DeFi, the yield is generated from an underlying protocol which performs certain operations that produce interest income. For example, Aave protocol enables lenders and borrowers to form a money market, generating interest for the lenders by charging fees to the borrowers.

## Yield Marketplace

The Yield Marketplace is an upcoming feature of the Spectra protocol. More details will come soon.

## Yield Token

The Yield Token (YT) represents the yield that will accrue to a certain [Interest Bearing Token](#ibt). For example, if a user deposits 100 USDC into Aave, they will receive 100 aUSDC that represents their deposit in Aave. If they take the resulting 100 aUSDC and deposit it into Spectra, they will receive [Principal Tokens](#principal-token) representing the 100 USDC and Yield Tokens representing the interest that will accrue to that deposit.

This is powerful and innovative as it allows the principal amount and interest earned to be separated, enabling new use cases in DeFi such as interest rate hedging or other interest rate derivatives.

## MetaVault

A MetaVault is a cross-chain managed yield vault. It primarily aggregates liquidity positions (LP positions consisting of PT + IBT) across multiple markets and chains, and can also hold PTs and other assets. Idle liquidity can be deposited in liquid DeFi protocols for additional yield. MetaVaults delegate allocation to permissioned curators while enforcing on-chain safety constraints via [Zodiac](#zodiac). They follow the [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) asynchronous vault standard. See the [MetaVaults Overview](/metavaults/metavaults) for more details.

## MetaVaultWrapper

The MetaVaultWrapper is the user-facing contract for a MetaVault. It implements the ERC-7540 interface (`requestDeposit`, `deposit`, `requestRedeem`, `redeem`) and holds wrapper shares (ERC-20) representing the depositor's pro-rata claim on the underlying infrastructure vault. See [MetaVaultWrapper](/metavaults/contract-functions/metavault-wrapper).

## Epoch

An epoch is a discrete time period between two settlements. During an epoch, new deposit and redeem requests are queued but not processed until the next settlement. At the boundary between epochs, the accountant calls `settle()` to process all pending requests, compute performance fees, and start a new epoch.

## Curator

A Curator is the permissioned role responsible for allocating liquidity within a MetaVault. The curator can buy/sell Principal Tokens and add/remove Curve liquidity — but only within the boundaries enforced by the Zodiac RolesModifier and on-chain validation contracts. See [Access Control and Roles](/metavaults/access-control-and-roles).

## Infrastructure Vault

The Infrastructure Vault (also referred to as "infra vault") is the underlying vault contract (based on Amphor's AsyncVault) that actually holds and deploys the MetaVault's assets. The [MetaVaultWrapper](#metavaultwrapper) delegates deposit/redeem operations to this vault while maintaining its own share accounting layer on top.

## Settle

Settle is the process that occurs at the boundary between [epochs](#epoch). During settlement, the accountant reports the new vault underlying value, performance fees are calculated and sent to the treasury, pending deposit requests are converted to shares, and pending redeem requests are converted to assets. The `settle()` function increments the epoch ID and begins a new epoch.

## Max Drawdown

Max Drawdown is a safety parameter (expressed in basis points) that limits how much the vault's reported balance can decrease between epochs. If the new balance reported during `settle()` falls below `lastSavedBalance * (10000 - maxDrawdown) / 10000`, the transaction reverts. This protects depositors from catastrophic losses. The default max drawdown is 30% (3000 bps).

## Zodiac

[Zodiac](https://www.gnosisguild.org/) is a modular governance framework for [Safe](https://safe.global/) multisigs, developed by [Gnosis Guild](https://github.com/gnosisguild). MetaVaults use the Zodiac [RolesModifier](https://github.com/gnosisguild/zodiac-modifier-roles) to enforce fine-grained permissions on what the curator Safe can do, and the [Delay module](https://github.com/gnosisguild/zodiac-modifier-delay) for time-delayed execution of sensitive actions. Each permitted action is scoped to specific contract addresses, function selectors, and parameter constraints. See [Access Control and Roles](/metavaults/access-control-and-roles) and the [Zodiac Roles documentation](https://docs.roles.gnosisguild.org/).


# Spectra Oracles

Spectra oracles facilitate the integration of Spectra's assets across all DeFi. In particular, they are designed to provide robust price feeds - robust to price manipulation - for integrations in lending markets.&#x20;

In this page, we describe the available oracles for Spectra's Principal Tokens (PT), Yield Tokens (YT) and LP Tokens (LP).

Spectra offers two types of oracles:

1. [Deterministic Oracles](/integration-reference/spectra-oracles/deterministic-oracles): offer a price reference of the PT against the underlying, evolving according to a pricing model based on an expected implied APY set at deployment. They are designed such that the reported price matches the par value of the PT at maturity.&#x20;
2. [Time-Weighted Average Price (TWAP) Oracles](/integration-reference/spectra-oracles/twap-oracles): based on an exponential moving average (EMA) of the prices given by the AMM, those oracles provide a price reference for the PT/YT/LP both in IBT and underlying asset.&#x20;
3. Hybrid Oracles that combine the best of TWAP Oracles and Deterministic Oracles: accurately represent the market by using the TWAP oracles and lower bounding it with a deterministic oracle to offer more robustness against price manipulation.&#x20;

For fetching the latest price, Spectra oracles follow[ Chainlink's standard](https://tfsaggregator.github.io/docs/v3/). That is, one can get the latest data point by calling

```solidity
    function latestRoundData()
        external
        view
        returns (
         uint80 roundId,
         int256 answer,
         uint256 startedAt,
         uint256 updatedAt,
         uint80 answeredInRound
         )
```

The oracles being fully on-chain, one can ignore `roundId`, `startedAt`, `updatedAt`, `answeredInRound` return arguments.&#x20;


# TWAP Oracles

The TWAP oracles report an [exponential moving average](https://en.wikipedia.org/wiki/Moving_average) of the prices of Principal Tokens, Yield Tokens and LP tokens, quoted in either interest bearing token or underlying asset. They primarily serve as one of the pricing references for lending market integrations.&#x20;

### Base Oracle Templates

The `BaseOracle` provides the base template of Spectra TWAP oracles. It follows Chainlink's [AggregatorV3Interface](https://docs.chain.link/data-feeds/api-reference). This template takes the address of the Principal Token and the address of the underlying pool at construction. Currently, we integrate the [twocrypto-ng](https://github.com/curvefi/twocrypto-ng) and the [stableswap-ng](https://github.com/curvefi/stableswap-ng) AMMs from Curve.&#x20;

The prices are fetched through the `getRoundData()` and `latestRoundData()` functions, both returning the latest version of the price data. The `decimals()` method is overriden and specifies the decimals in which the prices are quoted. The `_getQuoteAmount()` function is internally overriden and fetches the price data.&#x20;

This template is used to create the `BaseOracleCurveYT`, `BaseOracleCurvePT` and `BaseOracleCurveLP` contracts, which provide templates for reporting prices of the Yield Token, the Principal Token and the LP token respectively in a quote asset. In our case, the quote asset will either be the corresponding interest bearing token or the underlying asset.&#x20;

The aforementioned contracts define the virtual methods `_YTPrice()` , `_PTPrice()` and `_LPTPrice()` , whose role is to report the oracle price of the corresponding asset in a given quote asset. These have to be defined in the corresponding Chainlink Feed.&#x20;

### Chainlink Feeds

The Chainlink feeds acts as an isolated price data provider for a given base asset and a quote asset. Since we have 3 base assets to report the price of (PT, YT, LP), 2 quote assets (IBT, Underlying) and we support 2 AMMs (twocrypto-ng, stableswap-ng), we need 12 feeds.&#x20;

The feeds define `_YTPrice()` , `_PTPrice()` and `_LPTPrice()` by making calls to the library `CurveOracleLib` .  It also specifies the decimals of the data feed, which are the decimals of the quote asset.

This library takes care of fetching the prices from the underlying Curve pool by calling the `price_oracle()`  method. This method implements an exponential moving average of the spot prices as given by the AMM, along with native oracle manipulation protection. For further information, we refer the reader to the [Curve Documentation. ](https://docs.curve.fi/)

### Creating a Spectra Oracle instance

To deploy a TWAP oracle instance, you must create and deploy a contract inheriting from the appropriate Chainlink feed class. For instance, to deploy a price oracle for a PrincipalToken quoted in the underlying asset within a StableSwap pool, your contract should inherit from BaseFeedCurvePTAssetSNG.

Below is an example of a mock TWAP oracle implementation designed for obtaining PrincipalToken prices quoted in the underlying asset from a StableSwap AMM.

```solidity
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.20;

import {AggregatorV3Interface} from "src/interfaces/AggregatorV3Interface.sol";
import {BaseFeedCurvePTAssetSNG} from "src/spectra-oracles/chainlinkFeeds/stableswap-ng/BaseFeedCurvePTAsset.sol";

/// @dev Mock Curve PT price feed that gives the PT price in a provided IBT/PT Curve Pool in asset
contract MockPriceFeedCurvePTAssetSNG is BaseFeedCurvePTAssetSNG {
    string public constant description = "IBT/PT Curve Pool Oracle: PT price in asset";

    /* CONSTRUCTOR
     *****************************************************************************************************************/
    /**
     * @notice Constructor for a Mock Price Feed of a Curve Pool (in asset)
     */
    constructor(address _pt, address _pool) BaseFeedCurvePTAssetSNG(_pt, _pool) {}
}
```

If you do not plan on defining additional logic, you can deploy Spectra's oracles through the [Spectra Oracle Factory](/integration-reference/spectra-oracles/oracle-deployment/twap-and-hybrid-oracles-deployment).&#x20;


# Deterministic Oracles

Spectra offers semi-deterministic pricing models for the Principal Token (PT). Those oracles match the redemption value of the PT at maturity, that is one unit of underlying adjusted by the `ptRate` .&#x20;

We implement various discounting models, whose logic we explain in the subsequent pages.

To fetch the latest PT price in terms of underlying, one queries the `latestRoundData()` method from the `SpectraPriceOracle` contract. Dependent on the discounting model chosen at construction, this will either return the PT price in underlying discounted according to the linear discount model or according to the zero-coupon bond model.&#x20;


# Linear APR model

The linear APR model is a pricing model for a Principal Token quoted in underlying. It assumes that the price of the PT is a unit of underlying discounted by a non-compounded implied rate that stays constant during the term. More specifically, is assumes

$$
P(t,T) =\text{PT.previewRedeem(1)}\frac{1}{1+r(T-t)}
$$

where $$\text{previewRedeem(1)}$$ gives the redemption rate of the PT,  $$T$$ is the maturity timestamp, while $$t$$ is the current timestamp.


# Linear Discount Model

The linear discount model is a pricing model for the Principal Token (PT) that assumes the PT discount evolves linearly in time.  At start time, the model prices the PT as its par value discounted by the non-compounded instantaneous rate $$r$$ over the term. At maturity, the model prices the PT at its par value. The model linearly interpolates between those two points. More specifically

$$
P(t,T) = \text{PT.previewRedeem}(1) (1 - \frac{1}{1 + r(T-t)})\frac{t}{T} + \frac{\text{PT.previewRedeem}(1)}{1 + r(T-t)}
$$

where $$\text{previewRedeem(1)}$$ gives the redemption rate of the PT,  $$T$$ is the maturity timestamp, while $$t$$ is the current timestamp.


# Zero Coupon Bond Model

The zero coupon bond model is a pricing model for a Principal Token quoted in underlying. It assumes that the price of the PT is a unit of underlying discounted by an implied rate that stays constant during the term. More specifically, is assumes

$$
P(t,T) =\text{PT.previewRedeem(1)}\exp(-(T-t)r)
$$

where the $$\text{previewRedeem}$$ of the PT gives its redemption value , $$T$$ is the maturity timestamp and $$t$$ is the current timestamp.&#x20;


# Comparison

Here is a visual comparison of the three pricing models used in Spectra's deterministic oracles. It assumes a duration of 1 year, a 100% implied rate and a par value of 1 for the PT.&#x20;

<figure><img src="/files/9L5Flf5M1bkr4adqlGxi" alt=""><figcaption></figcaption></figure>


# Hybrid Oracles

In this section we describe the mechanism behind the Hybrid Oracles

Hybrid Oracles combine TWAP Oracle with the Zero Coupon Bond pricing model for the PT in underlying asset by returning the maximum between the two feeds. Additionally, hybrid oracles upper bound the PT price by its redemption value.&#x20;

Such approach enables

1. Having a LTV threshold under which no position can be liquidated by providing a lower bound on the price
2. Provide additional safeguards against price manipulation

### Combining the TWAP Oracle with the Zero Coupon Bond Model

Assume we are given an expected implied rate $$R$$ at which we assume the PT will be trading. We then instantiate a Deterministic Oracle based on Zero Coupon Bond Model using the implied rate $$R$$, as described in [Zero Coupon Bond Model](/integration-reference/spectra-oracles/deterministic-oracles/zero-coupon-bond-model), along with a [TWAP Oracle](/integration-reference/spectra-oracles/twap-oracles) that simply reports the EMA of the PT price in underlying asset.&#x20;

We combine the two feeds by reporting the maximum between their two values. This allows to lower bound the TWAP oracle value in case of a large rise in the implied rate, thus effectively defining a LTV threshold at which no position can be liquidated assuming no negative rate. More specifically, let $$D(t)$$ be the value of the ZCB model based oracle at time $$t$$ and $$S(t)$$ be the value of the TWAP oracle feed at time $$t$$.&#x20;

The hybrid oracle combines those two feeds by reporting

$$
H(t) = \min {\alpha, \max{D(t), S(t)}}
$$

where $$\alpha$$ is the redemption value of the PT.&#x20;

#### Deployment

We describe in the  [TWAP and Hybrid Oracles Deployment](/integration-reference/spectra-oracles/oracle-deployment/twap-and-hybrid-oracles-deployment)how to deploy an instance of the Hybrid Oracle through the TWAP oracle factory.&#x20;


# Oracle Deployment

In this page, we describe how you can deploy Spectra Oracles.&#x20;

* [Deterministic Oracles](/integration-reference/spectra-oracles/deterministic-oracles): deployment through `SpectraPriceOracleFactory`&#x20;
* [TWAP Oracles](/integration-reference/spectra-oracles/twap-oracles): deployment through `TwapOracleFactoryNG` and `TwapOracleFactorySNG` , the former used for `twocrypto-ng` pools, the latter for `stableswap-ng` .&#x20;


# Deterministic Oracles Deployment

### Overview

Deployment of deterministic oracles, both according to the linear discount model and the zero-coupon bond model, is done through the `SpectraPriceOracleFactory` .&#x20;

### Deployment

One deploys a deterministic oracle through the method

```solidity
/**
* @dev Deploys a new `SpectraOracle` for a given PT.
* @param _pt The address of the Principal Token (PT).
* @param _discountModel The discount model address.
* @return oracle The address of the newly deployed Oracle.
*/
function createOracle(
   address _pt,
   address _discountModel,
   uint256 initialImpliedAPY,
   address initOwner
) external returns (address oracle)
```

The `_discountModel` address corresponds to the address of either the `LinearDiscountModel` , `LinearAPRModel` or `ZeroCouponDiscountModel` , depending on the pricing model one wants to use.&#x20;

The `initialImpliedAPY` field is the implied APY according to which we want to discount the PT over the term. The  `initialImpliedAPY` field is in 18 decimals precision, `10 ** 18` representing a `100%`  implied APY.&#x20;

The owner of the oracle contract can change the discount model used in the oracle by calling `setDiscountModel()` .

### Example

For example, to deploy an oracle with a `30%` implied APY over the term, one does

```solidity

createOracle(
   address(0) // PT address,
   address(0) // Discount Model Contract address,
   3e17, // 30% implied APY
   address(0) // Address of the owner
) external returns (address oracle)
```


# TWAP and Hybrid Oracles Deployment

### Overview

To deploy a standard TWAP Oracle, as well as a hybrid oracle, you can use the `TwapOracleFactorySNG` ,

### Deployment

Deployment of Spectra TWAP oracles as well as the hybrid oracle is done through the `deployOracle()`  method described below

```solidity
    /**
     * @notice Deploys an oracle for a given principal token, pool, and oracle type
     * @param _pt The address of the principal token
     * @param _pool The address of the pool
     * @param _impliedRate The implied rate. Relevant only for hybrid oracles
     *                     1e18 represents 100%, so for example an implied rate of 30%
     *.                    is represesnted
     * @return oracleAddress The address of the deployed oracle
     */
    function deployOracle(
        address _pt,
        address _pool,
        uint256 _impliedRate,
        OracleType _oracleType
    ) external returns (address oracleAddress)
```

It deploys an oracle of `_oracleType` for a market composed of a PT of address `_pt` and of market address `_pool` . The `OracleType _oracleType` field allows you to choose the type of oracle you want to deploy.  The `_impliedRate` field is used exclusively for the [Hybrid Oracle](/integration-reference/spectra-oracles/hybrid-oracles) and ignored otherwise. The possible types are

```solidity
enum OracleType {
        PTIBT,
        PTUND,
        YTIBT,
        YTUND,
        LPIBT,
        LPUND,
        PTUNDHYBRID
    }
```

The possible types specify the base asset (PT, YT, LP) followed by the quote asset (IBT, Underlying).&#x20;


# Overview

MetaVaults are cross-chain managed yield vaults. They primarily aggregate liquidity positions (LP positions consisting of PT + IBT) across multiple markets and chains, and can also hold PTs and other assets. Idle liquidity can be deposited in liquid DeFi protocols for additional yield while honoring redemption requests.

## What problem do MetaVaults solve?

Managing liquidity across multiple yield markets involves finicky processes: monitoring maturities, redeeming positions, re-allocating across pools, and ensuring enough liquidity is available for redemptions. MetaVaults automate this by delegating liquidity allocation to a permissioned curator while enforcing on-chain safety constraints via [Zodiac](https://www.gnosisguild.org/).

## Key properties

| Property             | Detail                                                                                     |
| -------------------- | ------------------------------------------------------------------------------------------ |
| **Standard**         | [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) — asynchronous deposit/redeem          |
| **Share token**      | ERC-20, issued by the [MetaVaultWrapper](/metavaults/contract-functions/metavault-wrapper) |
| **Underlying asset** | A single ERC-20 (e.g. USDC) per vault                                                      |
| **Strategy scope**   | Provide Curve liquidity, buy/sell PTs, deposit idle assets in DeFi protocols               |
| **Access control**   | [Zodiac](https://www.gnosisguild.org/) RolesModifier + on-chain validation contracts       |
| **Fee model**        | Performance fee on positive yield, collected at [settle](/glossary#settle)                 |

## How it works — the epoch lifecycle

1. **Request** — depositors call `requestDeposit` or `requestRedeem`. Assets and shares are queued for the next settlement.
2. **Curator allocates** — the curator deploys queued and idle assets into yield strategies (LP positions, PTs, etc.) within the boundaries enforced by Zodiac.
3. **Settle** — the accountant reports the new vault underlying value via `settle()`. Fees are collected, pending requests are processed, and a new epoch begins.
4. **Share mint / share burn** — depositors call `deposit` (mint shares) or `redeem` (burn shares for assets) to collect their settled positions.

{% hint style="info" %}
Yield accrues from the epoch the deposit request is settled — depositors do not need to claim (`deposit`) their shares to start earning. Unclaimed shares continue to accrue yield until they are minted.
{% endhint %}

For a detailed walkthrough of the deposit/redeem flow, see [Depositing and Redeeming](/metavaults/depositing-and-redeeming).

## Entities

### Depositor

Deposits underlying assets (e.g. USDC) into a MetaVault and receives wrapper shares. Depositors are passive — they rely on the curator to allocate liquidity and the on-chain constraints to enforce safety.

* [Depositing and Redeeming](/metavaults/depositing-and-redeeming)
* [Fee Model](/metavaults/fee-model)

### Curator

Allocates liquidity within the MetaVault: providing/removing Curve liquidity, buying/selling PTs, deploying idle assets. All actions are scoped by Zodiac roles and validated on-chain.

* [Access Control and Roles](/metavaults/access-control-and-roles)
* [Architecture](/metavaults/architecture)

## Contract overview

| Contract                                                                 | Purpose                                                                                                                                                |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [MetaVaultWrapper](/metavaults/contract-functions/metavault-wrapper)     | User-facing ERC-7540 vault — handles deposit/redeem requests and wrapper share accounting                                                              |
| [AsyncVault](/metavaults/architecture#infrastructure-vault)              | Infrastructure vault ([Amphor](https://github.com/AmphorProtocol/asynchronous-vault/tree/main)) — epoch management, share calculations, fund isolation |
| [MetavaultsRegistry](/metavaults/contract-functions/metavaults-registry) | On-chain registry of vaults, markets, and chains                                                                                                       |


# Architecture

This page describes the contract architecture of MetaVaults, how the components interact, and the design decisions behind the system.

## Contract stack

{% @mermaid/diagram content="graph TB
%% Entities
User(\["<b>DEPOSITOR</b><br/>Deposit / Withdraw"])
Curator(\["<b>CURATOR</b><br/>Allocate Liquidity /<br/>Deploy Strategies"])
Guardian(\["<b>GUARDIAN</b><br/>Guard Curator Actions /<br/>Check Timelocked Actions"])
Accountant(\["<b>ACCOUNTANT</b><br/>Performance Tracking,<br/>Share Value Calculations,<br/>Epoch Management"])

```
%% Contracts
MW["MetaVault Wrapper<br/>ERC-7540"]
IV["Infrastructure Vault"]
AC{"Access Control<br/>(Zodiac)"}
GS["SAFE"]
SM[("Markets")]

%% User flow
User -.-> MW
MW --> IV
IV --> GS
GS --> SM

%% Roles through Zodiac
Curator -.-> AC
Guardian -.-> AC
Accountant -.-> AC
AC -.-> GS

%% Styling
classDef entity fill:#f5c542,stroke:#c9a227,stroke-width:2px,color:#000
classDef contract fill:#1b4332,stroke:#1b4332,stroke-width:2px,color:#fff
classDef zodiac fill:#2d6a4f,stroke:#2d6a4f,stroke-width:2px,color:#fff

class User,Curator,Guardian,Accountant entity
class MW,IV,GS,SM contract
class AC zodiac" %}
```

## MetaVaultWrapper

The [MetaVaultWrapper](/metavaults/contract-functions/metavault-wrapper) is the user-facing entry point. It implements [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) (asynchronous vault) and issues ERC-20 wrapper shares. The wrapper layer exists to allow infrastructure vault migrations without disrupting depositors, and to standardise the interface and events under ERC-7540.

Key responsibilities:

* Accept `requestDeposit` / `requestRedeem` from depositors
* Track per-user balances per epoch in wrapper-level storage
* Delegate actual asset custody to the infrastructure vault
* Mint/burn wrapper shares on settlement (`deposit` / `redeem`)

## Infrastructure Vault

The infrastructure vault is the core accounting layer responsible for epoch management, share price calculations, and isolating incoming/outgoing funds. It handles the `settle()` call, which computes performance fees, processes pending deposits and redeems, and increments the epoch.

The current infrastructure vault implementation is the [Amphor AsyncVault](https://github.com/AmphorProtocol/asynchronous-vault/tree/main). The accountant role calls `settle()` to trigger epoch transitions.

### Epoch data

Each epoch stores a snapshot of `totalSupply` and `totalAssets` at settlement time. This allows share/asset conversions for any historical epoch, which is critical for correctly processing claims from different request epochs.

## MetavaultsRegistry

The [MetavaultsRegistry](/metavaults/contract-functions/metavaults-registry) is a global on-chain registry that tracks:

* **Vaults** — registered MetaVault addresses
* **Markets** — whitelisted Curve pools (and their associated PT, YT, IBT, asset addresses)
* **Chains** — registered destination chains with remote vault addresses

The registry is access-managed (`AccessManagedUpgradeable`) — only authorized callers can modify it. On-chain validation contracts read from the registry at execution time to validate curator actions.

## Zodiac and Safe architecture

Each MetaVault is owned by a [Safe](https://safe.global/) multisig. The Safe uses the [Zodiac](https://www.gnosisguild.org/) [RolesModifier](https://github.com/gnosisguild/zodiac-modifier-roles) to delegate specific, scoped permissions to the curator role.

For more details on the Zodiac framework, see the [Gnosis Guild documentation](https://docs.roles.gnosisguild.org/).

### How scoping works

The curator is not a Safe signer — instead, the curator executes transactions against a RolesModifier, which acts as the gateway to the Safe. When the curator submits a transaction, the RolesModifier verifies that:

1. The sender belongs to the correct role (e.g. `CURATOR`).
2. The target contract and function selector are allowed for that role.
3. The parameters passed to the function match the restrictions configured for that scope.

Each permitted action is scoped with a combination of constraints. The available [scoping types](https://github.com/gnosisguild/zodiac-modifier-roles/blob/main/packages/evm/contracts/Types.sol) include target address restrictions, function-level allowlists, and parameter-level comparisons (equal, greater than, less than, one-of, etc.).

If any check fails, the transaction is reverted before reaching the Safe. This ensures that even a compromised curator key cannot interact with unauthorized contracts, functions, or parameter values.

### Delay module

A [Delay module](https://github.com/gnosisguild/zodiac-modifier-delay) can be configured between the curator role and execution, adding a time delay before sensitive actions take effect. This gives guardians time to veto malicious transactions.


# Depositing and Redeeming

MetaVaults follow the [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) asynchronous vault standard. Unlike standard ERC-4626 vaults where deposits and redeems are instant, MetaVaults use a **request → settle → share mint / share burn** flow.

## Deposit flow

### Step 1: Request a deposit

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

The depositor transfers `assets` (underlying token, e.g. USDC) to the wrapper. The assets are forwarded to the infrastructure vault.

{% hint style="warning" %}
`msg.sender` must approve the MetaVaultWrapper to spend the underlying asset before calling `requestDeposit`.
{% endhint %}

**Requirements:**

* `assets > 0`
* `owner == msg.sender` or `msg.sender` is an approved operator of `owner`
* The vault must not be paused

**What happens internally:**

1. If the controller has a claimable deposit from a previous epoch, it is automatically claimed first.
2. Assets are transferred from `owner` to the wrapper, then forwarded to the infrastructure vault.
3. The request is recorded in the current epoch's deposit request balance.

### Step 2: Wait for settlement

The accountant calls `settle()` on the infrastructure vault to end the current epoch. During settlement:

* Pending deposit assets are converted to shares using the epoch's total supply and total assets snapshot.
* Shares are minted and made available for claiming.

### Step 3: Mint shares

```solidity
function deposit(
    uint256 assets,     // not used — all claimable shares are minted
    address receiver,
    address controller
) external returns (uint256 shares)
```

{% hint style="info" %}
The `assets` parameter is not used in the MetaVaultWrapper implementation. Calling `deposit` always mints **all** claimable shares for the controller.
{% endhint %}

**Requirements:**

* `controller == msg.sender` or `msg.sender` is an approved operator of `controller`
* The controller must have a claimable deposit from a settled epoch

**Returns:** The number of wrapper shares minted to `receiver`.

### Complete deposit example

```solidity
// 1. Approve the wrapper to spend USDC
IERC20(usdc).approve(address(wrapper), 1000e6);

// 2. Request deposit
wrapper.requestDeposit(1000e6, msg.sender, msg.sender);

// ... wait for settle() ...

// 3. Claim shares
uint256 shares = wrapper.deposit(0, msg.sender, msg.sender);
```

## Redeem flow

### Step 1: Request a redeem

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

The depositor's wrapper shares are burned and the equivalent infrastructure vault shares are queued for redemption.

**Requirements:**

* `shares > 0`
* `owner == msg.sender` or `msg.sender` is an approved operator of `owner`
* The vault must not be paused

**What happens internally:**

1. If the controller has a claimable redeem from a previous epoch, it is automatically claimed first.
2. Wrapper shares are burned from `owner`.
3. The corresponding infrastructure vault shares are queued for redemption.
4. The request is recorded in the current epoch's redeem request balance.

### Step 2: Wait for settlement

During `settle()`, pending redeem shares are converted to assets using the epoch's snapshot values. Assets are made available for claiming.

### Step 3: Burn shares for assets

```solidity
function redeem(
    uint256 shares,     // not used — all claimable assets are returned
    address receiver,
    address controller
) public returns (uint256 assets)
```

{% hint style="info" %}
Like `deposit`, the `shares` parameter is not used. Calling `redeem` always returns **all** claimable assets for the controller.
{% endhint %}

**Returns:** The amount of underlying assets transferred to `receiver`.

### Complete redeem example

```solidity
// 1. Request redeem of 500 shares
wrapper.requestRedeem(500e6, msg.sender, msg.sender);

// ... wait for settle() ...

// 2. Claim assets
uint256 assets = wrapper.redeem(0, msg.sender, msg.sender);
```

## Decreasing a request

If a depositor changes their mind before settlement, they can decrease their pending request:

```solidity
// Decrease a deposit request (get assets back)
wrapper.decreaseDepositRequest(500e6);

// Decrease a redeem request (get shares back)
wrapper.decreaseRedeemRequest(250e6);
```

These functions can only be called by the original requester (`msg.sender`). The refunded assets or shares are returned directly to `msg.sender`.

## Operator model

MetaVaults support the ERC-7540 operator pattern. A controller can approve an operator to act on their behalf:

```solidity
wrapper.setOperator(operatorAddress, true);
```

Once approved, the operator can call `requestDeposit`, `deposit`, `requestRedeem`, and `redeem` on behalf of the controller.

## View functions for integrators

| Function                                         | Returns                                                         |
| ------------------------------------------------ | --------------------------------------------------------------- |
| `pendingDepositRequest(requestId, controller)`   | Assets queued in the current epoch                              |
| `claimableDepositRequest(requestId, controller)` | Assets claimable from a settled epoch                           |
| `pendingRedeemRequest(requestId, controller)`    | Shares queued in the current epoch                              |
| `claimableRedeemRequest(requestId, controller)`  | Shares claimable from a settled epoch                           |
| `maxDeposit(receiver)`                           | Maximum claimable deposit amount (0 if paused or current epoch) |
| `maxRedeem(owner)`                               | Maximum claimable redeem amount (0 if paused or current epoch)  |
| `convertToShares(assets)`                        | Convert assets to shares using the last settled epoch rate      |
| `convertToAssets(shares)`                        | Convert shares to assets using the last settled epoch rate      |
| `convertToShares(assets, epochId)`               | Convert using a specific epoch's rate                           |
| `convertToAssets(shares, epochId)`               | Convert using a specific epoch's rate                           |
| `totalAssets()`                                  | Estimated total assets of the wrapper                           |
| `epochId()`                                      | Current epoch ID from the infrastructure vault                  |

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


# Deployed Contracts

{% hint style="info" %}
All deployed contracts are verified on their respective block explorer. MetaVault contracts use a proxy pattern — the addresses below are the proxy addresses that users and integrators should interact with.
{% endhint %}

{% tabs %}
{% tab title="Ethereum" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th>Address</th></tr></thead><tbody><tr><td>MetavaultsRegistry</td><td><a href="https://etherscan.io/address/0x16b28223b607b4d90e2ec628fdc1a0ad9ee1f6b8">0x16b28223b607b4d90e2ec628fdc1a0ad9ee1f6b8</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Base" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th>Address</th></tr></thead><tbody><tr><td>MetavaultsRegistry</td><td><a href="https://basescan.org/address/0xa2c9da26c1982cacaf01c5c691e0cf0aeb031ac1">0xa2c9da26c1982cacaf01c5c691e0cf0aeb031ac1</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Arbitrum" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th>Address</th></tr></thead><tbody><tr><td>MetavaultsRegistry</td><td><a href="https://arbiscan.io/address/0xa355dd06f35bcfe98e45f743397b729b93c560be">0xa355dd06f35bcfe98e45f743397b729b93c560be</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Avalanche" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th>Address</th></tr></thead><tbody><tr><td>MetavaultsRegistry</td><td><a href="https://snowscan.xyz/address/0xacf33983cab5f9e914e6a93a2f4d531b5c516602">0xacf33983cab5f9e914e6a93a2f4d531b5c516602</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Katana" %}

<table><thead><tr><th width="241.33333333333331">Contract</th><th>Address</th></tr></thead><tbody><tr><td>MetavaultsRegistry</td><td><a href="https://katanascan.com/address/0x191ea03cccc27546ba9f5161b4948b6afce5263f">0x191ea03cccc27546ba9f5161b4948b6afce5263f</a></td></tr></tbody></table>
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Individual MetaVaultWrapper instances are deployed per vault and are not listed here as they are vault-specific. Check the [MetavaultsRegistry](/metavaults/contract-functions/metavaults-registry) on each chain to query registered MetaVaults.
{% endhint %}


# Contract Functions

This section contains the full function reference for all MetaVault contracts. Each page documents the public and external functions, view functions, events, and errors for a specific contract.

## Core contracts

| Contract                                                                 | Description                                                                                    |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| [MetaVaultWrapper](/metavaults/contract-functions/metavault-wrapper)     | User-facing ERC-7540 vault — deposit/redeem requests, wrapper share accounting, operator model |
| [MetavaultsRegistry](/metavaults/contract-functions/metavaults-registry) | On-chain registry for vaults, markets, and chains                                              |


# MetaVaultWrapper

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

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

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

## Methods

### requestDeposit

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

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

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

{% hint style="warning" %}
`msg.sender` must be `owner` or an approved operator of `owner`. The owner must approve the wrapper for the underlying asset before calling this function.
{% endhint %}

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

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

### decreaseDepositRequest

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

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

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

### deposit

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

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

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

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

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

### requestRedeem

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

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

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

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

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

### decreaseRedeemRequest

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

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

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

### redeem

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

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

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

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

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

### setOperator

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

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

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

### updateEpochID

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

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

## View Methods

### getInfraVault

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

Returns the address of the underlying infrastructure vault.

### isOperator

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

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

### totalVaultShares

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

Returns the total infrastructure vault shares held by the wrapper.

### epochId

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

Returns the current epoch ID from the infrastructure vault.

### lastUserDepositRequestEpoch

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

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

### lastUserRedeemRequestEpoch

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

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

### convertToShares

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

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

### convertToShares

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

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

### convertToAssets

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

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

### convertToAssets

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

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

### maxDeposit

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

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

### maxRedeem

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

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

### pendingDepositRequest

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

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

### claimableDepositRequest

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

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

### pendingRedeemRequest

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

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

### claimableRedeemRequest

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

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

### totalAssets

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

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

### decimals

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

Returns the decimals of the underlying asset.

### share

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

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

### supportsInterface

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

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

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

## Events

### DepositRequest

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

Emitted when a deposit request is created.

### RedeemRequest

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

Emitted when a redeem request is created.

### OperatorSet

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

Emitted when an operator approval is set or revoked.

### DecreaseDepositRequest

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

Emitted when a deposit request is decreased.

### DecreaseRedeemRequest

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

Emitted when a redeem request is decreased.

### ClaimPendingDeposit

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

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

### ClaimPendingRedeem

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

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

### MetaVaultWrapperInitialized

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

Emitted when the wrapper is initialized.

## Errors

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


# MetavaultsRegistry

The MetavaultsRegistry is the on-chain registry for managing MetaVaults and their associated markets, chains, and bridge paths. It is access-managed — only authorized callers can modify the registry. On-chain validation contracts read from this registry to validate curator actions.

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

## Structs

### PoolInfos

```solidity
struct PoolInfos {
    address pool;    // Curve pool address
    address pt;      // Principal Token address
    address yt;      // Yield Token address
    address ibt;     // Interest Bearing Token address
    address asset;   // Underlying asset address
}
```

Cached pool metadata, populated automatically when a market is registered.

### MarketConfig

```solidity
struct MarketConfig {
    bool isRegistered;
}
```

### MetavaultChainConfig

```solidity
struct MetavaultChainConfig {
    uint256 chainId;
    address remoteMetavaultAddress;
}
```

### BridgePath

```solidity
struct BridgePath {
    address tokenIn;
    address tokenOut;
    uint32 dstChainId;
    address bridge;
}
```

## View Methods

### getPoolInfos

```solidity
function getPoolInfos(address market) external view returns (PoolInfos memory)
```

Returns the cached pool info (pool, PT, YT, IBT, asset) for a market.

### getMarketFromPT

```solidity
function getMarketFromPT(address pt) public view returns (address)
```

Returns the Curve pool address associated with a Principal Token.

### getMarketFromYT

```solidity
function getMarketFromYT(address yt) public view returns (address)
```

Returns the Curve pool address associated with a Yield Token.

### getMarketConfig

```solidity
function getMarketConfig(address metavault, address market) external view returns (MarketConfig memory)
```

Returns the market config for a specific metavault-market pair.

### getBridgePaths

```solidity
function getBridgePaths(address metavault) public view returns (BridgePath[] memory)
```

Returns all registered bridge paths for a MetaVault.

### isMarketRegistered

```solidity
function isMarketRegistered(address metavault, address market) public view returns (bool)
```

Returns whether a market (Curve pool) is registered for a MetaVault.

### isPTRegistered

```solidity
function isPTRegistered(address metavault, address pt) public view returns (bool)
```

Returns whether a Principal Token's market is registered for a MetaVault.

### isYTRegistered

```solidity
function isYTRegistered(address metavault, address yt) public view returns (bool)
```

Returns whether a Yield Token's market is registered for a MetaVault.

### marketsCount

```solidity
function marketsCount(address metavault) external view returns (uint256)
```

Returns the number of registered markets for a MetaVault.

### market

```solidity
function market(address metavault, uint256 index) external view returns (address)
```

Returns the market address at the given index.

### allMarkets

```solidity
function allMarkets(address metavault) external view returns (address[] memory)
```

Returns all registered market addresses for a MetaVault.

### isChainRegistered

```solidity
function isChainRegistered(address metavault, uint256 chainId) external view returns (bool)
```

Returns whether a chain is registered for a MetaVault.

### isBridgePathAllowed

```solidity
function isBridgePathAllowed(
    address metavault,
    address tokenIn,
    address tokenOut,
    uint32 dstChainId,
    address bridge
) external view returns (bool)
```

Returns whether a specific bridge path is whitelisted for a MetaVault.

### getPathsCount

```solidity
function getPathsCount(address metavault) external view returns (uint256)
```

Returns the number of registered bridge paths for a MetaVault.

### chainConfig

```solidity
function chainConfig(address metavault, uint256 chainId) external view returns (MetavaultChainConfig memory)
```

Returns the chain configuration for a specific chain ID.

### allChains

```solidity
function allChains(address metavault) external view returns (MetavaultChainConfig[] memory)
```

Returns all registered chain configurations for a MetaVault.

### chainsCount

```solidity
function chainsCount(address metavault) external view returns (uint256)
```

Returns the number of registered chains for a MetaVault.

## Events

| Event                                                                                                                                | Description                           |
| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- |
| `MetavaultRegistered(address indexed metavault)`                                                                                     | A new MetaVault was registered        |
| `MetavaultUnregistered(address indexed metavault)`                                                                                   | A MetaVault was unregistered          |
| `MarketRegistered(address indexed metavault, address indexed market)`                                                                | A market was added to a MetaVault     |
| `MarketUnregistered(address indexed metavault, address indexed market)`                                                              | A market was removed from a MetaVault |
| `ChainRegistered(address indexed metavault, uint256 indexed chainId, address indexed remoteMetavaultAddress)`                        | A chain was registered                |
| `ChainUnregistered(address indexed metavault, uint256 indexed chainId, address indexed remoteMetavaultAddress)`                      | A chain was unregistered              |
| `BridgePathAllowed(address indexed metavault, address indexed tokenIn, address tokenOut, uint32 indexed dstChainId, address bridge)` | A bridge path was whitelisted         |
| `BridgePathRemoved(address indexed metavault, address indexed tokenIn, address tokenOut, uint32 indexed dstChainId, address bridge)` | A bridge path was removed             |

## Errors

| Error                                                                                                                  | Description                                     |
| ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `ZeroAddress()`                                                                                                        | An address parameter is `address(0)`            |
| `MetavaultAlreadyRegistered(address metavault)`                                                                        | MetaVault is already registered                 |
| `MetavaultNotRegistered(address metavault)`                                                                            | MetaVault is not registered                     |
| `MarketAlreadyRegistered(address metavault, address market)`                                                           | Market is already registered for this MetaVault |
| `MarketNotRegistered(address metavault, address market)`                                                               | Market is not registered for this MetaVault     |
| `ChainAlreadyRegistered(address metavault, uint256 chainId, address remoteMetavaultAddress)`                           | Chain is already registered                     |
| `ChainNotRegistered(address metavault, uint256 chainId)`                                                               | Chain is not registered                         |
| `BridgePathAlreadyRegistered(address metavault, address tokenIn, address tokenOut, uint32 dstChainId, address bridge)` | Bridge path is already registered               |
| `BridgePathNotRegistered(address metavault, address tokenIn, address tokenOut, uint32 dstChainId, address bridge)`     | Bridge path is not registered                   |
| `InvalidChainId()`                                                                                                     | Chain ID is 0                                   |
| `IndexOutOfBounds(uint256 index, uint256 length)`                                                                      | Market index out of bounds                      |
| `ChainIndexOutOfBounds(uint256 index, uint256 length)`                                                                 | Chain index out of bounds                       |
| `BridgePathIndexOutOfBounds(uint256 index, uint256 length)`                                                            | Bridge path index out of bounds                 |


# Access Control and Roles

MetaVaults use a layered access control system built on [Safe](https://safe.global/) and the [Zodiac](https://www.gnosisguild.org/) framework. This page explains how the system protects depositors by constraining what each role can do.

For more details on the Zodiac framework, see the [Gnosis Guild GitHub](https://github.com/gnosisguild), the [RolesModifier](https://github.com/gnosisguild/zodiac-modifier-roles), and the [Zodiac Roles documentation](https://docs.roles.gnosisguild.org/).

## Overview

Each MetaVault is owned by a Safe multisig. The Safe signers delegate specific scoped actions to other actors using Zodiac [RolesModifier](https://github.com/gnosisguild/zodiac-modifier-roles) and [Delay Modifier](https://github.com/gnosisguild/zodiac-modifier-delay) contracts, enabling fine-grained on-chain permission control.

## Roles

### Owner (Safe multisig)

The Safe signers are DAO-designated admins. They deploy and configure the vault, then only intervene for:

* **Role management** — assign or revoke curator, guardian, and accountant roles
* **Parameter changes** — update fees, max drawdown, treasury address
* **Urgent actions** — pause/unpause the vault
* **Timelock fast-track** — execute time-sensitive actions that would otherwise wait for the delay period

Day-to-day vault operations (liquidity allocation, settlement) are delegated to the curator and accountant roles via Zodiac.

### Curator

The Curator allocates liquidity within the MetaVault. All curator actions are **scoped** via Zodiac — they can only call specific functions on specific contracts, with parameter-level on-chain validation.

Examples of curator actions (non-exhaustive):

* Allocate liquidity to markets (e.g. Curve pools, Spectra pools, other DeFi protocols)
* Manage token approvals for registered contracts
* Bridge tokens to other chains

Some curator actions — those judged to be outside the bounds of routine day-to-day operations — are routed through [Delay Modifiers](/metavaults/architecture#delay-module), making them **timelocked**. During the delay period, Guardians can inspect and cancel any suspicious action. Routine operations that fall within safe, well-defined bounds are whitelisted to execute atomically through the default RolesModifier without delay.

### Guardian

The Guardian role is the security watchdog of the vault:

* **Monitor curator actions** — inspect timelocked transactions during the delay period and flag suspicious activity
* **Cancel timelocked actions** — call `increaseNonce` on the relevant [Delay Modifier](/metavaults/architecture#delay-module) to invalidate queued transactions before they execute
* **Report misbehaviour** — escalate to the vault admins / DAO if a curator acts against depositors' interests, which can result in the curator having their access revoked

### Accountant

The accountant role calls `settle()` on the infrastructure vault. It is responsible for performance tracking, share value calculations, and epoch management — reporting the vault's underlying value so that share prices are computed correctly. This is a deliberate **separation of duties** — the curator allocates liquidity but is not responsible for share price accounting. The accountant is typically assigned to an automated keeper or the same entity as the owner.

## Security properties

1. **Separation of concerns** — The curator allocates liquidity but cannot change vault parameters or drain funds. The accountant handles share price accounting independently — the curator has no control over settlement or epoch management.
2. **On-chain enforcement** — All permissions are enforced on-chain by the RolesModifier, not off-chain.
3. **Timelocked execution** — Sensitive curator actions pass through a delay period, giving Guardians time to review and cancel suspicious transactions.
4. **Max drawdown** — Even the accountant cannot report an underlying value below the [max drawdown](/metavaults/fee-model#max-drawdown-protection) threshold during settlement.
5. **Pausability** — The owner can pause the vault to halt all user-facing operations.
6. **Revocable access** — Curators who act against depositors' interests can have their role revoked by the Safe multisig.


# Fee Model

MetaVaults charge a **performance fee** on positive yield generated during each [epoch](/glossary#epoch). There is no management fee or deposit/withdrawal fee.

## Performance fee

The performance fee is calculated as a percentage of the profit (positive yield) between the previous epoch's saved underlying value and the new underlying value reported at settlement.

```
profit = newSavedBalance - lastSavedBalance
fees   = profit * feesInBps / 10000
```

* `feesInBps` is set at vault initialization and can be changed by the owner.
* The maximum allowed fee is **30%** (3000 bps), enforced by the `MAX_FEES` constant.
* If `newSavedBalance <= lastSavedBalance` (no profit or a loss), **no fees are charged**.

### Example

| Value                  | Amount         |
| ---------------------- | -------------- |
| `lastSavedBalance`     | 1,000,000 USDC |
| `newSavedBalance`      | 1,050,000 USDC |
| `feesInBps`            | 1000 (10%)     |
| Profit                 | 50,000 USDC    |
| Fee                    | 5,000 USDC     |
| Net balance after fees | 1,045,000 USDC |

## Fee collection during settle()

Fees are collected atomically during the `settle()` call:

1. The accountant reports `newSavedBalance` — the total vault underlying value after the epoch's strategies.
2. The contract computes the fee and subtracts it from the balance used for share pricing.
3. The fee amount is transferred from the accountant to the **treasury** address via `safeTransferFrom`.
4. Pending deposit and redeem requests are then processed using the post-fee balance.

{% hint style="info" %}
The fee is taken **before** processing pending deposits and redeems. This means depositors who requested during the epoch do not dilute the fee, and redeemers receive their share based on the post-fee net asset value.
{% endhint %}

## Treasury

The treasury is the address that receives all collected fees. It is set at vault initialization and can be updated by the vault owner via:

```solidity
function setTreasury(address _treasury) public onlyOwner
```

The treasury cannot be set to `address(0)`.

## Max drawdown protection

To protect depositors from catastrophic losses (whether from strategy failure or a compromised accountant key), the vault enforces a **max drawdown** check during settlement.

```solidity
if (newSavedBalance < lastSavedBalance * (BPS_DIVIDER - _maxDrawdown) / BPS_DIVIDER) {
    revert MaxDrawdownReached();
}
```

* The default max drawdown is **30%** (3000 bps).
* If the accountant attempts to report an underlying value that exceeds the max drawdown, the `settle()` transaction reverts.
* The max drawdown can be adjusted by the owner via `setMaxDrawdown(uint16)`.

### Example

With a max drawdown of 3000 bps (30%) and a `lastSavedBalance` of 1,000,000 USDC, the minimum acceptable `newSavedBalance` is 700,000 USDC. Reporting anything below this reverts the transaction.

## Fee parameter changes

```solidity
function setFee(uint16 newFee) external onlyOwner
```

* Cannot exceed `MAX_FEES` (3000 bps / 30%).


