Holders of DAI can permissionlessly earn interest on their stablecoin assets from multiple sources, including Maker’s Dai Savings Rate or through liquid lending pools like Compound. When using Compound, a user deposits DAI and receives cDAI (Compound’s token reflecting the current exchange rate for DAI) in exchange. When the user redeems from cDAI back to DAI, she will have more DAI than she started with, assuming at least one block (about 15 seconds) has elapsed, because Compound updates the exchange rate every block.
rDAI uses Compound to allow users to earn interest on DAI, but instead of redeeming the interest and principal back into the user’s wallet as one might do with a traditional bank savings account, it separates the principal from the interest. The principal is always under the original user’s control, but the interest is directed to flow wherever the rDAI instructions specify.
In this tutorial, we’ll first cover the main rDAI concepts that should help users and devs grasp how it works under the hood. Second, we’ll go through a couple of rDAI use cases to show you step-by-step how to program interest to flow wherever you like. Finally, we’ll link to a number of resources to help you build further on rDAI.
Q: What is the ratio of DAI to rDAI?
A: 1:1. One DAI = one rDAI.
rDAI uses a concept called a “hat” to contain the instructions for where the interest should flow. A hat is an object that contains two arrays:
recipients, which lists the addresses (maximum 50 recipients per hat to prevent out of gas errors) that will receive the interest, and
proportions, which specifies how much of the interest each address should receive.
Here’s an example:
New hats are created in rDAI through the
createHat function. They are given sequential uint256 IDs and are immutable once created. Anyone can create a new rDAI hat as long as they pay the gas to do so. Creating a hat does not require holding DAI or minting rDAI yourself with the newly created hat — it just specifies a set of interest redirection instructions that other rDAI users can choose to follow by setting that hat.
An Ethereum address can only have one rDAI hat at a time.
An rDAI holder can call the
changeHat function to swap to a different hat, but they cannot split the same address’ rDAI between two hats, nor can they modify an existing hat.
Minting rDAI requires a user to hold DAI. rDAI can be redeemed back to DAI at any time. You can transfer rDAI to to another address but for the purposes of this tutorial, we’ll just be looking at how a single user can mint rDAI from DAI and then redeem it back to DAI after some interest has accrued.
In order to mint rDAI from DAI, a user must first approve the DAI to be “spent” by the rDAI minting process. So the first step in minting rDAI is to tell the DAI contract that the DAI holder approves this usage of DAI. This is accomplished by the DAI contract’s ERC20
approve function. We’ll go over this step in detail below.
After the DAI approval step, there are three different functions that allow you to mint rDAI —
mintWithSelectedHat. They are easiest to explain in reverse order.
Users that want to mint rDAI and redirect their interest to an existing set of instructions can do in one step using
mintWithSelectedHat(mintAmount(uint256), hatID(uint256). This is a valuable use case for dapps that want to direct their users’ interest to the recipients the dapp has chosen.
For example the dapp rTrees, which allows users to plant real trees using rDAI interest, has its users call
mintWithSelectedHat for hatID 11. That activates whatever DAI amount the user has specified and directs the interest to Trees for the Future, minus a small platform fee.
Users that want to create a new custom hat and immediately start sending interest to it can use
mintWithNewHat(mintAmount(uint256), recipients(address), proportions(uint32)). This function essentially combines
mintWithSelectedHat, pointing the newly minted rDAI to the newly created hat. Dapps may also wish to use this function to preserve pre-existing rDAI preferences. For instance, if a user was already sending interest on 50 rDAI to Charity X and wanted to now send interest on another 100 rDAI to Charity Y, the Charity Y dapp could create a new hat specifying proportions of 1/3rd to Charity X and 2/3rds to Charity Y and then mint 100 additional rDAI into this new hat. The 50 original rDAI that had been set to the earlier hat would be yanked into the new hat, and the user’s preference would be preserved through the creation of a new hat.
(NOTE: The plans for rDAI v2 include a strategy to make multiple rDAI allocations easier that what is described here).
mint(mintAmount(uint256)) function simply converts DAI to rDAI without further instruction. This leads to two different outcomes, depending on whether the user already has a hat set on their address.
If no hat has been set, this function will “create” a self-hat (one that is not reflected in the hatID array and so is not usable by other rDAI users) that redirects the interest to the user’s own account. It is functionally the same as using Compound to earn interest.
In the scenario where the user has already set a hat,
mint will add the new rDAI to the same hat.
Redeeming is the process for converting rDAI back to DAI. There are two ways to redeem —
redeemAll, which will convert all one’s rDAI to DAI (after claiming the accrued interest) and
redeem(redeemTokens (uint256)), which allows a user to specify a particular amount of rDAI to redeem to DAI.
The rDAI contract also include
redeemAndTransfer(redeemTo(address), redeemTokens(uint256)) as well as
redeemAndTransferAll(redeemTo(address)), which allow a user with some amount of rDAI to swap it back to DAI and transfer the DAI to another address in a single transaction.
Accruing Interest and Token Balances
It’s worth taking a moment to explain where the interest is accruing, show you how to query the amount accrued, and to go over the steps needed to claim that interest and increment your rDAI token balance.
One misconception that some users and devs have regarding rDAI (and cDAI for that matter) is that the accruing interest automatically increases a user’s wallet token balance every block. This is not the case, because a transaction is always required to update token balances. Many front ends display incrementing balances based on the interest accrued to date, but this is a calculated representation of what one’s balance would be if a transaction were to claim the interest to date. The amount of accrued interest not yet reflected in your rDAI token balance can be read in the variable
interestPayableOf(address) in the rDAI contract. To sweep that amount into your token balance, either
payInterestInternal must be called.
Let’s walk through the lifecycle of rDAI interest step by step to show what’s really going on when. For this example Alice will be redirecting 60% of her rDAI interest to Charity X, while keeping the remainder of the interest herself.
- Alice creates a custom hat with 60% of the interest going to Charity X and 40% going to her wallet address.
- Alice mints 100 rDAI from 100 DAI in her wallet and sets the hat to the newly created 60%/40% hat. As soon as the transaction is confirmed, Alice now has a
balanceOf100 rDAI in her wallet.
- Let’s imagine that enough time passes so that 10 rDAI in interest has accrued on the 100 rDAI. Yet at this time, Alice’s wallet still has a
balanceOf100 rDAI and Charity X’s wallet still has a
- If we were to query
interestPayableOf(Alice’s address), we would get 4 DAI in wei. If we were to query
interestPayableOf(Charity X’s address), we would get 6 DAI in wei.
- To claim the amount of
interestPayableOf, the function
payInterestInternalmust be called. All rDAI actions call
payInterestInternalbefore they occur, so that any amount of
interestPayableOfis claimed before an action like
- If Alice were to call
payInterest(Alice’s address)and pay the gas for the transaction, she would have an rDAI
balanceOf104 after confirmation. Her
interestPayableOfamount would reset to 0 but then begin ticking up again because interest is still accruing according to the hat instructions.
- Alice could be feeling doubly generous and also decide to call
payInterest(Charity X’s address), thereby picking up the gas tab for Charity X claiming 6 rDAI. Anyone can call
payIntereston any address — this simply sweeps the amount in
interestPayableOfinto rDAI tokens viewable in a user’s wallet. Again, this resets the
interestPayableOfto 0 for Charity X, where it will begin ticking up.
TIP: This flow means that if you want to query the total amount of rDAI interest that an address has received, you need to track both
balanceOf for an address.
ANOTHER TIP: The current values stored in
interestPayableOf for all users are based on the current Compound exchange rate. This exchange rate is only updated with the Compound contract is “tickled” by someone performing a transaction on it. So in the event that no one tickles the contract for several blocks, the amount of
interestPayableOf will not update during that time. Typically these delays don’t last more than a couple of blocks.
So now that we’ve covered the basic concepts and shared where to test them out for yourself, let’s walk through a couple use case examples to get some hands on experience. You’ll need MetaMask or another web3 wallet, some ETH and some DAI to follow along.
Use Case 1 — Send the Interest to Charity
First, we’ll need to set up the charity hat so that it can receive your rDAI interest.
In the picture at left I’m using the official rDai app to create a new hat (called “pool” on the site) for the Gitcoin Open Source Support Fund, which I’ve copied into the interface (it’s the address beginning “0x00De”). I’ve also opted to send 5% of the interest generated to support the rDAI dev DAO. This
createHat transaction cost about 675,000 gas and created
To confirm that I created the hat correctly, I can query the new
hatID on OneClickDapp by plugging “179” into
getHatByID under the “Read” tab, which returns the following result:
You can see two addresses in the
recipients array, and two very large
proportions numbers. Because we can’t use decimals in Ethereum, the proportions are based on the maximum uint32 number, which is 4,294,967,295. The first number 214,748,364 is equivalent to ~5% of the maximum uint32 number. The second number 4,080,218,930 represents ~95% of that maximum.
OK so now we have created a specific hat to support a charity — let’s stream it some programmable interest! We’ll need a wallet with DAI in it and we’ll need to approve the rDAI contract to be able to “spend” our DAI by minting rDAI.
Here I’m on the DAI token’s Etherscan interface. After connecting my wallet, I’ll need to call the
approve function, providing the rDAI contract address (0x261b45D85cCFeAbb11F022eBa346ee8D1cd488c0) in the
usr field and specifying approval to spend (the
wad) up to 80 DAI in wei (meaning there are 19 zeros after the 8 — this wei converter is a useful tool if you use DAI in place of Ether).
We can verify that the allowance is approved by switching to the “Read Contract” tab and querying the
allowance variable. You need to enter your wallet address in the first
input field and the rDAI contract address in the second
input field — here the Response at the bottom it shows an allowance of 80 DAI, just as we intended.