Exercise 6
Table of Contents:
Join the team on Slack at: https://slack.empirehacking.nyc/ #ethereum
Setup
- Clone the repository:
git clone https://github.com/crytic/damn-vulnerable-defi-echidna - Install the dependencies with
yarn install.
Context
The challenge is described here: https://www.damnvulnerabledefi.xyz/challenges/1.html. We assume that the reader is familiar with it.
Goals
- Set up the testing environment with the appropriate contracts and necessary balances.
- Analyze the "before" function in
test/unstoppable/unstoppable.challenge.jsto identify the initial setup required. - Add a property to check whether
UnstoppableLendercan always provide flash loans. - Create a
config.yamlfile with the required configuration option(s). - Once Echidna finds the bug, fix the issue, and retry your property with Echidna.
Only the following contracts are relevant:
contracts/DamnValuableToken.solcontracts/unstoppable/UnstoppableLender.solcontracts/unstoppable/ReceiverUnstoppable.sol
Hints
We recommend trying without reading the following hints first. The hints are in the hints branch.
- The invariant we are looking for is "Flash loans can always be made".
- Read what the allContracts option is.
- The
receiveTokenscallback function must be implemented. - A template is provided in contracts/unstoppable/UnstoppableEchidna.sol.
- A configuration file is provided in unstoppable.yaml.
Solution
This solution can be found in the solutions branch.
Solution Explained (spoilers ahead)
Note: Please ensure that you have placed solution.sol (or UnstoppableEchidna.sol) in contracts/unstoppable.
The goal of the unstoppable challenge is to recognize that UnstoppableLender has two modes of tracking its balance: poolBalance and damnValuableToken.balanceOf(address(this)).
poolBalance is increased when someone calls depositTokens().
However, a user can call damnValuableToken.transfer() directly and increase the balanceOf(address(this)) without increasing poolBalance.
Now, the two balance trackers are out of sync.
When Echidna calls pool.flashLoan(10), the assertion assert(poolBalance == balanceBefore) in UnstoppableLender will fail, and the pool can no longer provide flash loans.
See the example output below from Echidna:
echidna . --contract UnstoppableEchidna --config unstoppable.yaml
...
echidna_testFlashLoan: failed!💥
Call sequence:
transfer(0x62d69f6867a0a084c6d313943dc22023bc263691,1296000)
...