Try   HackMD

Esoteric Secrets of DAI Accounting

Author: Kurt Barry

Finding out the total supply of DAI is simple, right? Invoke ERC20.totalSupply() and you're done. This essay wouldn't exist if that were the case, of course, so please read on if you're curious.

Secret #1: Not all DAI is ERC20 supply

The DAI ERC20 contract has a totalSupply() method, but not all DAI exists in ERC20 form. The source of truth is the core DAI Credit System (DCS) protocol (colloquially called the "Maker Protocol" at times), specifically a contract called the Vat. You can call Vat.debt() to get the "true" DAI total supply. Beware: this value has 45, not 18, decimals (so 10^45 = 1 DAI). In Daiwanese (MakerDAO jargon for "MakerDAO jargon"), we call a number like that a rad. We'll encounter more rads later. The supply of DAI ERC20 tokens is less than or equal to this internal measure.

Okay, so just call Vat.debt(), divide by 10^45, and call it a day? No way, that would be boring!

Secret #2: Surplus and sin cancel

The accounting logic for the DCS is asynchronous. The system accumulates income from sources like stability fees and liquidation penalties; it accumulates unbacked debt (a liability) when liquidations occur. The system's "income" balance is often called the "system surplus", and unbacked debt is referred to as sin. If the system has a million DAI of surplus but also a liability of a million DAI, those cancel out netting to zero and reducing the DAI supply by one million. This doesn't happen automatically though and quite a bit of both can build up, meaning that Vat.debt() may not be a good representation of "DAI in economic circulation" at any given time. To give an extreme example, suppose the debt is one billion but there's 900 million in both surplus and unbacked debt. The economically "real" supply of DAI is only 100 million but if you were just looking at Vat.debt(), you would get a very distorted idea of things.

That was rather abstract, so let's use some diagrams and examples to deepen our understanding.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The above diagram illustrates the accounting within the Vat. The equation at the top is called the "Fundamental Equation of DAI". While the system allows sin to be assigned to any address, it is currently assigned to just one, a contract called the Vow. The smaller of Vat.dai(Vow) and Vat.vice() (or Vat.sin(Vow) which is the same as vice currently) tells you how much should be subtracted from Vat.debt() to get a more accurate idea of the circulating DAI supply.

The Vow is responsible for keeping the system in a healthy financial state, and its logic is at the crux of why the divergence between Vat.debt() and circulating DAI occurs. In particular, when a liquidation happens, the debt from that Vault is recorded in a data structure within the Vow. A configurable time period has to pass before that debt can be dealt with in any way–this is to allow a chance for the liquidation auction to recapitalize the debt and prevent the unnecessary sale of MKR to cover said debt. But these entries don't erase or expire automatically, and thus have to be actively cleared once the waiting period is over. There's only an incentive or need to do this if the unbacked debt exceeds the surplus. More discussion and illustrations can be found in an old presentation.

Here's a concrete example: a liquidation of an ETH Vault occurs. The Vault had 5 million of debt. The amount (tab) of 5 million and the time (era) the liquidation happened is recorded in the Vow and the total amount of "locked" debt is incremented (this is Vow.Sin(), not to be confused with the Vat.sin mapping). Vat.vice() also increases by 5 million (note: all in rad units). In a few hours, the auction successfully recovers 5 million DAI, which is added to Vat.dai(Vow). In principle these amounts cancel, but that can't be done in the smart contracts until the waiting period elapses. Then someone needs to call Vow.flog(era) followed by Vow.heal(tab) (tab is a rad, too!). Not doing this doesn't affect normal operation of the system; the only time it needs to be done is if there's net unbacked DAI assigned to the system requiring a sale of MKR.

So far, our method to calculate total DAI supply (meaning circulating, economically real DAI), is:

 Vat.debt() - min(Vat.dai(Vow), Vat.vice())
--------------------------------------------
                     10^45

This is a pretty decent measure of "real" DAI for most purposes. But things get even weirder :).

Secret #3: D3Ms and Levered DAI-Containing LP Positions

These two sources of DAI supply have unique properties that in some cases might motivate treating them differently from DAI created via normal borrowing or stablecoin exchange. Arguably, applying smart corrections based on the utilization of these mechanisms provides a more "realistic" idea of the market's demand for DAI, although ambiguity still exists.

Direct Deposit Modules (D3Ms)

A D3M works by:

  1. minting some DAI out of thin air,
  2. depositing it in a lending market (e.g. AAVE or Compound), and
  3. using the resulting lending market shares to back the printed DAI.

Is this "real" DAI? Well, it is in the market and can be borrowed. It has an unmitigated impact on lending and borrowing rates in the protocol it was minted to. It can be stolen in a hack. So, it's pretty real. But in other ways, it isn't. To take an extreme example: suppose 100 million DAI is minted into a lending market, but there's zero DAI borrowing there. There are no claims on that DAI that would prevent the Maker protocol from unwinding the position. Or, Maker could mint another 100 million DAI in there. Or a trillion. But if there's no borrowing taking place, it's basically just bookeeping and you'd be well-justified to ignore that contribution to total DAI supply on the basis of there being no demand for that portion of supply.

