# Monetary Policy

Source: [https://docs.curve.finance/developer/crvusd/monetary-policy/](https://docs.curve.finance/developer/crvusd/monetary-policy/)

MonetaryPolicy contracts are integrated into the crvUSD ecosystem, where they play a pivotal role in determining the interest rates for crvUSD markets.

`AggMonetaryPolicy.vy`

The source code for the `AggMonetaryPolicy.vy` contract can be found on [GitHub](https://github.com/curvefi/curve-stablecoin/blob/master/curve_stablecoin/mpolicies/AggMonetaryPolicy4.vy). The contract is written in [Vyper](https://vyperlang.org/) version `0.3.7`.

The contract is deployed on Ethereum at [`0xc684432fd6322c6d58b6bc5d28b18569aa0ad0a1`](https://etherscan.io/address/0xc684432fd6322c6d58b6bc5d28b18569aa0ad0a1).

**{ }Contract ABI▼**

```json
[{"name": "SetAdmin", "inputs": [{"name": "admin", "type": "address", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "AddPegKeeper", "inputs": [{"name": "peg_keeper", "type": "address", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "RemovePegKeeper", "inputs": [{"name": "peg_keeper", "type": "address", "indexed": true}], "anonymous": false, "type": "event"}, {"name": "SetRate", "inputs": [{"name": "rate", "type": "uint256", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "SetSigma", "inputs": [{"name": "sigma", "type": "uint256", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "SetTargetDebtFraction", "inputs": [{"name": "target_debt_fraction", "type": "uint256", "indexed": false}], "anonymous": false, "type": "event"}, {"stateMutability": "nonpayable", "type": "constructor", "inputs": [{"name": "admin", "type": "address"}, {"name": "price_oracle", "type": "address"}, {"name": "controller_factory", "type": "address"}, {"name": "peg_keepers", "type": "address[5]"}, {"name": "rate", "type": "uint256"}, {"name": "sigma", "type": "uint256"}, {"name": "target_debt_fraction", "type": "uint256"}], "outputs": []}, {"stateMutability": "nonpayable", "type": "function", "name": "set_admin", "inputs": [{"name": "admin", "type": "address"}], "outputs": []}, {"stateMutability": "nonpayable", "type": "function", "name": "add_peg_keeper", "inputs": [{"name": "pk", "type": "address"}], "outputs": []}, {"stateMutability": "nonpayable", "type": "function", "name": "remove_peg_keeper", "inputs": [{"name": "pk", "type": "address"}], "outputs": []}, {"stateMutability": "view", "type": "function", "name": "rate", "inputs": [], "outputs": [{"name": "", "type": "uint256"}]}, {"stateMutability": "nonpayable", "type": "function", "name": "rate_write", "inputs": [], "outputs": [{"name": "", "type": "uint256"}]}, {"stateMutability": "nonpayable", "type": "function", "name": "set_rate", "inputs": [{"name": "rate", "type": "uint256"}], "outputs": []}, {"stateMutability": "nonpayable", "type": "function", "name": "set_sigma", "inputs": [{"name": "sigma", "type": "uint256"}], "outputs": []}, {"stateMutability": "nonpayable", "type": "function", "name": "set_target_debt_fraction", "inputs": [{"name": "target_debt_fraction", "type": "uint256"}], "outputs": []}, {"stateMutability": "view", "type": "function", "name": "admin", "inputs": [], "outputs": [{"name": "", "type": "address"}]}, {"stateMutability": "view", "type": "function", "name": "rate0", "inputs": [], "outputs": [{"name": "", "type": "uint256"}]}, {"stateMutability": "view", "type": "function", "name": "sigma", "inputs": [], "outputs": [{"name": "", "type": "int256"}]}, {"stateMutability": "view", "type": "function", "name": "target_debt_fraction", "inputs": [], "outputs": [{"name": "", "type": "uint256"}]}, {"stateMutability": "view", "type": "function", "name": "peg_keepers", "inputs": [{"name": "arg0", "type": "uint256"}], "outputs": [{"name": "", "type": "address"}]}, {"stateMutability": "view", "type": "function", "name": "PRICE_ORACLE", "inputs": [], "outputs": [{"name": "", "type": "address"}]}, {"stateMutability": "view", "type": "function", "name": "CONTROLLER_FACTORY", "inputs": [], "outputs": [{"name": "", "type": "address"}]}]
```

<a id="interest-rate-mechanics"></a>

## Interest Rate Mechanics

The interest rates in crvUSD markets are not static but fluctuate based on a set of factors, including:

-   The price of crvUSD, which is determined through an aggregated oracle price from multiple Curve Stableswap pools ([details here](https://docs.curve.finance/developer/crvusd/oracles/price-aggregator.md)).
-   The variables `sigma`, `rate0`, `TargetFraction`, and the `DebtFraction` specific to PegKeepers.

tip

A useful tool to explore and understand how the rate is affected by [0xreviews](https://twitter.com/0xreviews_xyz) is avaliable at: [https://crvusd-rate.0xreviews.xyz/](https://crvusd-rate.0xreviews.xyz/)

The formula for calculating the interest rate (`r`) is as follows:

$r = rate0 \cdot e^{\text{power}}$

Where:

$\text{power} = \frac{price_{peg} - price_{crvusd}}{sigma} - \frac{DebtFraction}{TargetFraction}$

And the DebtFraction is defined by:

$DebtFraction = \frac{PegKeeperDebt}{TotalDebt}$

Key variables in this calculation include:

-   `r`: the interest rate
-   `rate0`: the baseline rate, applicable when PegKeepers are debt-free and the crvUSD price equals 1
-   `price_peg`: the target crvUSD price, set at 1.00
-   `price_crvusd`: the actual crvUSD price, aggregated from `PRICE_ORACLE.price()`
-   `DebtFraction`: the portion of PegKeeper debt relative to the total outstanding debt
-   `TargetFraction`: the designated target fraction
-   `PegKeeperDebt`: the cumulative debt of all PegKeepers
-   `TotalDebt`: the aggregate crvUSD debt

For accuracy and consistency, both `rate` and `rate0` are expressed in terms of $10^{18}$ to denote precision and are calculated per second.

The annualized interest rate can be computed as:

$\text{annualRate} = (1 + \frac{rate}{10^{18}})^{365 \times 24 \times 60 \times 60} - 1$

* * *

<a id="rate"></a>

### `rate`

`MonetaryPolicy.rate() -> uint256: view`

Getter for the rate of the monetary policy contract. This is the current interest rate paid per second.

Returns: rate (`uint256`).

**<>Source code▼**

```vyper
@view
@external
def rate() -> uint256:
    return self.calculate_rate()

@internal
@view
def calculate_rate() -> uint256:
    sigma: int256 = self.sigma
    target_debt_fraction: uint256 = self.target_debt_fraction

    p: int256 = convert(PRICE_ORACLE.price(), int256)
    pk_debt: uint256 = 0
    for pk in self.peg_keepers:
        if pk.address == empty(address):
            break
        pk_debt += pk.debt()

    power: int256 = (10**18 - p) * 10**18 / sigma  # high price -> negative pow -> low rate
    if pk_debt > 0:
        total_debt: uint256 = CONTROLLER_FACTORY.total_debt()
        if total_debt == 0:
            return 0
        else:
            power -= convert(pk_debt * 10**18 / total_debt * 10**18 / target_debt_fraction, int256)

    return self.rate0 * min(self.exp(power), MAX_EXP) / 10**18
```

**▶Example▼**

<a id="rate0"></a>

### `rate0`

`MonetaryPolicy.rate0() -> uint256: view`

Getter for the `rate0` of the monetary policy contract. `rate0` has to be less than or equal to `MAX_RATE` (400% APY).

Returns: rate0 (`uint256`).

**<>Source code▼**

```vyper
MAX_RATE: constant(uint256) = 43959106799  # 400% APY
rate0: public(uint256)

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...

    assert rate <= MAX_RATE
    self.rate0 = rate

    ...
```

**▶Example▼**

<a id="set_rate"></a>

### `set_rate`

`MonetaryPolicy.set_rate(rate: uint256)`

Guarded Method

This function is only callable by the `admin` of the contract, which is the CurveOwnershipAgent.

Function to set a new rate0. New `rate0` has to be less than or equal to `MAX_RATE (=43959106799)`.

| Input | Type | Description |
| --- | --- | --- |
| `rate` | `uint256` | New rate0 value |

Emits: `SetRate` event.

**<>Source code▼**

```vyper
event SetRate:
    rate: uint256

MAX_RATE: constant(uint256) = 43959106799  # 400% APY
rate0: public(uint256)

@external
def set_rate(rate: uint256):
    assert msg.sender == self.admin
    assert rate <= MAX_RATE
    self.rate0 = rate
    log SetRate(rate)
```

**▶Example▼**

```shell
>>> MonetaryPolicy.set_rate(3488077118)
```

<a id="sigma"></a>

### `sigma`

`MonetaryPolicy.sigma() -> int256: view`

Getter for the sigma value. The following needs to hold: $10^{14} \leq \sigma \leq 10^{18}$.

Returns: sigma (`int256`).

**<>Source code▼**

```vyper
sigma: public(int256)  # 2 * 10**16 for example

MAX_SIGMA: constant(uint256) = 10**18
MIN_SIGMA: constant(uint256) = 10**14

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...

    assert sigma >= MIN_SIGMA
    assert sigma <= MAX_SIGMA
    
    ...
```

**▶Example▼**

<a id="set_sigma"></a>

### `set_sigma`

`MonetaryPolicy.set_sigma(sigma: uint256)`

Guarded Method

This function is only callable by the `admin` of the contract, which is the CurveOwnershipAgent.

Function to set a new sigma value. New value must be inbetween `MIN_SIGMA` and `MAX_SIGMA`.

| Input | Type | Description |
| --- | --- | --- |
| `sigma` | `uint256` | New sigma value |

Emits: `SetSigma` event.

**<>Source code▼**

```vyper
event SetSigma:
    sigma: uint256

sigma: public(int256)  # 2 * 10**16 for example

MAX_SIGMA: constant(uint256) = 10**18
MIN_SIGMA: constant(uint256) = 10**14

@external
def set_sigma(sigma: uint256):
    assert msg.sender == self.admin
    assert sigma >= MIN_SIGMA
    assert sigma <= MAX_SIGMA

    self.sigma = convert(sigma, int256)
    log SetSigma(sigma)
```

**▶Example▼**

```shell
>>> MonetaryPolicy.set_sigma(30000000000000000)
```

<a id="target_debt_fraction"></a>

### `target_debt_fraction`

`MonetaryPolicy.target_debt_fraction() -> uint256: view`

Getter for the debt fraction target.

Returns: target debt fraction (`uint256`).

**<>Source code▼**

```vyper
MAX_TARGET_DEBT_FRACTION: constant(uint256) = 10**18

target_debt_fraction: public(uint256)

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...
    
    self.target_debt_fraction = target_debt_fraction
```

**▶Example▼**

<a id="set_target_debt_fraction"></a>

### `set_target_debt_fraction`

`MonetaryPolicy.set_target_debt_fraction(target_debt_fraction: uint256)`

Guarded Method

This function is only callable by the `admin` of the contract, which is the CurveOwnershipAgent.

Function to set a new value for the debt fraction target. New value needs to be less than or equal to `MAX_TARGET_DEBT_FRACTION`.

| Input | Type | Description |
| --- | --- | --- |
| `target_debt_fraction` | `uint256` | New debt fraction target value |

Emits: `SetTargetDebtFraction` event.

**<>Source code▼**

```vyper
event SetTargetDebtFraction:
    target_debt_fraction: uint256

MAX_TARGET_DEBT_FRACTION: constant(uint256) = 10**18

target_debt_fraction: public(uint256)

@external
def set_target_debt_fraction(target_debt_fraction: uint256):
    assert msg.sender == self.admin
    assert target_debt_fraction <= MAX_TARGET_DEBT_FRACTION

    self.target_debt_fraction = target_debt_fraction
    log SetTargetDebtFraction(target_debt_fraction)
```

**▶Example▼**

```shell
>>> MonetaryPolicy.set_target_debt_fraction(200000000000000000)
```

<a id="pegkeepers"></a>

## PegKeepers

PegKeepers must be added to the MonetaryPolicy contract to calculate the rate as it depends on the *DebtFraction*. They can be added by calling `add_peg_keeper` and removed via `remove_peg_keeper`.

<a id="peg_keepers"></a>

### `peg_keepers`

`MonetaryPolicy.peg_keepers(arg0: uint256) -> address: view`

Getter for the PegKeeper contract at index `arg0`.

| Input | Type | Description |
| --- | --- | --- |
| `arg0` | `uint256` | Index of the PegKeeper |

Returns: PegKeeper contracts (`address`).

**<>Source code▼**

```vyper
interface PegKeeper:
    def debt() -> uint256: view

peg_keepers: public(PegKeeper[1001])

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...

    for i in range(5):
        if peg_keepers[i].address == empty(address):
            break
        self.peg_keepers[i] = peg_keepers[i]

    ...
```

**▶Example▼**

<a id="add_peg_keeper"></a>

### `add_peg_keeper`

`MonetaryPolicy.add_peg_keeper(pk: PegKeeper)`

Guarded Method

This function is only callable by the `admin` of the contract.

Function to add an existing PegKeeper to the monetary policy contract.

| Input | Type | Description |
| --- | --- | --- |
| `pk` | `PegKeeper` | PegKeeper address to add |

Emits: `AddPegKeeper` event.

**<>Source code▼**

```vyper
event AddPegKeeper:
    peg_keeper: indexed(address)

peg_keepers: public(PegKeeper[1001])

@external
def add_peg_keeper(pk: PegKeeper):
    assert msg.sender == self.admin
    assert pk.address != empty(address)
    for i in range(1000):
        _pk: PegKeeper = self.peg_keepers[i]
        assert _pk != pk, "Already added"
        if _pk.address == empty(address):
            self.peg_keepers[i] = pk
            log AddPegKeeper(pk.address)
            break
```

**▶Example▼**

```shell
>>> MonetaryPolicy.add_peg_keeper("PegKeeper address")
```

<a id="remove_peg_keeper"></a>

### `remove_peg_keeper`

`MonetaryPolicy.remove_peg_keeper(pk: PegKeeper)`

Guarded Method

This function is only callable by the `admin` of the contract.

Function to remove an existing PegKeeper from the monetary policy contract.

| Input | Type | Description |
| --- | --- | --- |
| `pk` | `PegKeeper` | PegKeeper address to remove |

Emits: `RemovePegKeeper` event.

**<>Source code▼**

```vyper
event RemovePegKeeper:
    peg_keeper: indexed(address)

peg_keepers: public(PegKeeper[1001])

@external
def remove_peg_keeper(pk: PegKeeper):
    assert msg.sender == self.admin
    replaced_peg_keeper: uint256 = 10000
    for i in range(1001):  # 1001th element is always 0x0
        _pk: PegKeeper = self.peg_keepers[i]
        if _pk == pk:
            replaced_peg_keeper = i
            log RemovePegKeeper(pk.address)
        if _pk.address == empty(address):
            if replaced_peg_keeper < i:
                if replaced_peg_keeper < i - 1:
                    self.peg_keepers[replaced_peg_keeper] = self.peg_keepers[i - 1]
                self.peg_keepers[i - 1] = PegKeeper(empty(address))
            break
```

**▶Example▼**

```shell
>>> MonetaryPolicy.remove_peg_keeper("PegKeeper address")
```

<a id="admin-ownership"></a>

## Admin Ownership

<a id="admin"></a>

### `admin`

`MonetaryPolicy.admin() -> address: view`

Getter for the admin of the contract, which is the CurveOwnershipAgent.

Returns: admin (`address`).

**<>Source code▼**

```vyper
admin: public(address)

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    self.admin = admin

    ...
```

**▶Example▼**

<a id="set_admin"></a>

### `set_admin`

`MonetaryPolicy.set_admin(admin: address)`

Guarded Method

This function is only callable by the `admin` of the contract, which is the CurveOwnershipAgent.

Function to set a new admin.

| Input | Type | Description |
| --- | --- | --- |
| `admin` | `address` | New admin address |

Emits: `SetAdmin` event.

**<>Source code▼**

```vyper
event SetAdmin:
    admin: address

admin: public(address)

@external
def set_admin(admin: address):
    assert msg.sender == self.admin
    self.admin = admin
    log SetAdmin(admin)
```

**▶Example▼**

```shell
>>> MonetaryPolicy.set_admin("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
```

<a id="contract-info-methods"></a>

## Contract Info Methods

<a id="price_oracle"></a>

### `PRICE_ORACLE`

`MonetaryPolicy.PRICE_ORACLE() -> address: view`

Getter for the price oracle contract.

Returns: price oracle contract (`address`).

**<>Source code▼**

```vyper
PRICE_ORACLE: public(immutable(PriceOracle))

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...

    PRICE_ORACLE = price_oracle

    ...
```

**▶Example▼**

<a id="controller_factory"></a>

### `CONTROLLER_FACTORY`

`MonetaryPolicy.CONTROLLER_FACTORY() -> address: view`

Getter for the controller factory contract. immutable variable!

Returns: controller factory contract (`address`).

**<>Source code▼**

```vyper
CONTROLLER_FACTORY: public(immutable(ControllerFactory))

@external
def __init__(admin: address,
            price_oracle: PriceOracle,
            controller_factory: ControllerFactory,
            peg_keepers: PegKeeper[5],
            rate: uint256,
            sigma: uint256,
            target_debt_fraction: uint256):
    ...

    CONTROLLER_FACTORY = controller_factory

    ...
```

**▶Example▼**

<a id="rate_write"></a>

### `rate_write`

`MonetaryPolicy.rate_write() -> uint256`

When adding a new market via the factory contract, `rate_write` is called to check if the MonetaryPolicy contract has the correct ABI.

Returns: the current rate (`uint256`).

**<>Source code▼**

```vyper
@external
def rate_write() -> uint256:
    # Not needed here but useful for more automated policies
    # which change rate0 - for example rate0 targeting some fraction pl_debt/total_debt
    return self.calculate_rate()

@internal
@view
def calculate_rate() -> uint256:
    sigma: int256 = self.sigma
    target_debt_fraction: uint256 = self.target_debt_fraction

    p: int256 = convert(PRICE_ORACLE.price(), int256)
    pk_debt: uint256 = 0
    for pk in self.peg_keepers:
        if pk.address == empty(address):
            break
        pk_debt += pk.debt()

    power: int256 = (10**18 - p) * 10**18 / sigma  # high price -> negative pow -> low rate
    if pk_debt > 0:
        total_debt: uint256 = CONTROLLER_FACTORY.total_debt()
        if total_debt == 0:
            return 0
        else:
            power -= convert(pk_debt * 10**18 / total_debt * 10**18 / target_debt_fraction, int256)

    return self.rate0 * min(self.exp(power), MAX_EXP) / 10**18
```

**▶Example▼**

```shell
>>> MonetaryPolicy.rate_write()
3488503937
```
