Closed mikemaccana closed 3 weeks ago
Hey Mike, we're working on it for the new Web3.js. ☺️
Have you seen this? [Copy/pasting the relevant section below].
These create program functions will be generated for each program and will allow us to transform an hex code into an actual program error.
resolveTransactionError()
This function takes a raw error caused by a transaction failure and attempts to resolve it into a custom program error.
For this to work, the resolveTransactionError
function also needs the following parameters:
transaction
object that failed to execute. This allows us to identify the failing instruction and correctly identify the program that caused the error.programs
that can be used to resolve the error. If the program that caused the error is not present in the array, the function won't be able to return a custom program error.Note that, if the error cannot be resolved into a custom program error, the original error is returned as-is.
// Store your programs.
const programs = [createSplSystemProgram(), createSplComputeBudgetProgram(), createSplAddressLookupTableProgram()];
try {
// Send and confirm your transaction.
} catch (error) {
throw resolveTransactionError(error, transaction, programs);
}
Great minds etc! 😃 Thanks @lorisleiva !
Would it be possible to allow resolveTransactionError()
to look up the program by ID and get the errors, removing the need for programs
?
Would it be possible to allow resolveTransactionError() to look up the program by ID and get the errors, removing the need for programs?
The resolveTransactionError
function already identifies the throwing program by ID and its custom error code. However we need a source of information where, given a program ID and an error code, we are provided with the custom program error that makes sense to the end user — namely, we need the name and message behind that error code.
That source of information here is being explicitly requested by the function as otherwise we would need a centralised registry of program error codes to fetch from. Is that what you are referring to?
P.S.: You might be interested in point [E] of the thread.
we would need a centralised registry of program error codes to fetch from. Is that what you are referring to?
Exactly! Ie if the IDL is published, we fetch the errors from the IDL.
MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD
error 0x0
is "Wrong reserve owner. Must be a system account" because https://explorer.solana.com/address/MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD/anchor-program says so.
If the IDL is not published, we tell this explicitly to the user and return Custom Program Error
and the hex number same as present.
Downsides: we have an HTTP request to resolve the error. Users could turn off custom errors handling if they wanted to to disable this behavior though.
Upsides: we have useful errors by default and don't ask users to provide/maintain a list of all the programs they want to use, or have something that recursively gets the programs another program may CPI to per [E]
Gotcha! I think having an additional asynchronous helper method that uses the Anchor IDL registry for that purpose makes total sense.
However, I do think we should keep the synchronous method for situations where we just want to use the information provided by the generated clients to avoid an extra HTTP call that may not even resolve.
I'd also like to explore a plugin ecosystem on top of the web3.js library that would help bind all the components together. For instance, this would be much easier to handle with a program repository plugin (which is how Umi handles this problem).
would it be possible to bubble custom Error
classes in the case where sendAndConfirmRawTransaction()
returns strings such as Raw transaction ${signature} failed ({"err":{"InstructionError":[2,{"Custom":30}]}})
, or failed to send transaction: Transaction simulation failed: Blockhash not found
.
it is less than ideal to have to parse message strings everywhere when implementing error handling. some of them are rpc/sending related, some might be network, some may or may not be program specific. for eg. the custom error class SolanaJSONRPCError
does not get exposed or bubble up to the client, so even though logic exists for error type handling, i have to then do a bunch of string fragment matching to re-type the error on each send invocation.
We now have first-class SolanaErrors
being thrown for custom program errors.
try {
// Something.
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE)) {
// Now TypeScript knows that you have e.context.logs and e.context.returnData and stuff.
// But also…
if (isSolanaError(e.cause, SOLANA_ERROR__INSTRUCTION_ERROR__CUSTOM)) {
// Now typescript has e.cause.context.code and e.cause.context.index
}
}
}
@steveluscher When is it going to be released? I see it's in the technical preview version, but not the stable one. I'd love to use the TP in my project, but I've only found one demo and there's no other examples or documentation. Am I missing something?
@mlshv Have you checked out this README in the main library? It's a little hidden, but we've kept that up to date. https://github.com/solana-labs/solana-web3.js/blob/master/packages/library/README.md
Besides that, some of the packages' READMEs are detailed, and some aren't. Admittedly we don't have end-to-end docs like we'd want to have, but that's mainly because everything has been changing so much. Perhaps Stack Exchange can be a decent medium until then?
Also, we have published a "Technology Preview 2", which contains the custom errors, as well as the errors package itself. https://www.npmjs.com/package/@solana/errors
Errors are so much better now in the 2.0 line of web3.js, and the addition of isProgramError()
here rounds it out.
Specifically, addressing the use case in your original post @mikemaccana, codegenerated clients now have their own specialized version of isProgramError()
, like this.
If there's anything more to do here, specifically, feel free to describe it in a new issue!
@steveluscher you are a true gent. i am curious tho, how would you approach network related error handling vs program error handling? most of the variable conditions at the rpc & network layer tend to cause the most issues within the code and are relevant for the UI to handle gracefully
Using isSolanaError()
you can disambiguate between network errors and other kinds of errors.
try {
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
} catch(e) {
if (isSolanaError(e, SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR)) {
console.error('o no bad network', e.cause);
// Do something specific about an HTTP error, like retry.
} else {
throw e;
}
}
Because there has been no activity on this issue for 7 days since it was closed, it has been automatically locked. Please open a new issue if it requires a follow up.
Motivation
A user is using web3.js, making transactions with instructions for the
Token
program. They recieve:Which actually means, per the Token program's errors:
Example use case
This is a frequent show stopper for developers we've seen at Hacker Houses, where someone assumes there's no way to find an actual error. As a short term solution, DevRel added https://github.com/solana-developers/helpers?tab=readme-ov-file#getcustomerrormessage to our helpers library, but the same or better (and hopefully better is possible) solution should be available out of the box.
Details
Worst case: just have something like https://github.com/solana-developers/helpers?tab=readme-ov-file#getcustomerrormessage, code is at https://github.com/solana-developers/helpers/blob/main/src/index.ts#L14
Ideally: web3.js can dynamically fetch the errors for the specific program, and actually resolve the hex code to the real error message from the program as needed.