Integrating Chai into your Dapp to earn interest from Dai Savings Rate.

makoto_inoue
Dec 8, 2019 · 7 min read

A day after chai (wrapped version of Dai in the DSC contract) was deployed to Mainnet, I received a message from Lev, one of the creators.

Luckily, I was just about to organise my codeup (a meetup for coders to talk about Ethereum coding), so we met a few hours early to actually integrate into Kickback and did code walkthrough during the event.

We had great guests. Not only Lev knew Chai inside out, but we also had Fran (co-creator of rDAI, wrapped version of Compound’s cDAI), Ricardo from Provable (a partner of Eidoo which integrated with Chai within 6 hours of the its release). We also had three people from Monolith (formerly TokenCard, which offers one of the first non-custodial wallet with credit card for crypto off ramp) who offered drinks, food, and the great space to spend almost 4 hours of intensive discussion.

In this blog post, we will try to replicate the conversation of the night by going through the the experimental branch we worked on that night.

  • Integrating Chai, the easy way
  • Dapp earning interest
  • MyChai
  • Including Chai into your codebase
  • Chai vs rDAI
  • Summary

Integrating Chai, the easy way

If you just simply want to let users stake Chai as a commitment, you actually don’t have to change your smart contract at all. Our smart contract already allows any ERC20 tokens so all you need to do is to let event organiser choose Chai as a default currency.

If I understand correctly the support Eidoo made was to simply allow users to convert Dai to Chai.

Dapp earning interests

What we want to do however is for our dapp to earn interest. To do so, you have to separate interest from Chai and send the only principles back to attendees while sending the interest to a beneficiary, like the event organiser or the dapp creator. Let me show you how it looks like in our test code.

this.register (line 89) is a function that allows you to commit when you RSVP.

When the event organiser (owner) calls finalize(line 90), it decides who checked in and who is not.

After that, the participant (in this case only the organiser) can call withdraw (line 91) his/her portion.

At this moment, the event contract will be empty, but thanks to the power of Chai, conference.totalDaiBalance() (line 95) shows that it earned something extra, hence totalDaiBalance.gt(0) returns true(line 96).

If our smartcontract keeps the interest forever, no one can get hold of the earned interest. In this example, we modified clear (line 97) function so that the event organiser can withdraw the Dai (after one week cooling period). In reality, we will probably add a beneficiary account which could be either just a multisig owned by Kickback, or it could be to a DAO if we want to let the community decide how to spend that money.

MyChai

Chai itself is a single solidity file with no dependencies so you can just copy and paste it into your local directory. The problem when testing is that the Chai depends on DSR contracts (the infamous short names Vat, Pot, Join, and Gem) so the actual Chai contract won’t return any interests unless you also include the rest of MakerDAO’s codebase. In this example, we created a fake Chai called MyChai.sol which mimics the behavior of Chai. When we put this code into production, we should define the interface of ChaiInterface.sol and should import that instead of MyChai.sol

This fake chai contract shows you which functions you need to call from your contract.

We set an arbitrary 2 % interest rate (line 14). You can send a transaction to dai() (line 39) to enquire about the latest amount the usr address owns. It is important to know that this is a state-changing activity hence you cannot just call to get the latest state.

join(dai) (line 26) will convert Dai into Chai, and draw(wad)(line 32)(wad represents the type of a decimal number with 18 decimals of precision, used for token quantities) will convert chai back to Dai.

Chai has another function called exit(wad). What is the difference?

The important thing you need to be aware of is that chai = dai + interest.

If you want to return Dai + interest, then you call exit(chai). If you want to withdraw the exact amount of Dai you specified when joining (leaving interest part behind), then call draw(dai)

Including Chai into your codebase

We have AbstractConference.sol which defines generic functions such as reserve, finalize, withdraw, and ERC20Conference.sol which implements ERC20 specific functions such as doDeposit, and doDeposit.

Let’s see the code diff of ERC20Conference.sol first.

At the constructor, we now pass chaiAddress. We added require(chai.daiToken() == token); to make sure that the token address we use is the one Chai references as Dai.

You can see that we call chai.join at deposit time and chai.draw at withdraw time. We also created updateDaiBalance and stores the output into totalDaiBalance. Bear in mind that this figure includes the amount including interests earned by Chai, which is different from our totalBalance .

Let’s now go to AbstractConference.sol

You can notice that we added totalDeposits to keep track of the increment and decrement of the staked amount. Previously we were able to track it by just calling token.balanceOf(address). Now that all the Dai token is converted to Chai and chai.balanceOf(address) does not necessarily returns the deposit amount, we have to keep track of the balance on our own (we decided to add a new variable called totalDeposits rather than replacing totalBalance function for now as changing it will compiler to fail unless we make more changes into the code).

Chai vs rDAI

Previously clear was used to send any leftover of the commitment which have not been withdrawn during the cooling-off period (we are going to add a functionality to send them back rather than the event organiser clear it all).

In this example, however, it is now checking the leftover balance including interests via updateDaiBalance function and send them back all to the organiser. If we want to add a beneficiary, then we also need to pass beneficiaryAddress to the constructor. If you want to change the amount or the destination address, you have to code the logic as well.

rDAI provides similar functionalities to Chai but using cDAI of Compound which has different risk profile. The interest rate of Compound is usually higher than DSR but it has additional counter party risk and liquidity risk.

If everyone tries to exchange cDai or rDai for Dai at the same time, there may not enough to do so. You don’t actually lose the money, but your transaction (In our use case withdraw and clear ) may fail. This may not be so much of the issue beyond minor inconvenience but some Dapp may have consequence if users cannot withdraw when they want to.

So far I only mentioned the downside of using rDAI (or rather the risk of cDAI), but there are some benefits apart from earning higher interest.

rDAI can simulate the real-time total balance of accumulated DAI value (DAI + interest) without sending transactions. You also do not need to keep track of totalDeposits at line 79 and 99. In essence, rDAI does the hard work of separating interest and principles.

rDAI also has a notion of Hat which allows you to set the beneficiary addresses and proportion to divides the interest earned. This means I don’t have to code the logic myself.

In the mission of the rToken project, it says that rDAI could support multiple currencies such as ETH 2.0 or USDC so they should be able to create to not only adding different risk profile to cDAI but the Dapps can take advantage of the hat mechanism as well.

Summary

As you have seen, the integration of Chai is not so hard. To make it production worthy, we still need to do the following though (we may add this to our list of Gitcoin global communities virtual hackathon bounties).

  • Rename totalDeposits to totalBalance.
  • Add a beneficiary address.
  • Define Chai interface and import it to ERC20Conference rather than importing MyChai
  • Rename ERC20* to DAI* as it now has a dependency on DAI (or do not call chai functions unless chai.daiToken() == token
  • Modify deployment script to point to correct Chai address depending on the deploying network.

More importantly, Chai itself is not audited yet so we have to assess the risk of using unaudited external contracts.

(Thank you very much for Lev and Fran to review my blog post.)

wearekickback

Kickback

makoto_inoue

Written by

http://makoto.github.io/me

wearekickback

Kickback

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade