I've been revisiting ethers.js
recently to refresh my understanding of the details and to write a simple tutorial called "WTF Ethers" for beginners.
Twitter: @0xAA_Science
Community: Website wtf.academy | WTF Solidity | discord | WeChat Group Application
All the code and tutorials are open-sourced on GitHub: github.com/WTFAcademy/WTF-Ethers
In this lesson, we will introduce the staticCall
method of contract classes, which allows you to check whether a transaction will fail before sending it, saving a significant amount of gas.
The staticCall
method is a method available in the ethers.Contract
class, and other similar methods include populateTransaction
and estimateGas
.
Transactions that Could Fail
Sending transactions on Ethereum requires expensive gas
fees and carries the risk of failure. Failed transactions do not refund the gas fees. Therefore, it is crucial to know which transactions will fail before sending them. If you have used the MetaMask browser extension, you may be familiar with the following image.

If your transaction is likely to fail, MetaMask will inform you by showing the message "This transaction may fail." When users see this red warning message, they will cancel the transaction unless they want to experience the failure themselves.
How does MetaMask achieve this? This is because Ethereum nodes have an eth_call
method that allows users to simulate a transaction and return the possible transaction result without actually executing it on the blockchain (the transaction will not be mined).
staticCall
In ethers.js
, you can use the staticCall()
method of the contract
object to call the eth_call
method of an Ethereum node. If the call is successful, it returns true
; if it fails, an error is thrown, and the reason for the failure is returned. The method is used as follows:
functionName
: the name of the function you want to call.arguments
: the arguments for the function call.{override}
: optional, can include the following parameters:from
: themsg.sender
during execution, which allows you to simulate the call from any address, such as Vitalik.value
: themsg.value
during execution.blockTag
: the block height during execution.gasPrice
: The gas price for legacy networks.gasLimit
: The maximum amount of gas to allow this transaction to consume.nonce
: a nonce is a special number that is used to create a unique block
Simulating a DAI
Transfer with staticCall
-
Create
provider
andwallet
objects. -
Create the
DAI
contract object. Note that we use theprovider
instead of thewallet
to instantiate the contract, otherwisefrom
cannot be modified in thestaticCall
method (this may be a bug or a feature). -
Check the
DAI
balance in the wallet, which should be 0, and check for Vitalik's balance too -
Use
staticCall
to call thetransfer()
function and set thefrom
parameter as the address of Vitalik to simulate a transfer of10000 DAI
from Vitalik. This transaction will succeed because Vitalik's wallet has sufficientDAI
. -
Use
staticCall
to call thetransfer()
function and set thefrom
parameter as the address of the test wallet to simulate a transfer of10000 DAI
. This transaction will fail, throwing an error with the reasonDai/insufficient-balance
.
Summary
ethers.js
encapsulates eth_call
in the staticCall
method, making it convenient for developers to simulate transaction results and avoid sending transactions that may fail. We have demonstrated the use of staticCall
to simulate transfers from Vitalik and the test wallet. Of course, this method has many other applications, such as calculating transaction slippage for tokens. Use your imagination - where else can you apply staticCall
?