Skip to main content

AggMonetaryPolicy (v4)

The AggMonetaryPolicy4 contract is the latest version of the aggregated monetary policy for crvUSD markets. It builds on the original AggMonetaryPolicy with several key improvements: EMA-smoothed PegKeeper debt ratios (reducing manipulation risk), debt candles (using the minimum debt over half-day periods for more stable rate calculations), and per-market rate adjustments (scaling rates based on how close a market is to its debt ceiling).

AggMonetaryPolicy4.vy

The source code for the AggMonetaryPolicy4.vy contract can be found on GitHub. The contract is written in Vyper version 0.4.3.

The contract is deployed on Ethereum at 0x07491D124ddB3Ef59a8938fCB3EE50F9FA0b9251.

{ }Contract ABI
[{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"peg_keeper","type":"address"}],"name":"AddPegKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"peg_keeper","type":"address"}],"name":"RemovePegKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"rate","type":"uint256"}],"name":"SetRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sigma","type":"int256"}],"name":"SetSigma","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"target_debt_fraction","type":"uint256"}],"name":"SetTargetDebtFraction","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"extra_const","type":"uint256"}],"name":"SetExtraConst","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"debt_ratio_ema_time","type":"uint256"}],"name":"SetDebtRatioEmaTime","type":"event"},{"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":"int256"},{"name":"target_debt_fraction","type":"uint256"},{"name":"extra_const","type":"uint256"},{"name":"_debt_ratio_ema_time","type":"uint256"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"name":"admin","type":"address"}],"name":"set_admin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"pk","type":"address"}],"name":"add_peg_keeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"pk","type":"address"}],"name":"remove_peg_keeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rate","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_for","type":"address"}],"name":"rate","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rate_write","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_for","type":"address"}],"name":"rate_write","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"rate","type":"uint256"}],"name":"set_rate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"sigma","type":"int256"}],"name":"set_sigma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"target_debt_fraction","type":"uint256"}],"name":"set_target_debt_fraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"extra_const","type":"uint256"}],"name":"set_extra_const","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_debt_ratio_ema_time","type":"uint256"}],"name":"set_debt_ratio_ema_time","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"debt_ratio_ema_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rate0","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sigma","outputs":[{"name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"target_debt_fraction","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"extra_const","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"peg_keepers","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_ORACLE","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONTROLLER_FACTORY","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"n_controllers","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"controllers","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"min_debt_candles","outputs":[{"name":"candle0","type":"uint256"},{"name":"candle1","type":"uint256"},{"name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"}]

Interest Rate Mechanics

This version of the monetary policy uses the same core formula as the original but adds several refinements for more robust and market-aware rate calculation.

Base Rate Formula

The base interest rate is computed as:

r=rate0epower+extra_constr = rate0 \cdot e^{\text{power}} + extra\_const

info

extra_const is currently set to 0 and is not actively used. The effective formula simplifies to r=rate0epowerr = rate0 \cdot e^{\text{power}}.

Where:

power=price_pegprice_crvusdσEMA(DebtRatio)TargetFraction\text{power} = \frac{price\_peg - price\_crvusd}{\sigma} - \frac{EMA(DebtRatio)}{TargetFraction}

Key variables:

  • rate0rate0: the baseline rate when PegKeepers are debt-free and crvUSD price equals 1
  • price_pegprice\_peg: target crvUSD price (1.00)
  • price_crvusdprice\_crvusd: actual crvUSD price from PRICE_ORACLE.price()
  • σ\sigma: sensitivity parameter controlling how much price deviations affect the rate (see intuition behind sigma)
  • EMA(DebtRatio)EMA(DebtRatio): exponentially smoothed PegKeeper debt ratio (see below)
  • TargetFractionTargetFraction: target debt fraction for PegKeepers
  • extra_constextra\_const: an additional constant added to the rate

EMA-Smoothed Debt Ratio

Instead of using the raw PegKeeper debt fraction directly, this version smooths it with an exponential moving average. The EMA uses a queueing mechanism where the value from the previous update is used as input, and new values are queued for the next update. This reduces manipulation risk—a flash loan attacker would have to sustain their position beyond a single transaction to impact the EMA.

Per-Market Rate Adjustment

After computing the base rate, it is further adjusted based on how close an individual market's debt is to its debt ceiling:

rate_adjusted=rate(1target)+target11f1rate\_adjusted = rate \cdot \frac{(1 - target) + target \cdot \frac{1}{1 - f}}{1}

Where f=debt_forceilingf = \frac{debt\_for}{ceiling} and target=0.1target = 0.1 (TARGET_REMAINDER). This means:

  • At 0% utilization: rate stays the same (1.0×rate1.0 \times rate)
  • At 90% of ceiling: rate doubles (1.9×rate1.9 \times rate)
  • At 100% of ceiling: rate is capped at MAX_RATE (300% APY)

Debt Candles

The contract tracks minimum debt over half-day periods using a candle-like structure. Instead of using the current (potentially manipulated) debt values, calculate_rate uses the minimum observed debt from recent candle periods, providing more stable rate calculations.

Annualized Rate

Both rate and rate0 are expressed per second with 101810^{18} precision. The annualized rate is:

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


Interest Rates

rate

AggMonetaryPolicy4.rate(_for: address = msg.sender) -> uint256: view

Getter for the current interest rate paid per second for a given controller. If no _for address is provided, it defaults to msg.sender. The rate is calculated using EMA-smoothed PegKeeper debt ratios and adjusted based on the controller's debt ceiling utilization.

InputTypeDescription
_foraddressController address to calculate rate for (optional, defaults to msg.sender)

Returns: rate (uint256).

<>Source code
@view
@external
def rate(_for: address = msg.sender) -> uint256:
rate: uint256 = 0
_: uint256 = 0
rate, _ = self.calculate_rate(_for, staticcall PRICE_ORACLE.price(), True)
return rate

@internal
@view
def calculate_rate(_for: address, _price: uint256, ro: bool) -> (uint256, uint256):
sigma: int256 = self.sigma
target_debt_fraction: uint256 = self.target_debt_fraction

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

total_debt: uint256 = 0
debt_for: uint256 = 0
total_debt, debt_for = self.read_debt(_for, ro)

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

# Rate accounting for crvUSD price and PegKeeper debt
rate: uint256 = self.rate0 * min(self.exp(power), MAX_EXP) // 10**18 + self.extra_const

# Account for individual debt ceiling to dynamically tune rate depending on filling the market
ceiling: uint256 = staticcall CONTROLLER_FACTORY.debt_ceiling(_for)
if ceiling > 0:
f: uint256 = min(debt_for * 10**18 // ceiling, 10**18 - TARGET_REMAINDER // 1000)
rate = min(rate * ((10**18 - TARGET_REMAINDER) + TARGET_REMAINDER * 10**18 // (10**18 - f)) // 10**18, MAX_RATE)

return rate, ratio
Example

rate_write

AggMonetaryPolicy4.rate_write(_for: address = msg.sender) -> uint256

State-changing version of rate that also updates the controller cache, debt candles, and the EMA of the PegKeeper debt ratio. This function is called by controllers when they need the current rate and want to ensure state is updated.

InputTypeDescription
_foraddressController address to calculate rate for (optional, defaults to msg.sender)

Returns: the current rate (uint256).

<>Source code
@external
def rate_write(_for: address = msg.sender) -> uint256:
# Update controller list
n_controllers: uint256 = self.n_controllers
n_factory_controllers: uint256 = staticcall CONTROLLER_FACTORY.n_collaterals()
if n_factory_controllers > n_controllers:
self.n_controllers = n_factory_controllers
for i: uint256 in range(MAX_CONTROLLERS):
self.controllers[n_controllers] = staticcall CONTROLLER_FACTORY.controllers(n_controllers)
n_controllers += 1
if n_controllers >= n_factory_controllers:
break

# Update candles
total_debt: uint256 = 0
debt_for: uint256 = 0
total_debt, debt_for = self.get_total_debt(_for)
self.save_candle(empty(address), total_debt)
self.save_candle(_for, debt_for)

rate: uint256 = 0
ratio: uint256 = 0
rate, ratio = self.calculate_rate(_for, extcall PRICE_ORACLE.price_w(), False)
ema.update(DEBT_RATIO_EMA_ID, ratio)
return rate
Example
>>> AggMonetaryPolicy4.rate_write()
3488503937

rate0

AggMonetaryPolicy4.rate0() -> uint256: view

Getter for the rate0 baseline rate. rate0 must be less than or equal to MAX_RATE (300% APY = 43959106799).

Returns: rate0 (uint256).

<>Source code
MAX_RATE: constant(uint256) = 43959106799  # 300% APY

rate0: public(uint256)

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

assert rate <= MAX_RATE
self.rate0 = rate

...
Example

set_rate

AggMonetaryPolicy4.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 value must be less than or equal to MAX_RATE (43959106799, i.e. 300% APY).

InputTypeDescription
rateuint256New rate0 value

Emits: SetRate event.

<>Source code
event SetRate:
rate: uint256

MAX_RATE: constant(uint256) = 43959106799 # 300% 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=rate)
Example
>>> AggMonetaryPolicy4.set_rate(3488077118)

Rate Parameters

sigma

AggMonetaryPolicy4.sigma() -> int256: view

Getter for the sigma value, which controls how sensitive the interest rate is to crvUSD price deviations. Must satisfy: 1014σ101810^{14} \leq \sigma \leq 10^{18}.

Returns: sigma (int256).

<>Source code
sigma: public(int256)  # 2 * 10**16 for example

MAX_SIGMA: constant(int256) = 10**18
MIN_SIGMA: constant(int256) = 10**14
Example

set_sigma

AggMonetaryPolicy4.set_sigma(sigma: int256)
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 between MIN_SIGMA (101410^{14}) and MAX_SIGMA (101810^{18}).

InputTypeDescription
sigmaint256New sigma value

Emits: SetSigma event.

<>Source code
event SetSigma:
sigma: int256

sigma: public(int256)

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

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

self.sigma = sigma
log SetSigma(sigma=sigma)
Example
>>> AggMonetaryPolicy4.set_sigma(30000000000000000)

target_debt_fraction

AggMonetaryPolicy4.target_debt_fraction() -> uint256: view

Getter for the target PegKeeper debt fraction. This is the desired ratio of PegKeeper debt to total debt.

Returns: target debt fraction (uint256).

<>Source code
MAX_TARGET_DEBT_FRACTION: constant(uint256) = 10**18

target_debt_fraction: public(uint256)
Example

set_target_debt_fraction

AggMonetaryPolicy4.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 target debt fraction. Must be greater than 0 and less than or equal to MAX_TARGET_DEBT_FRACTION (101810^{18}).

InputTypeDescription
target_debt_fractionuint256New target debt fraction value

Emits: SetTargetDebtFraction event.

<>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
assert target_debt_fraction > 0

self.target_debt_fraction = target_debt_fraction
log SetTargetDebtFraction(target_debt_fraction=target_debt_fraction)
Example
>>> AggMonetaryPolicy4.set_target_debt_fraction(200000000000000000)

extra_const

AggMonetaryPolicy4.extra_const() -> uint256: view

Getter for the extra_const value, an additional constant that is added to the computed rate. This allows setting a minimum floor rate independent of the exponential formula. Must be less than or equal to MAX_EXTRA_CONST (which equals MAX_RATE). Currently set to 0 and not actively used.

Returns: extra_const (uint256).

<>Source code
MAX_EXTRA_CONST: constant(uint256) = MAX_RATE

extra_const: public(uint256)
Example

set_extra_const

AggMonetaryPolicy4.set_extra_const(extra_const: uint256)
Guarded Method

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

Function to set a new extra_const value. Must be less than or equal to MAX_EXTRA_CONST.

InputTypeDescription
extra_constuint256New extra_const value

Emits: SetExtraConst event.

<>Source code
event SetExtraConst:
extra_const: uint256

MAX_EXTRA_CONST: constant(uint256) = MAX_RATE

extra_const: public(uint256)

@external
def set_extra_const(extra_const: uint256):
assert msg.sender == self.admin
assert extra_const <= MAX_EXTRA_CONST

self.extra_const = extra_const
log SetExtraConst(extra_const=extra_const)
Example
>>> AggMonetaryPolicy4.set_extra_const(1000000000)

debt_ratio_ema_time

AggMonetaryPolicy4.debt_ratio_ema_time() -> uint256: view

Getter for the EMA smoothing time (in seconds) used for the PegKeeper debt ratio. A longer EMA time means the debt ratio changes more slowly, providing more stability but less responsiveness.

Returns: EMA time in seconds (uint256).

<>Source code
DEBT_RATIO_EMA_ID: constant(String[4]) = "pkr"

@external
@view
def debt_ratio_ema_time() -> uint256:
return ema._emas[DEBT_RATIO_EMA_ID].ema_time
Example

set_debt_ratio_ema_time

AggMonetaryPolicy4.set_debt_ratio_ema_time(_debt_ratio_ema_time: uint256)
Guarded Method

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

Function to set a new EMA smoothing time for the PegKeeper debt ratio. Before updating, it first computes the current EMA value to avoid discontinuities. The new value must be greater than 0.

InputTypeDescription
_debt_ratio_ema_timeuint256New EMA time in seconds

Emits: SetDebtRatioEmaTime event.

<>Source code
event SetDebtRatioEmaTime:
debt_ratio_ema_time: uint256

@external
def set_debt_ratio_ema_time(_debt_ratio_ema_time: uint256):
assert msg.sender == self.admin

ema.set_ema_time(DEBT_RATIO_EMA_ID, _debt_ratio_ema_time)
log SetDebtRatioEmaTime(debt_ratio_ema_time=_debt_ratio_ema_time)
Example
>>> AggMonetaryPolicy4.set_debt_ratio_ema_time(86400)

PegKeepers

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

peg_keepers

AggMonetaryPolicy4.peg_keepers(arg0: uint256) -> address: view

Getter for the PegKeeper contract at index arg0.

InputTypeDescription
arg0uint256Index of the PegKeeper

Returns: PegKeeper contract (address).

<>Source code
interface PegKeeper:
def debt() -> uint256: view

peg_keepers: public(PegKeeper[1001])
Example

add_peg_keeper

AggMonetaryPolicy4.add_peg_keeper(pk: address)
Guarded Method

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

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

InputTypeDescription
pkaddressPegKeeper address to add

Emits: AddPegKeeper event.

<>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: uint256 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(peg_keeper=pk.address)
break
Example
>>> AggMonetaryPolicy4.add_peg_keeper("0x1234567890abcdef1234567890abcdef12345678")

remove_peg_keeper

AggMonetaryPolicy4.remove_peg_keeper(pk: address)
Guarded Method

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

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

InputTypeDescription
pkaddressPegKeeper address to remove

Emits: RemovePegKeeper event.

<>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: uint256 in range(1001): # 1001th element is always 0x0
_pk: PegKeeper = self.peg_keepers[i]
if _pk == pk:
replaced_peg_keeper = i
log RemovePegKeeper(peg_keeper=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
>>> AggMonetaryPolicy4.remove_peg_keeper("0x1234567890abcdef1234567890abcdef12345678")

Admin Ownership

admin

AggMonetaryPolicy4.admin() -> address: view

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

Returns: admin (address).

<>Source code
admin: public(address)

@deploy
def __init__(admin: address, ...):
self.admin = admin
...
Example

set_admin

AggMonetaryPolicy4.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.

InputTypeDescription
adminaddressNew admin address

Emits: SetAdmin event.

<>Source code
event SetAdmin:
admin: address

admin: public(address)

@external
def set_admin(admin: address):
assert msg.sender == self.admin
self.admin = admin
log SetAdmin(admin=admin)
Example
>>> AggMonetaryPolicy4.set_admin("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")

Contract Info Methods

PRICE_ORACLE

AggMonetaryPolicy4.PRICE_ORACLE() -> address: view

Getter for the price oracle contract. Immutable variable set at deployment.

Returns: price oracle contract (address).

<>Source code
PRICE_ORACLE: public(immutable(PriceOracle))

@deploy
def __init__(admin: address,
price_oracle: PriceOracle,
controller_factory: ControllerFactory, ...):
...
PRICE_ORACLE = price_oracle
...
Example

CONTROLLER_FACTORY

AggMonetaryPolicy4.CONTROLLER_FACTORY() -> address: view

Getter for the controller factory contract. Immutable variable set at deployment.

Returns: controller factory contract (address).

<>Source code
CONTROLLER_FACTORY: public(immutable(ControllerFactory))

@deploy
def __init__(admin: address,
price_oracle: PriceOracle,
controller_factory: ControllerFactory, ...):
...
CONTROLLER_FACTORY = controller_factory
...
Example

n_controllers

AggMonetaryPolicy4.n_controllers() -> uint256: view

Getter for the number of controllers cached in this contract. The controller list is automatically updated when rate_write is called and the factory has added new controllers.

Returns: number of cached controllers (uint256).

<>Source code
MAX_CONTROLLERS: constant(uint256) = 50000
n_controllers: public(uint256)
controllers: public(address[MAX_CONTROLLERS])
Example

controllers

AggMonetaryPolicy4.controllers(arg0: uint256) -> address: view

Getter for the cached controller address at index arg0.

InputTypeDescription
arg0uint256Index of the controller

Returns: controller address (address).

<>Source code
MAX_CONTROLLERS: constant(uint256) = 50000
controllers: public(address[MAX_CONTROLLERS])
Example

min_debt_candles

AggMonetaryPolicy4.min_debt_candles(arg0: address) -> (uint256, uint256, uint256): view

Getter for the debt candle data for a given controller address. Returns a struct with:

  • candle0: the earlier half-day candle (minimum debt observed)
  • candle1: the later half-day candle (minimum debt observed)
  • timestamp: the last update timestamp

The zero address (0x0000...) stores the total debt candle across all controllers.

InputTypeDescription
arg0addressController address (or zero for total)

Returns: candle0 (uint256), candle1 (uint256), timestamp (uint256).

<>Source code
struct DebtCandle:
candle0: uint256 # earlier 1/2 day candle
candle1: uint256 # later 1/2 day candle
timestamp: uint256

DEBT_CANDLE_TIME: constant(uint256) = 86400 // 2

min_debt_candles: public(HashMap[address, DebtCandle])

@internal
@view
def read_candle(_for: address) -> uint256:
out: uint256 = 0
candle: DebtCandle = self.min_debt_candles[_for]

if block.timestamp < candle.timestamp // DEBT_CANDLE_TIME * DEBT_CANDLE_TIME + DEBT_CANDLE_TIME:
if candle.candle0 > 0:
out = min(candle.candle0, candle.candle1)
else:
out = candle.candle1
elif block.timestamp < candle.timestamp // DEBT_CANDLE_TIME * DEBT_CANDLE_TIME + DEBT_CANDLE_TIME * 2:
out = candle.candle1

return out

@internal
def save_candle(_for: address, _value: uint256):
candle: DebtCandle = self.min_debt_candles[_for]

if candle.timestamp == 0 and _value == 0:
return

if block.timestamp >= candle.timestamp // DEBT_CANDLE_TIME * DEBT_CANDLE_TIME + DEBT_CANDLE_TIME:
if block.timestamp < candle.timestamp // DEBT_CANDLE_TIME * DEBT_CANDLE_TIME + DEBT_CANDLE_TIME * 2:
candle.candle0 = candle.candle1
candle.candle1 = _value
else:
candle.candle0 = _value
candle.candle1 = _value
else:
candle.candle1 = min(candle.candle1, _value)

candle.timestamp = block.timestamp
self.min_debt_candles[_for] = candle
Example