Skip to content
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

A better way to resolve custom error messages #2019

Open
mikemaccana opened this issue Jan 9, 2024 · 9 comments
Open

A better way to resolve custom error messages #2019

mikemaccana opened this issue Jan 9, 2024 · 9 comments
Labels
enhancement New feature or request

Comments

@mikemaccana
Copy link
Contributor

mikemaccana commented Jan 9, 2024

Motivation

A user is using web3.js, making transactions with instructions for the Token program. They recieve:

failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x10

Which actually means, per the Token program's errors:

This token mint cannot freeze accounts

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.

@mikemaccana mikemaccana added the enhancement New feature or request label Jan 9, 2024
@lorisleiva
Copy link
Collaborator

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:

  • The transaction object that failed to execute. This allows us to identify the failing instruction and correctly identify the program that caused the error.
  • An array of all 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);
}

@mikemaccana
Copy link
Contributor Author

mikemaccana commented Jan 9, 2024

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?

@lorisleiva
Copy link
Collaborator

lorisleiva commented Jan 10, 2024

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.

@mikemaccana
Copy link
Contributor Author

mikemaccana commented Jan 10, 2024

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]

@lorisleiva
Copy link
Collaborator

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).

@dtmrc
Copy link

dtmrc commented Feb 4, 2024

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.

@steveluscher
Copy link
Collaborator

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
        }
    }
}

@mlshv
Copy link

mlshv commented Apr 12, 2024

@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?

@buffalojoec
Copy link
Collaborator

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants