title | description | parent | tags | slug | contentType | root | ||||
---|---|---|---|---|---|---|---|---|---|---|
Monitoring Collateral Types and Vaults |
Learn how to monitor Maker Protocol State |
vaults |
|
monitoring-collateral-types-and-vaults |
guides |
false |
Level: Intermediate
Estimated Time: 45 minutes
Monitoring part of the Maker Protocol state is an important part of servicing users of the protocol, such as Vault owners. Proactive monitoring is recommended and has the potential to save Vault owners from liquidation and high stability fees. This guide will highlight the locations of relevant data as implemented in smart contract Solidity code, so it is up to the reader to choose which API is used to access that data. Some notable APIs are Dai.js and Pymaker; more generalized Web3 libraries can be used, though without the valuable utility functions that are exposed in the former two APIs. Moreover, because they abstract away much of the complexity of data transformation, we strongly recommend trying to work with Dai.js or Pymaker before attempting to read the state directly.
After going through this guide, you will gain a better understanding of:
- How to monitor the state and health of a particular Vault
- How to monitor relevant risk parameters
Maker Protocol 101, especially:
- Vault basics.
- Governance Risk parameters.
Mainnet addresses to contracts mentioned below can be found in the latest release of the Maker Protocol. To see the contract solidity code, go to etherscan.io, click on the Contract
tab, and finally select the Code
card. When reading numeric values, remember to account for their magnitudes. Of the fixed point integers:
wad
- 18 decimal placesray
- 27 decimal placesrad
- 45 decimal places
Every Collateral type state is stored in the Ilk
data structure in the Vat, the central accounting contract of the Maker Protocol.
struct Ilk {
uint256 Art; // Total Normalised Debt [wad]
uint256 rate; // Accumulated Rates [ray]
uint256 spot; // Price with Safety Margin [ray]
uint256 line; // Debt Ceiling [rad]
uint256 dust; // Urn Debt Floor [rad]
}
The state of a particular Ilk
can be found through the ilks
mapping:
mapping (bytes32 => Ilk) public ilks;
On the mapping, the first argument is a bytes32
representation of the collateral type.
Once you can read the Ilk
struct in the Vat, you have access to most of its risk parameters. The other important risk parameters, such as the stability fee, liquidation penalty, and liquidation ratio, can be found in similar Ilk
structs in the Jug, Cat, and Spot contracts, respectively. Similar to the Vat, each contract has their own ilks
mapping.
In the Jug:
struct Ilk {
uint256 duty; // Collateral-specific, per-second stability fee contribution [ray]
uint256 rho; // Time of last drip [unix epoch time]
}
In the Cat:
struct Ilk {
address flip; // Liquidator
uint256 chop; // Liquidation Penalty [ray]
uint256 lump; // Liquidation Quantity [wad]
}
In the Spot:
struct Ilk {
PipLike pip; // Price Feed
uint256 mat; // Liquidation ratio [ray]
}
Here's a non-exhaustive example of reading common risk parameters of a collateral type within the Maker Protocol. Data location is shown in pseudocode and follows this format: Contract.function(...).variable
.
Collateral Type = Ilk = bytes32(`ETH-A`) = 0x4554482d41000000000000000000000000000000000000000000000000000000
The Maker Protocol pulls recent price data from the Oracle Security Module (OSM) to properly value the assets it accepts as collateral. The sole purpose of the OSM is to delay price feed updates and protect the system from oracle attacks. Thus, the price used to evaluate a Vault’s state is delayed by a predetermined amount of time (e.g. 1 hour). Moreover, as was designed, the Liquidation Ratio, which is the minimum Vault Collateralization Ratio before liquidation, is baked into the price and stored in the Vat. As a result, the Collateral Price is delayed, has a “safety margin” that’s in size to the Liquidation Ratio, and is used directly when evaluating a Vault’s health.
Delayed Collateral Price price w/ safety margin = Vat.ilks(Ilk).spot = 150 x 10^27 ($150)
Liquidation Ratio = Spot.ilks(Ilk).mat = 1.50 x 10^27 (150%)
Delayed Collateral Price = Delayed Collateral Price w/ safety margin * Liquidation Ratio = 150 * 1.5 = 225 ETH / USD
Liquidation Penalty = Cat.ilks(Ilk).chop = 1.13 * 10^27 = 13%
All collateral types are charged a stability fee, which is the combination of two fee contributions: a collateral-specific fee and a global fee. The combination of the two results in a “debt multiplier” that when multiplied by a Vault’s normalized internal Dai (urn.art
) gives the total amount of debt at any time. Each collateral type has a debt multiplier (ilk.rate
) that continuously, but irregularly, updates in the Vat. This is one of the more important, fundamental mechanisms within the Maker Protocol, so if you have time, we recommend reading more on the Rates Module.
Global, per-second stability fee contribution = Jug.base = 0.0000 x 10^27
Collateral-specific, per-second stability fee contribution = Jug.ilks(Ilk).duty = 1.000000001847694957439350562 x 10^27
Although the total stability fee is the sum of the global and collateral specific fee, the latter (known as "duty") is currently the only contributing factor to the fee, as the global fee is set to 0.
Stability Fee per second = Global + Collateral-specific, per second stability fee contributions = 0.0000 x 10^27 + 1.000000001847694957439350562 x 10^27
Stability Fee per year = Stability Fee per second ^ 31536000 seconds in a year (365 days x 24 hours x 60 minutes x 60 seconds) = 1.06 or 6% per year
Similar to the Ilk, every Vault state is stored in the Urn
data structure in the Vat.
struct Urn {
uint256 ink; // Locked Collateral [wad]
uint256 art; // Normalised Debt [wad]
}
Acting as an alias to Vault state, a particular Urn
state can be found through the urns
mapping:
mapping (bytes32 => mapping (address => Urn )) public urns;
On the mapping, the first argument is a bytes32
representation of the collateral type, while the second argument is the user's Ethereum address.
You may notice that an Ethereum address only has access to a single Urn
for each Ilk
Collateral type. The CDP-Manager exists to circumvent this constraint. The CDP-Manager manages a list of UrnHandlers
, which is a simple contract that has a single goal of being owned by an Ethereum address and holds ownership of an Urn
. In other words, with the CDP-Manager, one could own multiple UrnHandlers
and thus open(...)
multiple urns
for each Ilk
. Although CDP-Manager can be used manually, most interactions are conducted through a DSProxy, a proxy contract used to execute atomic transactions, and DssProxyActions, an atomic transaction library.
In order to calculate collateralization ratios for Vaults in the Maker Protocol, it is important to take the variable par
, also known as the reference price of Dai, into consideration. Failure to do so, might result in incorrect calculation of collateralization ratios, which can result in unwanted liquidations.
To calculate the collateralization ratio of a collateral type (ilk), use the following formula:
Collateralization Ratio = Vat.urn.ink * Vat.ilk.spot * Spot.ilk.mat / (Vat.urn.art * Vat.ilk.rate)
Vat
: Vault database contract
Spot
: Collateral price contract (Interface between price oracles and core Maker contracts)
urn
: Vault
ink
: Amount of collateral tokens
ilk
: Collateral type
mat
: Liquidation ratio
spot
: collateral price with safety margin, i.e. the maximum stablecoin allowed per unit of collateral. Uses par internally.
art
: Normalized stablecoin debt
rate
: Stability fee accumulator - (urn.art*ilk.rate = Dai debt for a Vault)
Note the difference between Spot the contract, and spot the variable.
Since par
is being used in the Spot.poke() function, it will affect the spot
value of the collateral type. See spot variable in the poke() function below.
function poke(bytes32 ilk) external {
(bytes32 val, bool has) = ilks[ilk].pip.peek();
uint256 spot = has ? rdiv(rdiv(mul(uint(val), 10 ** 9), par), ilks[ilk].mat) : 0;
vat.file(ilk, "spot", spot);
emit Poke(ilk, val, spot);
}
Since spot
takes par
into consideration, the formula for collateralization ratio above will work, even if par
changes.
In order to ensure that your integration calculates the same collateralization ratio as the Maker Protocol, only parameters used in the Vat and Spot contracts should be utilized.
In this guide, you were introduced to the locations of important data structures within the Maker Protocol, ranging from collateral types and their risk parameters to the state of Vaults.
Run into an issue that’s not covered in this guide? Please find our contact information at the end of this guide, and we’ll add it above or to this section.
Rates Module Documentation Guide: Intro to Rates Mechanism in the Maker Protocol Example: Compounding rates
- Rocket chat - #dev channel