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.senderduring execution, which allows you to simulate the call from any address, such as Vitalik.value: themsg.valueduring 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
providerandwalletobjects. -
Create the
DAIcontract object. Note that we use theproviderinstead of thewalletto instantiate the contract, otherwisefromcannot be modified in thestaticCallmethod (this may be a bug or a feature). -
Check the
DAIbalance in the wallet, which should be 0, and check for Vitalik's balance too
-
Use
staticCallto call thetransfer()function and set thefromparameter as the address of Vitalik to simulate a transfer of10000 DAIfrom Vitalik. This transaction will succeed because Vitalik's wallet has sufficientDAI.
-
Use
staticCallto call thetransfer()function and set thefromparameter 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?