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.
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
.
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
externalReturns the amount of tokens owned by
https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#IERC20-balanceOf-address-account
.
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.
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.
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