Unstoppable | Damn Vulnerable DeFi #1 Walk-Through

There’s a tokenized vault with a million DVT tokens deposited. It’s offering flash loans for free, until the grace period ends.

To pass the challenge, make the vault stop offering flash loans.

You start with 10 DVT tokens in balance.

https://www.damnvulnerabledefi.xyz/challenges/unstoppable/

We are given two smart contracts ReceiverUnstoppable.sol and UnstoppableVault.sol and the unstoppable.challenge.js file to code our solution.

Let’s run our test file:

yarn run unstoppable

It should return the results in the console:

  [Challenge] Unstoppable
    ✔ Execution
    1) "after all" hook for "Execution"


  1 passing (2s)
  1 failing

  1) [Challenge] Unstoppable
       "after all" hook for "Execution":
     AssertionError: Expected transaction to be reverted

Let’s go ahead and open up the unstoppable.challenge.js file to make sense of these results.

Inside unstoppable.challenge.js

From the top, the test file deploys and funds the contracts involved.
The comment on Line 39 explains that the scenario shows it’s possible for someUser to executeFlashloan on receiverContract.
Line 47 gives us a place to code our solution.
Line 53 gives us the success condition; it’s no longer possible to execute a flash loan.

Now that we understand what the challenge is asking for we can start by checking out the receiverContract and executeFlashloan function.

    function executeFlashLoan(uint256 amount) external onlyOwner {
        address asset = address(pool.asset());
        pool.flashLoan(
        [. . . . . .]
        );
    }

The function uses pool to reference the UnstoppableVault contract and calls flashLoan.

Inside of the flashLoan function we can see these two lines that call a function totalAssets() to store the balance of the contract in balanceBefore.
Then requires the totalSupply to equal that balanceBefore.

Lines 95 & 96

Let’s peek into totalAssets()

    function totalAssets() public view override returns (uint256) {
        [. . . . . .]
        return asset.balanceOf(address(this));
    }

The function returns the balanceOf(address(this)) which is an ERC20 function that returns the contracts token balance.

balanceOf(address account) → uint256 external

Returns the amount of tokens owned by account.

https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#IERC20-balanceOf-address-

This is a problem because the token balance can be altered by sending tokens directly to the contract using the transfer function.

Let’s head to our challenge file and write up our solution.

Solution

Inside of the function provided for our solution we connect to our address player and user .transfer to send our balance.

        await token.connect(player).transfer(vault.address, INITIAL_PLAYER_TOKEN_BALANCE);

This should imbalance the pool and make the transaction revert at the comparison step we saw on Line 96 earlier:

        if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance(); // enforce ERC4626 requirement

Save your file and head back to the terminal to run your solution:

yarn run unstoppable

It should return the results in the terminal.

Success

Congratulations! We have successfully imbalanced the pool and stopped the Unstoppable Lender.

If you missed how to set up Damn Vulnerable DeFi, see yesterday’s installation tutorial post.
If you’re following along I will see you here tomorrow for challenge 2.

DAVE

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top