Things are more interesting when there's borrowing. Example: consider a lending market with 100 million DAI supplied and 60 million borrowed. A Maker D3M mints another 200 million DAI into the market. So long as the amount of DAI borrowed doesn't go above 100 million, the Maker protocol could still withdraw all of its own liquidity at a moment's notice. So, is the "correct" market cap adjustment still -200 million? Not necessarily–for example if the 100 million in non-D3M deposits gets withdrawn by the other LPs, Maker is now responsible for all of the borrowed DAI! Since the market pools deposits, a reasonable option is to assign a proportional fraction of the borrows to Maker's liquidity, i.e. to consider (200 / 300) * 60 million = 40 million as being borrowed from Maker's contribution, leading to a "real DAI" correction of -160 million. Or you could use the simpler method of subtracting the entire amount up until borrows exceed the non-D3M liquidity. Ultimately, the way you factor D3M-generated DAI into the DAI total supply probably depends on what you want that number to tell you, or the situation you want to analyze.

Levered DAI-Containing LP Positions

Consider a DEX liquidity provision (LP) position where one of the tokens is DAI, for example a Uniswap V2 DAI-USDC LP token. Some such tokens are permissible collateral for minting DAI (this is usually used to lever up the LP position, going long the swap fee yield relative to the cost of borrowing DAI). So this is DAI that's backed, in-part, by…DAI? Yes, and though it sounds weird at first it's not really any different from or riskier than other types rehypothecation (like recursive DAI borrow/lend on Compound) if done on an overcollateralized basis (which it is). But you might want to make a similar correction to what's done for the D3M case, as we'll discuss.

To use a concrete example: say you have LP tokens representing a DEX position of 500K USDC paired with 500K DAI. You deposit this into a Vault and take a loan of 500K DAI. You swap half the DAI for USDC and supply all funds as liquidity, resulting in more LP tokens representing 250K USDC paired with 250K DAI (yes ignoring fees and slippage), which you put into your Maker Vault to up your collateralization ratio. DAI supply has increased by 500K, but via a loan against an asset that is itself 50% DAI. How "real" is that DAI? Well, the 250K DAI swapped for USDC went towards increasing DAI in circulation, and the 250K DAI that is now paired with 250K USDC on the DEX is meaningfully impacting liquidity as well. So, similar to the D3M, it all has some level of econmic implication. But in another sense only 250K is truly real, due to 50% of the collateral being DAI itself. E.g. if the system were to go through global settlement, $500K worth of LP tokens would be reserved (i.e. not available to you to withdraw) to back the 500K DAI borrowed, meaning there is 250K DAI not held by economic actors looking to redeem for collateral during settlement. So to measure "unlevered" DAI you'd correct amounts borrowed against DAI-containing assets by the amount of DAI backing they have.

It's worth noting that analogous reasoning would apply to zero-coupon "principal" tokens that redeem 1:1 at a set maturity for DAI if used as Vault collateral. These are produced by various fixed rate protocols, and while not none are currently onboarded as DAI collateral, they might be someday.

That covers the caveats that apply to the current system…but how will things change in the future?

Secret #4: Cross-Domain Preminting (coming soon)

The DAI Credit System hasn't yet been deployed on other domains (like L2s, sidechains, and other L1s), but when it is, the ETH L1 instance remains the central accounting ledger. For efficiency reasons, DAI will be "pre-minted" to other domains. To be concrete: suppose the protocol is deployed on Arbitrum with a 1 billion DAI debt ceiling (meaning up to 1 billion DAI can be generated by Vaults on Arbitrum). A billion DAI will be minted and placed in an "escrow" contract for the Arbitrum chain. This DAI is completely unreal and inaccessible to the market until DAI is generated by a Vault on Arbitrum (and it only becomes active on mainnet if that DAI is bridged from Arbitrum to mainnet). Once there are working Vault engines on other domains, it will be necessary to correct for "unborrowed" DAI in escrows by subtracting from the supply the difference between all cross-domain debt ceilings and the DAI actually drawn from Vaults on other domains.

And yes, if other domains have D3Ms or levered DAI-containing LP positions, it may be necessary to apply further corrections for those recursively depending on whether they matter for your use case.

Secret #5: Locked DAI

This category is a small correction, but worth mentioning as it often comes up in discussions. Some DAI has regrettably been permanently locked, for example by being accidentally sent to the DAI contract itself or to the zero address. If you are trying to quantify market demand for DAI, it might be reasonable to subtract off DAI that is known to be irretrievable. There is no known comprehensive list of locked DAI, however, so it can be somewhat tricky to measure completely.

There's no way we're actually done, right?

Yes, in fact that wraps things up! Hopefully this helps you understand how to calculate a meaningful "total supply" of DAI, no matter what your need is.