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.vyThe 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:
extra_const is currently set to 0 and is not actively used. The effective formula simplifies to .
Where:
Key variables:
- : the baseline rate when PegKeepers are debt-free and crvUSD price equals 1
- : target crvUSD price (1.00)
- : actual crvUSD price from
PRICE_ORACLE.price() - : sensitivity parameter controlling how much price deviations affect the rate (see intuition behind sigma)
- : exponentially smoothed PegKeeper debt ratio (see below)
- : target debt fraction for PegKeepers
- : 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:
Where and (TARGET_REMAINDER). This means:
- At 0% utilization: rate stays the same ()
- At 90% of ceiling: rate doubles ()
- 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 precision. The annualized rate is:
Interest Rates
rate
AggMonetaryPolicy4.rate(_for: address = msg.sender) -> uint256: viewGetter 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.
| Input | Type | Description |
|---|---|---|
_for | address | Controller 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) -> uint256State-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.
| Input | Type | Description |
|---|---|---|
_for | address | Controller 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: viewGetter 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)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).
| Input | Type | Description |
|---|---|---|
rate | uint256 | New 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: viewGetter for the sigma value, which controls how sensitive the interest rate is to crvUSD price deviations. Must satisfy: .
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)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 () and MAX_SIGMA ().
| Input | Type | Description |
|---|---|---|
sigma | int256 | New 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: viewGetter 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)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 ().
| Input | Type | Description |
|---|---|---|
target_debt_fraction | uint256 | New 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: viewGetter 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)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.
| Input | Type | Description |
|---|---|---|
extra_const | uint256 | New 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: viewGetter 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▼
- AggMonetaryPolicy4.vy
- ema.vy (curve_std)
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
struct EMA:
ema_time: uint256
prev_value: uint256
prev_timestamp: uint256
queued_value: uint256
_emas: HashMap[String[4], EMA]
▶Example▼
set_debt_ratio_ema_time
AggMonetaryPolicy4.set_debt_ratio_ema_time(_debt_ratio_ema_time: uint256)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.
| Input | Type | Description |
|---|---|---|
_debt_ratio_ema_time | uint256 | New EMA time in seconds |
Emits: SetDebtRatioEmaTime event.
<>Source code▼
- AggMonetaryPolicy4.vy
- ema.vy (curve_std)
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)
@internal
def set_ema_time(_ema_id: String[4], _ema_time: uint256):
assert self._is_allowed(_ema_id)
self.update(_ema_id, self._emas[_ema_id].queued_value)
assert _ema_time > 0
ema: EMA = self._emas[_ema_id]
ema.ema_time = _ema_time
self._emas[_ema_id] = ema
▶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: viewGetter for the PegKeeper contract at index arg0.
| Input | Type | Description |
|---|---|---|
arg0 | uint256 | Index 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)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.
| Input | Type | Description |
|---|---|---|
pk | address | PegKeeper 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)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.
| Input | Type | Description |
|---|---|---|
pk | address | PegKeeper 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: viewGetter 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)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▼
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: viewGetter 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: viewGetter 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: viewGetter 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: viewGetter for the cached controller address at index arg0.
| Input | Type | Description |
|---|---|---|
arg0 | uint256 | Index 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): viewGetter 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.
| Input | Type | Description |
|---|---|---|
arg0 | address | Controller 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