The previous article Solidity - Custom Error mentioned how to use custom error in smart contracts. This article continues this topic and explains how to use Web3.js to parse custom error when call, estimateGas and transaction failed. Web3.js version 4.0.1 is used here, tested at Arbitrum Goerli.
// Parsed data will appear console.log(JSON.stringify(e.innerError));
Transaction failed
Next is the processing after sending the transaction
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
try { // Using private key to sign the transaction const data = contract.methods.sendEmptyError().encodeABI(); const signedTx = await web3.eth.accounts.signTransaction({ // .... }, privateKey);
// Since estimateGas will be executed first by default and can't send. Skip here for testing await web3.eth.sendSignedTransaction(signedTx.rawTransaction, undefined, { checkRevertBeforeSending: false }); } catch (e: any) { // transaction failed if (e.receipt && !e.receipt.status) { // start parsing... } }
At this time, we will find that neither the tx object nor the receipt has any available data. At this time, we can only use a strange way to achieve.
if (e.receipt && !e.receipt.status) { try { const tx = await web3.eth.getTransaction(e.receipt.transactionHash); constrequest: any = { }; ['to', 'from', 'nonce', 'gas', 'gasPrice', 'maxPriorityFeePerGas', 'maxFeePerGas', 'data', 'value', 'chainId', 'type', 'accessList'].forEach((key) => { request[key] = (tx as any)[key]; }); if (request.gasPrice) { delete request.maxPriorityFeePerGas; delete request.maxFeePerGas; } // Specify the node to simulate the state of a block number await web3.eth.call(request, tx.blockNumber); } catch (e2) { // will give the same result as estimateGas
// processing can be done in the above way decodeContractErrorData(contract._errorsInterface, e2.innerError); console.log(JSON.stringify(e2.innerError)); } }
It should be noted that if blockTag is specified as a block number, there is a time limit. Bblocks that are too long ago cannot be executed.
Browser Provider
This section is to use something like Metamask. For example:
1
newWeb3(window.ethereum);
call
Unlike the built-in provider, there is no innerError object available.
// Parsed data will appear console.log(JSON.stringify(e.innerError));
estimateGas
The behavior and processing method of estimateGas is exactly the same as that of the call above.
Transaction failed
Next is the processing after sending the transaction
1 2 3 4 5 6 7 8 9
try { // Use send directly. Since estimateGas cannot be executed, set a gas so that it can send. await contract.methods.sendEmptyError().send({ from, gas: '1000000' }); } catch (e: any) { // transaction failed if (e.receipt && !e.receipt.status) { // start parsing... } }
After entering the catch, use the method of the previous transaction failed section, and create innerError object first.