rtoken-project / rtoken-monorepo

RToken Ethereum monorepo
https://rdai.money
89 stars 43 forks source link

New York Blockchain Week Hackathon GitHub Prize: Write rDAI allocation strategy for Aave #64

Closed Bobface closed 3 years ago

Bobface commented 4 years ago

Aave allocation strategy for the rDai contract

This PR is a submission to the mentioned hackathon and introduces support for the Aave protocol as an allocation strategy.

New Contracts

New Tests

contracts/test/RDaiWithAave: Contains tests for rDai with the Aave allocation strategy similar to the existing tests for the Compound allocation strategy

Implementation details of AaveAllocationStrategy

From the outside, the implementation can be used exactly the same as the existing Compound allocation strategy. However, the implementation has a few differences:

A key difference between Compound and Aave is that Compound models interest accrual by increasing the value of cTokens meaning that if I hold X amount of cTokens I can later swap this amount back to a larger amount of the underlying asset. Aave on the other hand models interest accrual by increasing my balance - the exchange rate between aToken <-> underlying is always 1:1 but if I hold aTokens my balance steadily increases.

For the implementation this means:

accrueInterest does not implement any behaviour, it simply returns true. There is no explicit interest accrual mechanism in Aave.

exchangeRateStored calculates the exchange rate from aToken to underlying as the total balance of aTokens divided by the total invested underlying amount: rate = (aToken.balanceOf(address(this)) * 10**18) / totalInvested;. Using this calculation we do not need to modify the existing main rDai contract. However, the allocation strategy needs to keep track of the total amount of invested Dai. Thus a new state variable totalInvested is introduced which is increased when investing and decreased when redeeming. When investing or redeeming from Aave we need to modify the totalInvested variable in a way which will keep the exchange rate unchanged. We can do so by using the formula totalInvested = (aTotalAfter * totalInvested) / aTotalBefore. To deduce this formula:

// We want to keep the exchange rate while updating the totalInvested. We calculate the newTotalInvested value we need to have the same exchange rate as before

oldExchangeRate = newExchangeRate
oldATokenBalance / oldTotalInvested = newATokenBalance / newTotalInvested      // solve for newTotalInvested
newATokenBalance  / (oldATokenBalance / oldTotalInvested) = newTotalInvested
newTotalInvested = (newATokenBalance * oldTotalInvested) / oldATokenBalance

Kovan deployed addresses

Note: The Kovan Dai listed below is not the "official" MakerDAO Kovan Dai, it is Aaves own Dai variant with a faucet which is used by Aave on Kovan.)

Test transactions (see kovan.etherscan.io)

Example three user interaction

3 user interaction

Video Demonstration

rDai with Aave Demo on Youtube

Further questions?

Feel free to contact me in this PR or also on Discord (Bobface#9040 - I am on the rDai Discord server)

pi0neerpat commented 4 years ago

Documenting how to test:

  1. Build core contracts with yarn build
  2. Add contracts_directory: "aave" in truffle-config.js and run truffle migrate to build the new Aave contracts.
  3. Remove the line you just added in truffle-config.js
  4. Run truffle test test/RTokenWithAave.test.js (note: yarn test isn't currently injecting mocha dependencies, maybe due to the decentral.ee/web3-test-helpers being used instead of the truffle test suite?
Bobface commented 4 years ago

@hellwolf I am not sure what makes the pipeline failed. @pi0neerpat suggested it might be caused by using yarn test and not truffle test because truffle injects mocha dependencies into the tests which are required for expect. yarn test does not seem to do that for some reason.

Also, the contracts in aave/* are not compiled by the default configuration and thus we get "Could not find artifact" errors. The truffle documentation says it is possible to supply a contracts_directory in truffle-config.js with a regex to match directories however I did not get it to work that way. Maybe you know how we can fix that?

Bobface commented 4 years ago

@hellwolf @pi0neerpat The tests in the CI are now working. What I have done:

  1. Edit truffle-config.js to use a glob for the contract directories: contracts_directory: "./{aave,compound,contracts}/**/*.sol". This way all contracts are compiled

  2. Import expect in RTokenWithAave.test.js directly to not have eslint errors: const expect = require("chai").expect;

  3. Renamed some of Aaves contracts because they had the same name as existing contracts from Compound and rDai which caused the Truffle artifacts to become overwritten.

The last thing which makes the coverage test fail is that, for some reason, the glob I have mentioned is not expanded by solidity-coverage and fails with this error:

ls: no such file or directory: /home/runner/work/rtoken-monorepo/rtoken-monorepo/packages/contracts/{aave,compound,contracts}/**/*.sol/**/*.sol

As you can see the glob in raw form is in the path. Edit: One possible solution to that problem would be to move the aave and compound folder into the contracts folder. This way we can get rid of the glob and use Truffles default behaviour. Let me know what you think.

Note: @pi0neerpat the method to test the contracts you have documented above should not be required anymore and testing should work out of the box with yarn test.

Bobface commented 4 years ago

I have added a commit which moves the folders into the contracts directory as described above and the pipeline is working fine now. If this is not desired I can revert the commit again.