Default Functions
Calling Contracts w/o Calling Any 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:
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.
receive
and fallback
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:
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.
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 for this):
If we are passing in the empty string as calldata
receive
is called if it existsfallback
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.
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.
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 callingwithdraw
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 callswithdraw
The logical path of the entire reentrancy attack is described below:
attack
is called, which starts the reentrancy attack by callingwithdraw
withdraw
executes until it calls Evefallback
is executed, and callswithdraw
withdraw
executes until it calls Evefallback
is executed, and callswithdraw
...
fallback
is executed; Eve sees that the bank no longer has enough funds to be siphoned and ends the reentrancy attackwithdraw
finishes the rest of its execution
Resources
Last updated