Flash Loan Counter-Exploit

close button

Bank Loan Theft

Download quests in Questplay

View the contracts and any other additional content from your IDE.

By cergyk.eth

The banking house runs a simple loan system. Customers can deposit gold as collateral to withdraw coin. Specifically, a customer has to deposit 1000 $GLD tokens to draw a $COIN loan. The deposited $GLD should only be withdrawable if the customer repays the loan.

A thief has cleverly bypassed the bank's loan system, robbing the bank's $COIN balance. Can you figure out how to recover the stolen funds?

Your Task

Find out how the coins in BankingHouse have been stolen and recover it back from the thief. To pass this quest:

  1. The thief should have a $COIN balance of 0.

  2. The BankingHouse contract should have its original $COIN balance back.

Contract Code

// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/interfaces/IERC20.sol"; contract BankingHouse { bytes32 constant PASSWORD_HASH = 0x3a1cf401d67c55af5baa816bfb6f886bdb7ff802e35c98333ce62b2843dfdbb2; uint256 constant REQUIRED_COLLATERAL = 1000 ether; IERC20 public immutable gold; IERC20 public immutable coin; bool public paused; mapping(address => uint256) public deposits; mapping(address => int256) public loans; constructor(IERC20 _gold, IERC20 _coin) { gold = _gold; coin = _coin; } function deposit(uint256 amount) external { if (paused) { return; } gold.transferFrom(msg.sender, address(this), amount); deposits[msg.sender] += amount; } function transactCoin(int256 amount) external { if (amount > 0) { require( deposits[msg.sender] >= REQUIRED_COLLATERAL, "Borrower has insufficient gold collateral" ); loans[msg.sender] += amount; coin.transfer(msg.sender, uint256(amount)); } else { loans[msg.sender] += amount; coin.transferFrom( msg.sender, address(this), uint256(-amount) ); } } function withdraw(uint256 amount) external { if (paused || loans[msg.sender] > 0) { return; } deposits[msg.sender] -= amount; gold.transfer(msg.sender, amount); } function resetLoan( address borrower, string calldata password ) external { require(keccak256(bytes(password)) == PASSWORD_HASH); loans[borrower] = 0; } function setPaused( bool _paused, string calldata password ) external { require(keccak256(bytes(password)) == PASSWORD_HASH); paused = _paused; } }

You settle in, pouring over the bank’s security system. Where did all the coin go?