# Default Functions

In Contract Interactions, we were introduced to the idea of calling other smart contracts and in particular, the `call` function. Recall if we just want to send ether to an account, we can use the following code:

```solidity
account.call{value: 1}("");
```

When talking about the `call` function, we examined it from the perspective of the sender, but not from the perspective of the recipient. And as it turns out, there is *lots to talk about* if the recipient of a `call` function is a smart contract. In this section, we focus on *default functions* - functions that are automatically run whenever a target function is not specified.

### How Default Functions are Activated

Although we will not get into the specifics of how the calldata of the call function is organized, note that if we are calling a specific function, said function's id is contained within the calldata. Therefore, it is evident that if we pass in the empty string as the calldata for the call function, we are not specifying a specific function to call.&#x20;

### `receive` and `fallback`

In the case that we do not specify a function to call (and we are calling a smart contract), the following two scenarios can occur:

* The transaction reverts (occurs when a default function is not implemented)
* Execution is handled by a default function

What is a default function, you might ask? The following is the syntax for the two types of default functions that can be implemented in a smart contract:

```solidity
receieve() external payable {}
fallback() external {}
```

The first thing to notice about default functions is that they lack the `function` keyword; default functions are a special type of function and therefore, they are declared via the `receive` and `fallback` keywords.&#x20;

Now that we are aware of the two types of fallback functions, the next logical question to ask is how are the two functions different? The difference between `receive` and `fallback` lies in when they are called. Assume we are sending ether to a contract; the following logical chart explains what function is called (credit goes to [Solidity-by-Example](https://solidity-by-example.org/) for this):

* If we are passing in the empty string as calldata
  * `receive` is called *if it exists*
  * `fallback` is called *only if it is marked as payable*
* If we are **not** passing in the empty string as calldata
  * `fallback` is called *if it exists*

As for what default functions can do, recall that default functions are... functions and so anything that functions can do, default functions can do as well.

### The Security Nightmare of Default Functions

Although default functions appear mundane, their existence is the cause for one of the most common security vulnerabilities for smart contracts - the reentrancy attack. We will investigate the reentrancy attack in-depth when discussing smart contract security, but for now, we will discuss reentrancy attacks from a high level.

Assume we have a smart contract `Bank` which acts like a bank. Users can deposit and withdraw ether as they wish via the `deposit` and `withdraw` functions, respectively. Furthermore, the amount of ether a user has in the bank is tracked via a mapping variable.

{% code title="Bank.sol" %}

```solidity
contract Bank {

    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Not enough funds!");
        (bool success, ) = address(msg.sender).call{value: amount}("");
        require(success, "Sending ether failed");
        balances[msg.sender] -= amount;
    }

}
```

{% endcode %}

Now consider Eve, a malicious smart contract who wants to drain the bank of all its funds. Eve cannot change the logic of the smart contract, but she can dictate her own logic. Furthermore, Eve notices the following logical path of the `withdraw` function:

* The bank first checks if the user has enough funds to withdraw the desired amount
  * If not, the transaction reverts
* The bank sends the caller their desired funds
* The bank checks if the funds were able to be sent
  * If not, the transaction reverts
* The bank updates the balance of the caller to account for the withdraw

The most important thing to note here is that *the balance of the caller is updated **after** the funds are sent*. And once again, because Eve can dictate her own logic, she can dictate the logic of her `fallback` function. Therefore, if Eve is able to repeatedly call `withdraw` prior to her balance being updated by the bank, she is able to drain the bank of all its funds. This, in essence, is the reentrancy attack - looping through the execution of a function multiple times through default functions.&#x20;

{% code title="Eve.sol" %}

```solidity
contract Eve {

    Bank bank;

    function attack() public {
        bank.withdraw(1 ether);
    }

    fallback() external payable {
        if (address(bank).balance >= 1 ether) {
            bank.withdraw(1 ether);
        }
    }

}
```

{% endcode %}

Above is the logic that Eve would implement to conduct the reentrancy attack. In particular, there are two functions to consider here:

* `attack`: starts the reentrancy attack by calling `withdraw`
* `fallback`: executed whenever the bank sends the funds to Eve. It first checks if the bank has enough funds to be siphoned. If so, the function calls `withdraw`

The logical path of the entire reentrancy attack is described below:

* `attack` is called, which starts the reentrancy attack by calling `withdraw`
* `withdraw` executes until it calls Eve
* `fallback` is executed, and calls `withdraw`
* `withdraw` executes until it calls Eve
* `fallback` is executed, and calls `withdraw`
* ...
* `fallback` is executed; Eve sees that the bank no longer has enough funds to be siphoned and ends the reentrancy attack
* `withdraw` finishes the rest of its execution

### Resources

<https://solidity-by-example.org/sending-ether/>
