Jug
The primary function of the Jug smart contract is to accumulate stability fees for a particular collateral type whenever its drip() method is called. This effectively updates the accumulated debt for all Vaults of that collateral type as well as the total accumulated debt as tracked by the Vat (global) and the amount of Dai surplus (represented as the amount of Dai owned by the Vow.
Contract Details
Section titled “Contract Details”Structs
Section titled “Structs”Ilk : contains two uint256 values—duty, the collateral-specific risk premium, and rho, the timestamp of the last fee update
VatLike : mock contract to make Vat interfaces callable from code without an explicit dependency on the Vat contract itself
Storage Layout
Section titled “Storage Layout”wards : mapping(address => uint) that indicates which addresses may call administrative functions
ilks : mapping (bytes32 => Ilk) that stores an Ilk struct for each collateral type
vat : a VatLike that points the the system’s Vat contract
vow : the address of the Vow contract
base : a uint256 that specifies a fee applying to all collateral types
Public Methods
Section titled “Public Methods”Administrative Methods
Section titled “Administrative Methods”These methods require wards[msg.sender] == 1 (i.e. only authorized users may call them).
rely/deny : add or remove authorized users (via modifications to the wards mapping)
init(bytes32) : start stability fee collection for a particular collateral type
file(bytes32, bytes32, uint) : set duty for a particular collateral type
file(bytes32, data) : set the base value
file(bytes32, address) : set the vow value
Fee Collection Methods
Section titled “Fee Collection Methods”drip(bytes32) : collect stability fees for a given collateral type
Key Mechanisms & Concepts
Section titled “Key Mechanisms & Concepts”drip(bytes32 ilk) performs stability fee collection for a specific collateral type when it is called (note that it is a public function and may be called by anyone). drip does essentially three things:
- calculates the change in the rate parameter for the collateral type specified by
ilkbased on the time elapsed since the last update and the current instantaneous rate (base + duty); - calls
Vat.foldto update the collateral’srate, total tracked debt, and Vow surplus; - updates
ilks[ilk].rhoto be equal to the current timestamp.
The change in the rate is calculated as:
$$ \Delta rate = (base+duty)^{now-rho} \cdot rate- rate $$
where “now” represents the current time, “rate” is Vat.ilks[ilk].rate, “base” is Jug.base, “rho” is Jug.ilks[ilk].rho, and “duty” is Jug.ilks[ilk].duty. The function reverts if any sub-calculation results in under- or overflow. Refer to the Vat documentation for more detail on fold.
rpow(uint x, uint n, uint b), used for exponentiation in drip, is a fixed-point arithmetic function that raises x to the power n. It is implemented in Solidity assembly as a repeated squaring algorithm. x and the returned value are to be interpreted as fixed-point integers with scaling factor b. For example, if b == 100, this specifies two decimal digits of precision and the normal decimal value 2.1 would be represented as 210; rpow(210, 2, 100) returns 441 (the two-decimal digit fixed-point representation of 2.1^2 = 4.41). In the current implementation, 10^27 is passed for b, making x and the rpow result both of type ray in standard MCD fixed-point terminology. rpow’s formal invariants include “no overflow” as well as constraints on gas usage.
Parameters Can Only Be Set By Governance
Section titled “Parameters Can Only Be Set By Governance”Jug stores some sensitive parameters, particularly the base rate and collateral-specific risk premiums that determine the overall stability fee rate for each collateral type. Its built-in authorization mechanisms need to allow only authorized Sky Ecosystem governance contracts/actors to set these values. See “Failure Modes” for a description of what can go wrong if parameters are set to unsafe values.
Gotchas (Potential Sources of User Error)
Section titled “Gotchas (Potential Sources of User Error)”Ilk Initialization
Section titled “Ilk Initialization”init(bytes32 ilk) must called when a new collateral is added (setting duty via file() is not sufficient)—otherwise rho will be uninitialized and fees will accumulate based on a start date of January 1st, 1970 (start of Unix epoch).
base + Ilk.duty imbalance in drip()
Section titled “base + Ilk.duty imbalance in drip()”A call to drip(bytes32 ilk)will add the base rate to the Ilk.duty rate. The rate is a calculated compounded rate, so rate(base + duty) != rate(base) + rate(duty). This means that if base is set, the duty will need to be set factoring the existing compounding factor in base, otherwise the result will be outside of the rate tolerance. Updates to the base value will require all of the ilks to be updated as well.
Failure Modes (Bounds on Operating Conditions & External Risk Factors)
Section titled “Failure Modes (Bounds on Operating Conditions & External Risk Factors)”Tragedy of the Commons
Section titled “Tragedy of the Commons”If drip() is called very infrequently for some collateral types (due, for example, to low overall system usage or extremely stable collateral types that have essentially zero liquidation risk), then the system will fail to collect fees on Vaults opened and closed between drip() calls. As the system achieves scale, this becomes less of a concern, as both Keepers and SKY holders are have an incentive to regularly call drip (the former to trigger liquidation auctions, the latter to ensure that surplus accumulates to decrease SKY supply); however, a hypothetical asset with very low volatility yet high risk premium might still see infrequent drip calls at scale (there is not at present a real-world example of this—the most realistic possibility is base being large, elevating rates for all collateral types).
Malicious or Careless Parameter Setting
Section titled “Malicious or Careless Parameter Setting”Various parameters of Jug may be set to values that damage the system. While this can occur by accident, the greatest concern is malicious attacks, especially by an entity that somehow becomes authorized to make calls directly to Jug’s administrative methods, bypassing governance. Setting duty (for at least one ilk) or base too low can lead to Dai oversupply; setting either one too high can trigger excess liquidations and therefore unjust loss of collateral. Setting a value for vow other than the true Vow’s address can cause surplus to be lost or stolen.
Released into the public domain (CC0 1.0 Universal) – trademarks remain with their owners; no warranty. See full license.