{"id":22352237,"url":"https://github.com/superical/ethers-decode-error","last_synced_at":"2025-07-01T13:06:44.555Z","repository":{"id":164032648,"uuid":"639450725","full_name":"superical/ethers-decode-error","owner":"superical","description":"A utility library for ethers.js to easily handle custom errors and extract the actual error message/reason from a smart contract transaction.","archived":false,"fork":false,"pushed_at":"2024-02-09T15:36:05.000Z","size":413,"stargazers_count":34,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-29T21:30:03.582Z","etag":null,"topics":["blockchain","decode-strings","error-handling","errors","ethereum","ethers","json-rpc","smart-contracts","solidity","solidity-contracts"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/superical.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-11T13:42:28.000Z","updated_at":"2024-10-18T06:46:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"a1a09b32-5ca1-4206-9033-2344d43bb038","html_url":"https://github.com/superical/ethers-decode-error","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superical%2Fethers-decode-error","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superical%2Fethers-decode-error/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superical%2Fethers-decode-error/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/superical%2Fethers-decode-error/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/superical","download_url":"https://codeload.github.com/superical/ethers-decode-error/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228106309,"owners_count":17870438,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","decode-strings","error-handling","errors","ethereum","ethers","json-rpc","smart-contracts","solidity","solidity-contracts"],"created_at":"2024-12-04T12:17:52.384Z","updated_at":"2024-12-04T12:17:53.048Z","avatar_url":"https://github.com/superical.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ethers-decode-error\n\n[![Release][gha-badge]][gha-ci] [![TypeScript version][ts-badge]][typescript-5-0]\n[![License: Apache 2.0][license-badge]][license]\n\n[gha-ci]: https://github.com/superical/ethers-decode-error/actions/workflows/release.yml\n[gha-badge]: https://github.com/superical/ethers-decode-error/actions/workflows/release.yml/badge.svg\n[ts-badge]: https://img.shields.io/badge/TypeScript-5.0-blue.svg\n[typescript-5-0]: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/\n[license-badge]: https://img.shields.io/badge/license-Apache_2.0-blue.svg\n[license]: https://github.com/superical/ethers-decode-error/blob/main/LICENSE\n\nFor those who've grappled with extracting the actual error message or reason from the JSON RPC when a transaction fails\nor a smart contract reverts, you'll certainly appreciate how cumbersome it could at times.\n\nThis is a simple utility library to help simplify the process of determining the actual errors from smart contract. You simply pass in the error object and you will get the actual error message and a bunch of other information about the error. It works with the regular revert errors, panic errors, Metamask rejection error and custom\nerrors.\n\n## Installation\n\n```bash\nnpm install ethers-decode-error --save\n```\n\nYou will need to install ethers.js in your project if you have not:\n\n```bash\nnpm install ethers@^6 --save\n```\n\n\u003e 💡 If you wish to use it with ethers v5 instead, you may want to refer to [v1](../../tree/1.x).\n\n## Usage\n\nThis library decodes an ethers error object reverted from a smart contract into results that lets you decide the best course of action from there.\n\nStart by creating an instance of the `ErrorDecoder`:\n\n```typescript\nimport { ErrorDecoder } from 'ethers-decode-error'\n\nconst errorDecoder = ErrorDecoder.create()\n```\n\nThe `create` method optionally accepts an array of ABI or contract interface objects as its first argument. Although the ABI is not required for regular reverts, it's recommended to supply the ABI or contract interfaces if you're expecting custom errors. See the examples in [Custom Error ABI and Interfaces](#custom-error-abi-and-interfaces) section for more details.\n\nAfter creating the instance, you can reuse the `decode` method throughout your code to handle any errors thrown when interacting with smart contracts:\n\n```typescript\nimport type { DecodedError } from 'ethers-decode-error'\n\ntry {\n  // Send a transaction that will revert\n} catch (err) {\n  const decodedError: DecodedError = await errorDecoder.decode(err)\n  console.log(`Revert reason: ${decodedError.reason}`)\n}\n```\n\n### Decoded Error\n\nThe `DecodedError` object is the result of the decoded error, which contains the following properties for handling the error occurred:\n\n| Property    | Value Type       | Remarks                                                                                                                                                                                                                                    |\n| ----------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `type`      | `ErrorType`      | The type of the error. See [Error Types](#error-types).                                                                                                                                                                                    |\n| `reason`    | `string \\| null` | The decoded error message, or `null` if error is unknown or has no message.                                                                                                                                                                |\n| `data`      | `string \\| null` | The raw data bytes returned from the contract error, or `null` if error is unknown or empty.                                                                                                                                               |\n| `args`      | `Array`          | The parameter values of the error if exists. For custom errors, the `args` will always be empty if no ABI or interface is supplied for decoding.                                                                                           |\n| `name`      | `string \\| null` | The name of the error. This can be used to identify the custom error emitted. If no ABI is supplied for custom error, this will be the selector hex. If error is `RpcError`, this will be the error code. `null` if error is `EmptyError`. |\n| `selector`  | `string \\| null` | The hexidecimal value of the selector. `null` if error is `EmptyError`.                                                                                                                                                                    |\n| `signature` | `string \\| null` | The signature of the error. `null` if error is `EmptyError` or no specified ABI for custom error.                                                                                                                                          |\n| `fragment`  | `string \\| null` | The ABI fragment of the error. `null` if error is `EmptyError` or no specified ABI for custom error.                                                                                                                                       |\n\n### Error Types\n\nThese are the possible `ErrorType` that could be returned as the `type` property in the `DecodedError` object:\n\n| Type                        | Description                               |\n| --------------------------- | ----------------------------------------- |\n| `ErrorType.EmptyError`      | Contract reverted without reason provided |\n| `ErrorType.RevertError`     | Contract reverted with reason provided    |\n| `ErrorType.PanicError`      | Contract reverted due to a panic error    |\n| `ErrorType.CustomError`     | Contract reverted due to a custom error   |\n| `ErrorType.UserRejectError` | User rejected the transaction             |\n| `ErrorType.RpcError`        | An error from the JSON RPC                |\n| `ErrorType.UnknownError`    | An unknown error was thrown               |\n\n## Examples\n\n### Revert/Require Errors\n\n```typescript\nimport { ErrorDecoder } from 'ethers-decode-error'\n\nconst errorDecoder = ErrorDecoder.create()\n\nconst WETH = new ethers.Contract('0xC02aa...756Cc2', abi, provider)\ntry {\n  const tx = await WETH.transfer('0x0', amount)\n  await tx.wait()\n} catch (err) {\n  const { reason, type } = await errorDecoder.decode(err)\n\n  // Prints \"ERC20: transfer to the zero address\"\n  console.log('Revert reason:', reason)\n  // Prints \"true\"\n  console.log(type === ErrorType.RevertError)\n}\n```\n\n### Panic Errors\n\n```typescript\nimport { ErrorDecoder } from 'ethers-decode-error'\n\nconst errorDecoder = ErrorDecoder.create()\n\nconst OverflowContract = new ethers.Contract('0x12345678', abi, provider)\ntry {\n  const tx = await OverflowContract.add(123)\n  await tx.wait()\n} catch (err) {\n  const { reason, type } = await errorDecoder.decode(err)\n\n  // Prints \"Arithmetic operation underflowed or overflowed outside of an unchecked block\"\n  console.log('Panic message:', reason)\n  // Prints \"true\"\n  console.log(type === ErrorType.PanicError)\n}\n```\n\n### Custom Errors\n\n```typescript\nimport { ErrorDecoder } from 'ethers-decode-error'\nimport type { DecodedError } from 'ethers-decode-error'\n\nconst abi = [\n  {\n    inputs: [\n      {\n        internalType: 'address',\n        name: 'token',\n        type: 'address',\n      },\n    ],\n    name: 'InvalidSwapToken',\n    type: 'error',\n  },\n]\nconst errorDecoder = ErrorDecoder.create([abi])\n\nconst MyCustomErrorContract = new ethers.Contract('0x12345678', abi, provider)\ntry {\n  const tx = await MyCustomErrorContract.swap('0xabcd', 123)\n  await tx.wait()\n} catch (err) {\n  const decodedError = await errorDecoder.decode(err)\n  const reason = customReasonMapper(decodedError)\n\n  // Prints \"Invalid swap with token contract address 0xabcd.\"\n  console.log('Custom error reason:', reason)\n  // Prints \"true\"\n  console.log(type === ErrorType.CustomError)\n}\n\nconst customReasonMapper = ({ name, args, reason }: DecodedError): string =\u003e {\n  switch (name) {\n    case 'InvalidSwapToken':\n      // You can access the error parameters using their index:\n      return `Invalid swap with token contract address ${args[0]}.`\n      // Or, you could also access the error parameters using their names:\n      return `Invalid swap with token contract address ${args['token']}.`\n\n    // You can map any other custom errors here\n\n    default:\n      // This handles the non-custom errors\n      return reason ?? 'An error has occurred'\n  }\n}\n```\n\n#### Custom Error ABI and Interfaces\n\nAlthough the ABI or ethers `Interface` object of the contract is not required when decoding normal revert errors, it is recommended to provide it if you're expecting custom errors. This is because the ABI or `Interface` object is needed to decode the custom error name and parameters.\n\n\u003e 💡 It's much more convenient to supply the ABIs and Interface objects for all smart contracts your application may interact with when creating the `ErrorDecoder` instance. You will then only need a single `ErrorDecoder` instance that you can reuse across your codebase to handle any smart contract errors.\n\nIf you're expecting custom errors from multiple contracts or from external contracts called within your contract, you can provide the ABIs or interfaces of those contracts:\n\n```typescript\nconst myContractAbi = [...]\nconst externalContractAbi = [...]\n\n// From here on, the errorDecoder is aware of all the custom errors throw from these contracts.\nconst errorDecoder = ErrorDecoder.create([myContractAbi, externalContractAbi])\n\ntry {...} catch (err) {\n  // It's aware of errors from MyContract, ExternalContract and ExternalContract errors emitted from MyContract.\n  const decodedError = await errorDecoder.decode(err)\n  // ...\n}\n```\n\nIf you are using TypeChain in your project, it may be more convenient to pass the contract `Interface` objects directly:\n\n```typescript\n// If you have the contract instances, you can access their `interface` property\nconst errorDecoder = ErrorDecoder.create([MyContract.interface, MySecondContract.interface])\n\n// Otherwise, you can use the `createInterface` method from the contract factory\nconst errorDecoder = ErrorDecoder.create([\n  MyContract__factory.createInterface(),\n  MySecondContract__factory.createInterface(),\n])\n```\n\nYou can also mix both ABIs and contract `Interface` objects, and the library will sort out the ABIs for you. This can be useful if you just want to append adhoc ABI of external contracts so that their errors can be recognised when decoding:\n\n```typescript\nconst externalContractFullAbi = [...]\nconst anotherExternalContractErrorOnlyAbi = [{\n    name: 'ExternalContractCustomError1',\n    type: 'error',\n}]\n\nconst errorDecoder = ErrorDecoder.create([MyContract__factory.createInterface(), externalContractFullAbi, anotherExternalContractErrorOnlyAbi])\n```\n\nIf the ABI of a custom error is not provided, the error name will be the selector of the custom error. In that case, you can check the selector of the error name in your reason mapper to handle the error accordingly:\n\n```typescript\nconst customReasonMapper = ({ name, args, reason }: DecodedError): string =\u003e {\n  switch (name) {\n    // For custom errors with ABI, you can check the error name directly\n    case 'InvalidSwapToken':\n      return `Invalid swap with token contract address ${args[0]}.`\n\n    // For custom errors without ABI, you'll have to check the error name against the selector\n    // Note that when ABI is not provided, the `args` will always be empty even if the custom error has parameters.\n    case '0xec7240f7':\n      return 'This is a custom error caught without its ABI provided.'\n\n    default:\n      return reason ?? 'An error has occurred'\n  }\n}\n```\n\n## Contributing\n\nFeel free to open an issue or PR for any bugs/improvements.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperical%2Fethers-decode-error","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuperical%2Fethers-decode-error","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperical%2Fethers-decode-error/lists"}