--- title: DSR Integration Guide description: Learm about DSR amd integrate it into your platform parent: dai tags: - dai - DSR - dai savings rate - earn savings in dai slug: dsr-integration-guide contentType: guides root: false ---
DSR Integration Guide
Intended Audience: Developers/Technical teams
Time: 30-60 minutes
- DSR Integration Guide
- What is DSR
- How to integrate DSR
- Smart contract addresses and ABIs
- How to integrate DSR using DsrManager
- How to integrate with proxy contracts
- Example of how to interact with the proxy contracts
- How to integrate DSR through the core
- How to integrate with Dai.js
- How to integrate with Pymaker
- How to calculate rates and savings
- Integration Stories
- Additional Resources
- Need help
This guide will explain the Dai Savings Rate and how to integrate DSR into your platform. For specific DSR integration use cases, such as integrating DSR as a centralized exchange see the Integration Stories section for a quick start guides.
Understand the concept of DSR
Understand the functionality of the DSR contract
How to best integrate DSR
What is DSR
Dai Savings Rate (DSR) is an addition to the Maker Protocol that allows any Dai holder to earn savings in Dai.
How to activate DSR
Dai does not automatically earn savings, rather you must activate the DSR by interacting with the DSR contract pot of the Maker Protocol. This means transferring Dai from your wallet to the Maker Protocol. It is important to note that the Dai can always be redeemed immediately (within a block), as there are no liquidity constraints, and can ONLY be retrieved to the depositing account.
Therefore it can be beneficial for any centralized exchange or custodian of Dai to integrate functionality to activate the DSR on Dai in their custody. Similarly, any decentralized exchange, wallet or dapp in general can enable anyone to earn the DSR by integrating the functionality as well.
How to integrate DSR
There are different ways to integrate the DSR, the four main ones being either to integrate directly with the smart contracts of the Maker Protocol,
If you are running a smart contract system, or are already integrated with other protocols at a smart contract level, then it makes sense to interact directly with the Maker smart contracts. In most cases the DsrManager is the simplest way to integrate DSR functionality, as it provides an easy to use contract for this purpose. However there is also the option to integrate through the core or through proxy contracts depending on the use case.
If you just need to enable DSR on an amount of Dai, then it makes sense to integrate using
If you need to integrate with multiple features of the Maker protocol, and want to carry over the proxy identity of users that are reflected in Maker front ends (i.e. be able to automatically show vaults, earned savings etc. in a UI), then it makes sense to integrate with the proxy contracts that the Maker Foundation uses.
If you need to integrate the DSR and the functionality of the
DsrManageris not enough, then it makes sense to look at integrating directly with the core Maker smart contracts.
If you custody Dai, but are not otherwise integrated directly with the smart contract layer of Ethereum, then it makes sense to use Dai.js, as the heavy plumbing of calling the smart contracts have been done for you. In the following, both approaches will be detailed.
Smart contract addresses and ABIs
The latest contract addresses and ABIs of the Maker Protocol can be found here: https://changelog.makerdao.com
The 1.0.2 contracts we are going to cover in the following are:
How to integrate DSR using DsrManager
DsrManager is an easy-to-use smart contract that allows service providers to deposit/withdraw Dai into the DSR contract pot, and activate/deactivate the Dai Savings Rate to start earning savings on Dai within a single function call.
Other than integrating DSR through the Maker Protocol’s core, interacting with
DsrManager is very similar to the other integration methods; in fact, the only difference is the former doesn’t require the Ethereum address to build and own a proxy contract.
To use DsrManager, first you need to approve the DsrManager contract in the Dai Token contract by calling
Dai.approve(address DsrManager). Then you are free to use the contract functions to deposit and withdraw Dai from the DSR contract.
To activate DSR on your Dai, call
join(address dst, uint256 amount) with the address parameter being your own wallet address, or if you want to add Dai to DSR to be owned by another address you can specify that - i.e. if you want to move Dai from a hot a wallet into DSR for a cold wallet. To retrieve Dai from DSR, you can use
exit(address dst, uint256 wad) for withdrawing a certain amount to your chosen address. Or,
exitAll(address dst) to withdraw all Dai to a chosen address.
The savings activation flow can be split into the following steps:
Earn savings on Dai
join(address dst, uint wad)
Monitor savings of an address
daiBalance(address usr) returns (uint wad)to return the entire balance (principle + savings)
Retrieve earned savings
a. For a specific amount
exit(address dst, uint wad)
b. For entire balance
For more details on the DsrManager, read the DsrManager documentation.
For a DsrManager contract integration example, look here. In this example you'll see a simple contract that uses the DsrManager contract as an interface.
Difference between DSRManager and Proxy Contracts
By using DsrManager, you can avoid having to use a DS-Proxy contract to deposit Dai into the DSR. Instead, you can simply interact directly with the DsrManager which keeps track of user balances in
pot through the
pieOf mapping. In many instances using DsrManager will be the simpler way of doing a smart contract based DSR integration.
How to integrate with proxy contracts
The Maker Protocol has been developed with formal verification in mind. Therefore, the core smart contracts only contains functions that carry out singular simple actions. Consequently, in order to carry out meaningful invocations on the protocol, you must string together a series of core function calls. Instead of having to send a series of transactions, Maker’s proxy contracts atomically invoke a series of function calls that are used to interact with the Maker core in a safe and easy way. This is done through using a proxy identity for the user called DS-Proxy. This library is therefore only safe if you execute actions through this proxy identity, since the proxy manages access rights. Therefore, if you execute functions directly on the proxy library DSS-Proxy-Actions-DSR, and not through DS-Proxy, there will be no access management, and funds can therefore be lost, so it is very important you only execute functions through a DS-Proxy. The good thing is that anyone who integrates the DS-Proxy will be able to reflect the same user identity as in the Maker frontends, such as oasis.app. So if you are building a similar product suite and you want to carry over existing users, and their vaults and access to DSR from the Maker frontends, you should integrate using DS-Proxy and the proxy libraries.
In the case of a DSR integration, we want to interact with the core DSR contract called
pot (detailed documentation here) by executing function calls on a proxy contract called
dss-proxy-actions-dsr using the ds-proxy contract.
IMPORTANT! You should be familiar with working with ds-proxy before you attempt this integration, so it is very important that you are well acquainted with the concepts in this guide before you proceed to ensure that you do not risk funds.
The main idea is that you use the
execute(address _target, bytes memory _data) function of
ds-proxy by using the
dss-proxy-actions-dsr contract address and the ABI encoded call data of the function you want to execute in that specific contract.
In order to activate savings you must send Dai to the pot contract by invoking the dss-proxy-actions-dsr proxy contract. In order to do this, you must from a ds-proxy, invoke the function join of
dss-proxy-actions-dsr which takes the address of the Dai adapter -
DaiJoin, the address of the DSR contract
pot, and the amount of Dai you would like to add to the DSR contract. In order to do this, the
ds-proxy must have an allowance on the amount of Dai you want to enable savings on.
In order to retrieve Dai and savings from the
pot there are two options - either you can retrieve a specific amount of Dai, or all Dai that the ds-proxy has the rights to with the functions exit and exitAll. In order to do this, you must from the
ds-proxy invoke the
exitAll functions of the
Example of how to interact with the proxy contracts
We’ll use the 0.2.17 MCD Kovan Deployment to showcase how you could interact with DSR using the
seth tool with the Proxy Actions DSR contract.
You can also use the Rinkeby, Ropsten and Goerli deployments in this guide. Just make sure to change the contract addresses from the specific network from https://changelog.makerdao.com
Before starting, you need to set up the right variables in your terminal. Save the below variables:
export PROXY_REGISTRY=0x64A436ae831C1672AE81F674CAb8B6775df3475C export PROXY_ACTIONS_DSR=0xc5cc1dfb64a62b9c7bb6cbf53c2a579e2856bf92 export POT=0xea190dbdc7adf265260ec4da6e9675fd4f5a78bb export DaiJoin=0x5aa71a3ae1c0bd6ac27a1f28e1415fffb6f15b8c export DAI=0x4f96fe3b7a6cf9725f59d353f723c1bdb64ca6aa export ETH_GAS=4000000
Check if you have a DS-Proxy contract
Next step is to verify if your address has a proxy contract already deployed. If you have used oasis.app before, you should already have one.
Execute the command below to verify if you have a proxy contract:
seth call $PROXY_REGISTRY 'proxies(address)(address)' $ETH_FROM
If your output is:
0000000000000000000000000000000000000000, then you don’t have a DS-Proxy contract.
Execute the command below to build a DS-Proxy contract for your address:
seth send $PROXY_REGISTRY 'build()'
Now save your proxy address in a variable:
export MYPROXY=$(seth call $PROXY_REGISTRY 'proxies(address)(address)' $ETH_FROM)
In order to use the DS-Proxy contract, first you need to approve it in the Dai token contract to allow it to use your Dai for transfers.
Execute command below to approve your DS-Proxy:
seth send $DAI 'approve(address,uint256)' $MYPROXY $(seth --to-bytes32 $(mcd --to-hex -1))
Here is an example of a successful transaction.
Deposit Dai in DSR
Let’s create the calldata that we’ll use for the
execute function in DS-Proxy.
In a terminal save the following variables:
export wad=$(seth --to-uint256 $(seth --to-wei 1 eth))
export calldata=$(seth calldata 'join(address,address,uint)' $DaiJoin $POT $wad)
wad is the amount of Dai you want to deposit. The
$(seth --to-wei 1 eth)) command converts the number to the eth conversion, i.e. to an integer with 18 decimals after it.
calldata is the bytes data defining the function call you will use in the
Now let’s call the
execute function in your proxy contract to deposit 1 Dai into the DSR
seth send $MYPROXY 'execute(address,bytes memory)' $PROXY_ACTIONS_DSR $calldata
Here is an example of a successful transaction.
Withdraw Dai from DSR
To withdraw a portion of your Dai from DSR you can use the
Set the amount you want to withdraw, in this case we use 0.5 Dai:
export amount=$(seth --to-uint256 $(seth --to-wei 0.5 eth))
Export the call data of the
export exitDaiCalldata=$(seth calldata 'exit(address,address,uint)' $DaiJoin $POT $amount)
execute function in your proxy contract to withdraw 0.5 Dai from DSR:
seth send $MYPROXY 'execute(address,bytes memory)' $PROXY_ACTIONS_DSR $exitDaiCalldata
To check that you have received the Dai, you can check your balance using the following command:
seth --from-wei $(seth --to-dec $(seth call $DAI 'balanceOf(address)' $ETH_FROM))
Withdraw all Dai from DSR
To withdraw all your Dai balance from DSR you can call the
Export the call data of the
export exitAllCalldata=$(seth calldata 'exitAll(address,address)' $DaiJoin $POT)
execute function in your proxy contract to withdraw the remaining Dai from DSR:
seth send $MYPROXY 'execute(address,bytes memory)' $PROXY_ACTIONS_DSR $exitAllCalldata
Here is an example of a successful transaction.
How to integrate DSR through the core
In order to integrate DSR by interacting directly with the core, you need to implement a smart contract that invokes functions in the
pot is the Dai Savings Rate contract, and you can read detailed documentation of the contract here. In order to earn savings on Dai, you must call the function join with the amount of Dai you want to earn savings on. However, in order for this function call to succeed you must first call drip to update the state of the system, to ensure internal balances are calculated correctly. Therefore to activate savings on x amount of Dai, you must call
pot.drip() and then
x is a
uint256 in the same transaction. In order to do this atomically you need to implement these calls in a smart contract that can carry out both function calls in a single transaction. If you use a smart contract to carry out these function calls, since the DSR contract uses
msg.sender as the depositor,
msg.sender will be the only one able to retrieve Dai from DSR. So, ensure that only you have access to withdraw Dai from the DSR contract by implementing the necessary access mappings.
A simple DSR example of how to interact with the Maker core can be found here. NOTE: This is just an example and has not been audited. In this example, you can see how to properly call the
pot.exitAll() functions. Give close attention to the helper math functions, as they convert the Dai ERC-20 token 18 decimal into the internal accounting 27 decimal numbers.
How to integrate with Dai.js
By using Dai.js, you will have access to all the available functions of the Maker Protocol. This library can be used both for backend and frontend applications. The documentation of Dai.js can be found here: https://github.com/makerdao/dai.js/wiki. The specific functionality we will go over in this document is contained and documented in the Multi-Collateral Dai package found here.
To install the library make sure to have node.js installed and then run the below command:
npm install @makerdao/dai and include the MCD package to work with the functions that will be covered in the following sections.
Earning Savings on Dai
Using Dai.js, you can utilize the join function to add a specified amount of Dai to the DSR contract, which will activate savings. In the example of an exchange, any time Dai is deposited into the exchange, the
join function can be called by the exchange, so the user will instantly start earning savings on the deposited Dai. The diagram below shows the flow for the user, using an exchange which has integrated the DSR. In this case, the flow of the user does not change, as he simply deposit Dai into the exchange, which on behalf of the user will add Dai to the DSR contract to start earning savings.
In order to see the balance of a user’s Dai holdings in the savings contract, and thus how much savings he has earned, the function balance can be used. The function getYearlyRate can be used to get the current savings rate. User’s Dai balance with the exchange can be updated continuously by the exchange or in specified intervals.
Retrieve Accrued Savings
When a user wants to withdraw Dai, the function exit can be used to withdraw a specific amount of Dai from the DSR contract. You can also invoke the exitAll function, to retrieve all Dai for a user including earned savings. Afterwards, the Dai can be withdrawn from the exchange itself. Therefore, when a user wants to withdraw Dai from the exchange, these function calls can simply be invoked first in that process, resulting in a seamless experience for the user. The diagrams below show the flow for the user, when he wants to withdraw Dai from the exchange.
In order to ease Keeper development, a Python API for most of the Maker contracts has been created. It can be used not only by keepers, but may also be found useful by authors of some other, unrelated utilities aiming to interact with these contracts, such as DSR interaction.
You only need to import this project as a Python module if you want to utilize the API. Moreover, it offers a transaction facility, wrapped around
web3.eth.sendTransaction(), which enables the use of dynamic gas pricing strategies.
The below examples follow the same interaction pattern as described above in the Dai.js section, which covers the full DSR Lifecycle of joining and exiting Dai in the
- Here's a simple integration example
- If you need some guidance on incorporating pymaker into your project, check out our more comprehensive integration example.
How to calculate rates and savings
Whether you are integrating with the core, or the proxy contracts, you must interact directly with the core contract
pot to retrieve the current status of the system, such as the current savings rate or the balance and earned savings for a specific user.
The current savings rate is stored in the variable
dsr which will return the savings rate per second. At the time of writing this variable returns: 1.000000000627937192491029810 which is the savings % per second. To get the APR you simply have to uplift this number to the amount of seconds in a year:
dsr^(60*60*24*365) - in this case this is
(1.000000000627937192491029810)^(60*60*24*365)=1.019999766 equal to 2 % APR.
The way savings grow for user balances deposited in
pot is by using normalized balances and a rate accumulator. When you deposit Dai into
pot the user balance is divided by the rate accumulator
chi and stored in the normalized user balances map
To calculate the normalized balance stored in pie you simply do the following equation:
Normalized Balance = Deposited Dai / chi
Everytime the system is updated by calling
drip() the number
chi grows according to the savings rate
dsr. However the normalized balances in
pie remain unchanged.
To retrieve current balance of a user in Dai, and therefore also earned savings, you must retrieve the normalized balance and and multiply it with the number
chi to calculate the amount of Dai the user can retrieve from the contract. This is thus the reverse calculation of the earlier deposit function.
Therefore to retrieve the balance of a user, you need to do the following calculation:
Dai balance of a user = pie[usr_address] * chi
The above equation makes it trivial to see that when
chi grows, Dai balance of a user grows, so this is how the compounded savings are calculated.
You can read more about rates here:
Calculating user earnings on a pool of Dai in DSR
If you are an exchange or some other type of custodian that holds funds on behalf of users, and you want to manage a pool of Dai with DSR activated, rather than a stack for each user, you need to do some internal accounting to keep track of how much Dai each user has earned and are allowed to withdraw from the pool.
In order to do this, any time a user activates DSR on an amount of Dai, you need to calculate a normalized balance, to calculate the savings for each user.
The normalized balance is a balance that does not change, but resembles a fraction of Dai in the DSR contract. It is calculated by taking the amount of Dai a user wants to add to the DSR contract, and dividing it by the variable
chi, which is the “rate accumulator” in the system.
chi is a variable that increases in value at the rate of the DSR. So if the DSR is set to 4% APY, the chi value will grow 4% in a year. The rate is accumulated every second and is updated almost every block, why the number
chi is a small number that grows slowly. The variable
chi can be read from the Oasis API at https://api.oasis.app/v1/save/info, directly from the DSR smart contract pot, or retrieved from the integration libraries.
Therefore, when an amount of Dai of a user is added to the DSR contract, you simply need to store how much Dai they are supplying, and calculate and store what the normalized balance is at that time. So if Alice adds 10 Dai to your pool of Dai in DSR, you would record the following:
Deposit 2020-01-08: User: Alice, Dai: 10, Chi: 1.0002345, Normalized Balance (Dai/Chi): 9.9976555
In this case, at the time of deposit,
chi is 1.0002345, which evaluates to a normalized balance of 9.9976555. In reality
chi has 27 decimals, and in a production scenario it is beneficial to use all the decimals in order to achieve maximum precision, since
chi accumulates the savings rate every second, and thus the number only grows a tiny bit every block.
3 days go by, and the
chi value grows according to the DSR. Now
chi is 1.0006789. Alice wants to know how much her savings has increased in value. To calculate her stack, you simply take
Normalized Balance_Alice * chi
= 9.9976555 * 1.0006789 = 10.0044429 Dai
Alice has thus earned 0.0044 Dai in 3 days, and can withdraw this amount + her original 10 Dai from the Dai pool in DSR. However Alice decides to add 10 Dai extra to the pool. Again, you simply need to record the amount she deposits, and the current
chi value to calculate the normalized balance.
Deposit 2020-01-11: User: Alice, Dai: 10, Chi: 1.0006789, Normalized Balance (Dai/Chi): 9.9932156
Notice that since
chi has gone up since the first deposit, this time Alice’s normalized balance is lower. This is how the system keeps track on how much Dai deposits on different days have earned from the DSR.
3 days more go by, and now Alice wants to calculate how much her total amount of Dai in DSR is worth. Now
chi is: 1.0011233.
This time, you must add the two normalized balances, and multiply it with
chi. So the equation is:
= (9.997655+9.9932156)*1.0011233 = 20.0133263 Dai
Alice’s Dai holdings in DSR is now 20.0133 Dai, so she has in total earned 0.0133 Dai over the 6 days.
To sum up, in order to keep track of user holdings of Dai in a DSR pool, you must simply calculate and store the normalized balance of their Dai at that point in time their Dai is added to the pool, by dividing their Dai contribution with
chi. When the user wants to retrieve all their Dai, you simply take their entire normalized balance and multiply it with
chi, to calculate how much Dai he can retrieve.
As described in this guide, there are many approaches to integrate DSR support into your system. Each approach has pros and cons, varying levels of complexity, and different technical requirements. This section is purposed to help streamline the integration experience for integration stories of common intention. Akin to directions on a map, integration stories help guide the reader through a suggested path, but they do not prohibit exploration of other paths, which may lead to improved or more thoughtful integrations. Each story begins with some assumptions of the developer’s system, an introduction to the story, and the contents thereof.
The following assumptions about your system’s design and operation:
Single cold/hot wallet(s) holding assets for multiple users
Scalable off-chain accounting system holding a record of users’ balances
Asynchronous contract calls following User action
Ability to make calls to contracts that don’t inherit the ERC20 spec
Will not need to interact with adjacent systems that require a proxy identity.
With these assumptions, the following integration story is relieved of the proxy-identity requirement and prioritizes simplicity for the centralized exchange. In the following steps, we build up the prerequisite knowledge of the DSR and outline the steps to integrate the
DsrManager before concluding with some recommendations. Considered as the simplest and most secure way of adding Dai to the DSR, the
DsrManager is a smart contract that allows service providers to deposit/withdraw Dai into the DSR contract (Pot contract), and activate/deactivate the Dai Savings Rate to start earning savings on a pool of Dai in a single function call. Let us begin:
- Become knowledgeable of the DSR
- Understand at a high level how to integrate the DSR (read only the introduction)
- Integrate with the DsrManager
- Learn how rates and savings are calculated in the Pot contract
- Calculate the savings for each user while operating a single wallet
- Incorporate design patterns to handle Emergency Shutdown. Under extreme circumstances, such as prolonged market irrationality, governance attacks, or severe vulnerabilities, the Maker Protocol will go through Emergency Shutdown. It’s of paramount importance to ensure your systems can handle Dai in the DSR contract after Emergency shutdown has been triggered.
- Consider automatically depositing Dai into the DSR contract either as soon as it’s deposited into the exchange or after a User action to “activate” the DSR.
- Both joining and exiting from the
DsrManagercan direct ownership of the Dai deposit and withdrawal between accounts. Although private key management may differ across exchanges, we suggest joining with the hot wallet and transferring ownership of the Dai activated in the DSR to the cold wallet; similarly, we recommend exiting with the cold wallet and transferring the Dai principle + savings amount to the hot wallet, where it can be later transferred to a user wishing to withdrawal Dai from the exchange. Presented in another way:
join(<cold address>, <Dai amount>)from the Hot wallet
exit(<hot address>, <Dai amount>)from the Cold wallet
In this guide, we covered the basics of DSR, and how to properly integrate DSR, by either using core contracts, proxy contracts or the Dai.js library.
- Rocket chat - #dev channel