Monetary Policy
MonetaryPolicy contracts are integrated into the crvUSD ecosystem, where they play a pivotal role in determining the interest rates for crvUSD markets.
Contract Source & Deployment
Source code is available on Github. Relevant contract deployments can be found here.
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).
 - The variables 
sigma,rate0,TargetFraction, and theDebtFractionspecific to PegKeepers. 
Tip
A useful tool to explore and understand how the rate is affected by 0xreviews is avaliable at: https://crvusd-rate.0xreviews.xyz/
The formula for calculating the interest rate (r) is as follows:
Where:
And the DebtFraction is defined by:
Key variables in this calculation include:
r: the interest raterate0: the baseline rate, applicable when PegKeepers are debt-free and the crvUSD price equals 1price_peg: the target crvUSD price, set at 1.00price_crvusd: the actual crvUSD price, aggregated fromPRICE_ORACLE.price()DebtFraction: the portion of PegKeeper debt relative to the total outstanding debtTargetFraction: the designated target fractionPegKeeperDebt: the cumulative debt of all PegKeepersTotalDebt: 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:
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
@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
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
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
    ...
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).
Emits: SetRate
| Input | Type | Description | 
|---|---|---|
rate |  uint256 |  New rate0 value | 
Source code
sigma¶
 MonetaryPolicy.sigma() -> uint256: view
Getter for the sigma value. The following needs to hold: \(10^{14} <= sigma <= 10^{18}\).
Returns: sigma (uint256).
Source code
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
    ...
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.
Emits: SetSigma
| Input | Type | Description | 
|---|---|---|
sigma |  uint256 |  New sigma value | 
Source code
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)
target_debt_fraction¶
 MonetaryPolicy.target_debt_fraction() -> uint256: view
Getter for the debt fraction target.
Returns: target debt fraction (uint256).
Source code
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
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.
Emits: SetTargetDebtFraction
| Input | Type | Description | 
|---|---|---|
target_debt_fraction |  uint256 |  New debt fraction target value | 
Source code
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)
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.
peg_keepers¶
 MonetaryPolicy.peg_keepers(arg0: uint256) -> address: view
Getter for the PegKeeper contract at index arg0.
Returns: PegKeeper contracts (address).
| Input | Type | Description | 
|---|---|---|
arg0 |  uint256 |  Index of the PegKeeper | 
Source code
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]
    ...
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.
Emits: AddPegKeeper
| Input | Type | Description | 
|---|---|---|
pk |  PegKeeper |  PegKeeper address to add | 
Source code
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
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.
Emits: RemovePegKeeper
| Input | Type | Description | 
|---|---|---|
pk |  PegKeeper |  PegKeeper address to remove | 
Source code
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
Admin Ownership¶
admin¶
 MonetaryPolicy.admin() -> address: view
Getter for the admin of the contract, which is the CurveOwnershipAgent.
Returns: admin (address).
Source code
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.
Emits: SetAdmin
| Input | Type | Description | 
|---|---|---|
admin |  address |  New admin address | 
Source code
Contract Info Methods¶
PRICE_ORACLE¶
 MonetaryPolicy.PRICE_ORACLE() -> address: view
Getter for the price oracle contract.
Returns: price oracle contract (address).
Source code
CONTROLLER_FACOTRY¶
 MonetaryPolicy.CONTROLLER_FACOTRY() -> address: view
Getter for the controller factory contract. immutable variable!
Returns: controller factory contract (address).
Source code
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.
Source code
@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