Price volatility is the Achilles’ heel of a distributed application, or DApp for short. Nobody wants to deposit funds when Ether dipped 30% in the last month. And, while stablecoins like Dai can solve this issue, they have notoriously poor documentation, as we mentioned in our previous post. Furthermore, most existing DApps are already designed around Ether, which is relatively easy to process, and converting them to use DAI is an arduous and under-documented task.
The UX Problem
Using Dai also poses another issue: the user experience. If a smart contract only accepts Dai, new users will have to manually go to an exchange (of which there are few) to convert their Ether to Dai, then return to the DApp and deposit their Dai. Then, when taking out Dai, they have to manually convert it back to Ether to sell it for fiat on an exchange.
Oasis.Direct is the prime example of what the UX for a decentralized app could be — a one-click transaction converts your Ether to Dai, without any manual ordering or price setting. Moreover, by integrating it into Dai-accepting smart contracts, it has the potential to make them far easier to use.
Step 1: Connecting to Maker Contracts
First, let’s take a (slightly simplified) look at how the entrypoint for our
StudyManager contract looked like before Dai integration:
Ignore the other parameters, what matters is that it’s a
payable function to which the user can send Ether. That Ether is then held by the contract and recorded in the new study’s
To convert this function to accept Dai, we have to retool it a little:
We’ve changed the contract to look much like a function that accepts ERC20 tokens — which it should, since Dai is, at its heart, an ERC20 token.
However, there are two issues with this approach:
- The user still has to manually convert their Ether to Dai.
- The user has to manually call
dai.approve(YOUR_CONTRACT_ADDRESS, tokenAmount)before calling this function, thanks to the design of the ERC20 standard. This means that the user has to approve two transactions.
Thankfully, we can solve both of these issues simultaneously:
Step 2: Interfacing with OasisDEX
OasisDEX is a decentralized exchange that converts ETH to DAI, running on both the Kovan test network and the main network. Its contract addresses (under “market”), along with Dai’s Kovan and mainnet addresses, can be found in the source code repository.
By interacting with OasisDEX, we can simplify the user flow, automatically converting deposited Ether to Dai for long-term storage.
Let’s see an example from our code:
(Contract interfaces from Oasis.Direct’s code.)
Here, the contract does four things:
- It deposits the paid Ether into the WETH contract, which is needed to trade on OasisDEX. Our contract now owns msg.value in WETH.
- It gives OasisDEX approval to use as much WETH as it needs to trade it to Dai.
- It executes an automatic trade from WETH → DAI, requiring an outcome of at least minBuyAmt DAI or else the trade will fail.
- It creates the study as we did in the previous step.
Note that the function has to be passed in the “minimum buy amount” of Dai that they want; it specifies the minimum exchange rate, in effect. You can calculate this on the DApp end by calling (with ethers.js/web3):
Which you can do invisibly before sending the createStudy transaction, in this case.
Step 3: Mocking Maker
Now, there’s one downside with this approach: to test your contracts in the future, you’ll have to use the live OasisDEX contracts on Kovan or the main network. This obviously doesn’t play very well with automated testing like Truffle, as each test would require actual Kovan/mainnet Ether.
First, we tried a new way of testing: with the tool ganache-cli, you can actually do a virtual chain fork by connecting to a mainnet/Kovan node and effectively creating a development blockchain that starts with all the state of the live network. This would let you test cheaply, without incurring costs on the live network, but while still connecting to “real” MakerDAO contracts.
Unfortunately, we ran into issues where our contracts either failed to deploy or didn’t work properly with this setup, so ultimately we ditched it for a more traditional, if more annoying, approach.
Let’s deploy all the Maker contracts on the Truffle development network!
The full code is pretty long, but I’ve put it on a GitHub gist at the end of the post if you’d like to use it. Here are the major steps:
Deploys OasisDEX, WETH, and a FakeDAI contract (just a mintable ERC20 token) from /contracts/maker/.
Whitelists the WETH and FakeDAI pair for fake-trading. This is necessary to prevent the OasisDEX contract from erroring on “untrusted” token pairs.
Creates the WETH to DAI trading side of the exchange and the DAI to WETH side.
To do this, you have to deposit WETH, approve it for use by OasisDEX, and make a fake WETH → DAI offer at a fake exchange rate. For the other side, you have to mint some fake DAI, approve it for use by OasisDEX, and make a fake DAI → WETH offer.
This creates a marginally-functional OasisDEX contract on the development network which you can use to mock Dai trading.
And there you go! That’s how you convert a contract accepting Ether to one that seamlessly converts it to Dai for long-term storage.
This code was used in our product Delphus, a DApp revolutionizing the management of clinical data in scientific studies. Find out more there!
Other interesting links:
- MakerDAO TX Manager: batches Ethereum transactions
- OasisDirectProxy: how Oasis.Direct does it
- DSProxy: also composing transactions
Development Truffle migration script: