# PriceAndFeeCalculator

**Inherits:** IPriceAndFeeCalculator, BaseFeeCalculator, HasNumeraire

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

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

## State Variables

### ORACLE\_REGISTRY

Oracle registry contract for price feeds

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

### \_vaultPriceStates

Mapping of vault addresses to their state information

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

## Functions

### requiresVaultAuthOrAccountant

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

### constructor

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

### registerVault

Register a new vault with the fee calculator

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

### setInitialPrice

Set the initial price state for the vault

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

**Parameters**

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

### setThresholds

Set vault thresholds

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

**Parameters**

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

### setUnitPrice

Set the unit price for the vault in numeraire terms

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

**Parameters**

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

### pauseVault

Pause the vault

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

**Parameters**

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

### unpauseVault

Unpause the vault

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

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

**Parameters**

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

### resetHighestPrice

Resets the highest price for a vault to the current price

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

**Parameters**

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

### convertUnitsToToken

Convert units to token amount

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

**Parameters**

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

**Returns**

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

### convertUnitsToTokenIfActive

Convert units to token amount if vault is not paused

*MUST revert if vault is paused*

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

**Parameters**

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

**Returns**

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

### convertUnitsToNumeraire

Convert units to numeraire token amount

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

**Parameters**

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

**Returns**

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

### convertTokenToUnits

Convert token amount to units

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

**Parameters**

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

**Returns**

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

### convertTokenToUnitsIfActive

Convert token amount to units if vault is not paused

*MUST revert if vault is paused*

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

**Parameters**

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

**Returns**

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

### getVaultState

Return the state of the vault

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

**Parameters**

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

**Returns**

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

### getVaultsPriceAge

Returns the age of the last submitted price for a vault

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

**Parameters**

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

**Returns**

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

### isVaultPaused

Check if a vault is paused

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

**Parameters**

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

**Returns**

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

### previewFees

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

### \_accrueFees

Accrues fees for a vault

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

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

**Parameters**

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

### \_setVaultPaused

Sets the paused state for a vault

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

**Parameters**

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

### \_convertTokenToUnits

Converts a token amount to units

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

**Parameters**

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

**Returns**

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

### \_convertUnitsToToken

Converts a units amount to tokens

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

**Parameters**

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

**Returns**

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

### \_validatePriceUpdate

Validates a price update

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

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

**Parameters**

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

### \_shouldPause

Determines if a price update should pause the vault

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

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

**Parameters**

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

**Returns**

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