⚔️Draining the Naive Receiver

The learning process will be more beneficial for you if you will avoid the hints.

However, if you are frustrated, open these by order of frustration:

Hint 1

Understand through the code the relation in which NaiveReceiverLenderPool provides flash loans while FlashLoanReceiver borrows flash loans from the pool.

The two contracts interact directly through smart contract function calls.

The NaiveReceiverLenderPool calls the FlashLoanReceiver's onFlashLoan function, and the FlashLoanReceiver sends the repayment back to the NaiveReceiverLenderPool.

Hint 2

The vulnerability resides as an access control issue in the FlashLoanReceiver::onFlashLoan implementation.

function onFlashLoan(
    address token,
    uint256 amount,
    uint256 fee,
    bytes calldata
) external returns(bytes32) {
        assembly { // gas savings
        if iszero(eq(sload(pool.slot), caller())) {
            mstore(0x00, 0x48f5c3ed)
            revert(0x1c, 0x04)

    if (token != ETH)
            revert UnsupportedCurrency();
        uint256 amountToBeRepaid;
        unchecked {
        amountToBeRepaid = amount + fee;


    // Return funds to pool
    SafeTransferLib.safeTransferETH(pool, amountToBeRepaid);

    return keccak256("ERC3156FlashBorrower.onFlashLoan");

The FlashLoanReceiver contract implements a function that is to be called by the NaiveReceiverLenderPool, and completely ignoring the first function parameter.

Hint 3

Since the user that implemented the FlashLoanReceiver contract forgot to validate the receiver parameter, another user of the NaiveReceiverLenderPool can deploy a malicious contract that triggers a request of a flash loan on behalf of the FlashLoanReceiver contract, draining it's funds into the pool.

Last updated