How to handle errors when sending a transaction using Ethers JS
In this tutorial, we are going to learn how to handle errors that might happen when you send a transaction using Ethers JS and JavaScript.
In this tutorial, we are going to learn how to handle errors that might happen when you send a transaction or call a smart contract function using Ethers JS and JavaScript.
When sending a transaction, it can fail for many reasons and you might not want to handle all the error cases the same way.
Here is an example of how to catch errors when sending a transaction:
const ethers = require("ethers")
const provider = new ethers.providers.Web3Provider(YOUR_PROVIDER_HERE)
const signer = provider.getSigner()
// Send Ethereum transaction
signer.sendTransaction({
to: "0x5F70Ddd9908B04f952b9cB2A6F8E4D451725ceDC",
value: ethers.utils.parseEther("0.1")
})
.then((transaction) => {
transaction.wait()
.then((receipt) => {
console.log("the transaction was successful")
})
.catch((error) => {
// handle errors here
if (error.code === "INSUFFICIENT_FUNDS") {
console.log("not enough funds to pay for gas fees")
}
if (error.code === "NETWORK_ERROR") {
console.log("could not validate transaction, check that you're on the right network")
}
if (error.code === "TRANSACTION_REPLACED") {
console.log("the transaction was replaced by another transaction with the same nonce")
}
})
})
.catch((error) => {
// at this point, it's the sendTransaction that failed, not the transaction itself
})
// Calling a smart contract "write" function
const myNft = new ethers.Contract(address, ABI, provider)
myNft.mint(1, { value: ethers.utils.parseEther("0.1") })
.then((transaction) => {
console.log("The transaction was successful")
// you can wait for more confirmations if you want or get the receipt here
})
.catch((err) => {
// need to simulate the transaction here to access a complete error object because err doesn't contain information, just an error message
myNft.callStatic.mint(1, { value: ethers.utils.parseEther("0.1") })
.catch((error) => {
// handle errors exactly like above
})
})
As you can see, it's pretty straightforward when sending a transaction using sendTransaction
but not when calling a smart contract function.
When calling sendTransaction
, the error object in the catch
block will have all the information you need (see below for more information) but when you call a smart contract function that creates a transaction, if that transaction fails, the error inside the catch
block will just be a stack trace.
To get information about the error, you need to simulate the transaction using the callStatic
property. This will run the transaction locally but not affect the blockchain and it doesn't cost any gas, it just simulates the transaction.
That simulation will return the same error object as sendTransaction
and that error object in sendTransaction
has the following properties:
code
: The error code that you can use in your code to know why the transaction failed (more about the error codes below)reason
: The reason why it failed (available for error codesCALL_EXCEPTION
andTRANSACTION_REPLACED
data
: A hexadecimal string containing information about the errortransaction
: An object containing information about the transaction that was sent
Now, depending on the error code, the error object will be slightly different. Here are all the possible error codes and what they mean:
CALL_EXCEPTION
: there was an error with the transaction on the blockchain. If you called a smart contract function, that means the smart contract reverted the transaction.
In that case, the error object has 3 more properties:
•reason
: contains the message sent by the smart contract when it reverted.
•method
: the signature of the function that was called (name, parameters and return value).
•args
: an array that contains the arguments you sent to the smart contract function.INSUFFICIENT_FUNDS
: the wallet that sends this transaction doesn't have enough funds to pay for the gas feesNETWORK_ERROR
: There was a network error and the transaction couldn't be validated. A common reason why it happens is because the chain ID is wrong (the transaction was sent on the wrong network)NONCE_EXPIRED
: Thenonce
of the transaction was already used in a previous transaction that is completed. You need to pass a highernonce
to the transaction to make it work.REPLACEMENT_UNDERPRICED
: That means you attempted to replace a pending transaction with this transaction by passing the samenonce
but you didn't pass enough gas for this transaction to work. Try again and pass more gas to the transaction.TRANSACTION_REPLACED
: This transaction has been replaced (in other words, cancelled) by another transaction you sent with the samenonce
and more gas.
In that case, the error object will have the following extra properties:
•hash
: the hash of the transaction that was replaced
•reason
: the reason why another transaction replaced this one. One of"repriced"
,"cancelled"
or"replaced"
.
•replacement
: the transaction that replaced this transaction, a TransactionResponse object
•receipt
: the receipt of the transaction that replaced this transaction, a TransactionReceipt object
•cancelled
: a boolean indicating if the transaction was cancelled or not, it is true ifreason
is"cancelled"
or"replaced"
but false ifreason
is"repriced"
UNPREDICTABLE_GAS_LIMIT
: The node that you sent the transaction to couldn't estimate the gas needed for this transaction. The solution is to pass a gas limit yourself when sending the transaction.
With all that information, you can now handle errors properly, depending on what happens, when sending transactions.
And that's it 🎉
Thank you for reading this article