-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expose revert data
field in TransactionException
for decoding CustomError
#2180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm not sure what network and node(live network, local network, or emulator like The reason why you get Regarding decoding a revert reason for a contract in interest, I think, the best would be like trying to decode revert reason into a // In Contract.java line 431
...
if (!(receipt instanceof EmptyTransactionReceipt)
&& receipt != null
&& !receipt.isStatusOK()) {
String revertReason = extractRevertReason(receipt, data, web3j, true, weiValue);
Optional<CustomError> customError = this.decodeRevertReasonIntoCustomError(revertReason);
if (customError.isPresent()) {
throw new ContractCustomErrorException(customError.get());
}
throw new TransactionException(
String.format(
"Transaction %s has failed with status: %s. "
+ "Gas used: %s. "
+ "Revert reason: '%s'.",
receipt.getTransactionHash(),
receipt.getStatus(),
receipt.getGasUsedRaw() != null
? receipt.getGasUsed().toString()
: "unknown",
revertReason),
receipt);
}
return receipt;
}
private Optional<CustomError> decodeRevertReasonIntoCustomError(String revertReason) {
return getCustomErrors().stream()
.filter(error -> revertReason.startsWith(CustomErrorEncoder.encode(error).substring(0, 10)))
.findFirst();
}
// This is to be implemented in the generated contract
// Returns all the CustomErrors defined in the generated contract
protected abstract List<CustomError> getCustomErrors();
// Exception specific to custom error case, so this can be caught when calling contract functions
class ContractCustomErrorException extends Exception {
private final CustomError customError;
public ContractCustomErrorException(CustomError customError) {
super( String.format("Transaction is reverted with custom error, %s", customError.getName()));
this.customError = customError;
}
public CustomError getCustomError() {
return customError;
}
}
...
.
.
.
// And then in client code
try {
contractInstance.withdraw(...params).send();
} catch (ContractCustomErrorException ex) {
CustomError error = ex.getCustomError();
// handle the error
} catch (TransactionException ex) {
// handle this
} |
TL;DR: I'm proposing that Web3J expose the Hi @psychoplasma, Thanks a lot for reviewing this and for your thorough explanation. I believe part of my original intent might have been misunderstood, so allow me to add a little more context. I'm using Web3J v4.14.0 and interacting with an Alchemy node (free tier). {
"jsonrpc": "2.0",
"id": 4,
"error": {
"code": 3,
"message": "execution reverted",
"data": "0xe2517d3f000000000000000000000000..." // selector + ABI-encoded params
}
} However, calling I'm not suggesting that Web3J should decode the The library only needs to expose the data field. Once available, client applications can use tools like What I suggest in simple terms is:
This doesn't alter current behavior — it's additive, safe, and very helpful in real-world scenarios. Now, regarding your design suggestion for Having a structured mechanism like that to match and throw decoded custom errors would definitely improve the developer experience. In fact, I think it aligns perfectly with the feature introduced in [PR #2173] and builds upon it very elegantly. But I’d like to emphasize that this proposal is not mutually exclusive with what I'm suggesting. The key differences are:
Without access to the That said, I’m happy to see you're open to this direction and would be glad to help explore how both ideas could be implemented cohesively. |
# Issue_title
Expose revert
data
field inTransactionException
class for decodingCustomError
Issue_description
Currently, when executing a transaction using
.send()
and it fails on-chain (e.g. due to arevert
), Web3J throws aTransactionException
. This exception provides access to the error message (execution reverted
) and optionally the transaction hash or receipt.However, it does not expose the
data
field from the JSON-RPC error response, which is critical for decoding custom errors (CustomError
) returned by the contract.Issue_context
Imagine calling a contract function with
.send()
and catching aTransactionException
.💡 Example
The actual JSON-RPC response from the node may include a field like
"data": "0x..."
which contains the ABI-encoded custom error selector and parameters.The
data
field contains the selector and ABI-encoded parameters of the custom error, which can be decoded using the new CustomError feature added in PR #2173.🧩 Problem
data
field is currently not mapped inorg.web3j.protocol.core.Response.Error
TransactionException
does not expose it eitherCustomError
revert reasons from.send()
✅ Proposed enhancement
Response.Error
to include adata
field (i.e., the full revert payload)TransactionException
to receive and expose that fielddata
, it is accessible from the exception✅ Optional
Consider adding utilities to:
CustomError
instances generated by Web3J wrappers🎯 Benefits
.send()
workflowseth_call
logic using HTTP clientsCustomError
added in recent PRs🏷 Suggested labels
enhancement
custom-error
revert-reason
ux-improvement
📎 Related
CustomError
The text was updated successfully, but these errors were encountered: