Repository: ecadlabs/taquito (ts)

Issue:
Error improvements per packages part2
Error improvement of packages below
- [x] taquito-http-utils
- [x] taquito-contracts-library
- [x] taquito-beacon-wallet
- [x] taquito-ledger-signer
- [x] taquito-remote-signer

_Follow up of https://github.com/ecadlabs/taquito/issues/160_ as described in https://hackmd.io/@tca0T8LZT3au8LG6hSX20g/r1zTRTlfj_

Task 3 (should be split **a least** into one PR per package to ease the review): Go through all the errors in each package and:
 - Make the error extends one of these categories: https://github.com/ecadlabs/taquito/issues/1992
 - Ensure that the error message is informative (what went wrong?) Use complete but simple sentences
 - When there is a problem with a value received as a parameter, the erroneous value should be part of the error message and an indication of what is wrong with it. For example: `The key "inva2obfVMEuPUnadAConLWk7Tf4Dt3n4svSgJwrgpamRqJXvaYcg1" is invalid. It contains the invalid prefix "inva", but "p2sk" was expected.` Surround the variable in the error message with quotes for clarity.
 - Make sure the variables are displayed properly in the error message to avoid error messages like: `Failed to encode value '[object Object]'`. Add tests to ensure proper rendering.
 - Ensure that the errors are actionable (tell the users what they can do to resolve the issue)
 - If an error overrides another error, make sure that it includes the "original error" as a param.
 - Document the error with a typedoc comment
 - Add an error code to each error with an additional explanation on a documentation page of the Taquito website. Add a link to the documentation in error.
 - Collect all error classes and put them inside a single-consistently named file in each package. E.g. an `errors.ts` file in the root of each package's /src folder. It will increase the ease of extending and maintaining error classes in the future.
 - When a method/function can throw an error, include the tag [@throws](https://typedoc.org/tags/throws/) in the typedoc comment with an explanation of when the error can be thrown.
 - Make sure there is a follow-up issues for the next set of packages:
   - based on effort involved, create an issue per package or cover multiple packages per issue

Here is an example of an error that needs improvement:
- When querying a big map with Taquito, if the key is not passed properly, an Error is thrown saying Error: Unable to encode big map key; We need to provide more information on why Taquito is unable to encode it.
- https://github.com/ecadlabs/taquito/pull/1960/files#diff-fafad56e5e81807b06d4001d7aacd4843ddd7d4205e66908c4b43029d67cb0d4R130

Pull request description:
1994 leger signer and remote signer error improvement
closes #2505 
Thank you for your contribution to Taquito.

Before submitting this PR, please make sure:

- [x] Your code builds cleanly without any errors or warnings
- [x] You have run the linter against the changes
- [ ] You have added unit tests (if relevant/appropriate)
- [ ] You have added integration tests (if relevant/appropriate)
- [ ] All public methods or types have TypeDoc coverage with a complete description, and ideally an @example
- [ ] You have added or updated corresponding documentation
- [x] If relevant, you have written a first draft summary describing the change for inclusion in Release Notes. 

In this PR, please also make sure: 

- [ ] You have linked this PR to the issue by putting `closes #TICKETNUMBER` in the description box (when applicable)
- [ ] You have added a concise description on your changes
## Release Note Draft Snippet

__If relevant, please write a summary of your change that will be suitable for
inclusion in the Release Notes for the next Taquito release.__

Overall error improvement of @taquito/ledger-signer and @taquito/remote-signer packages

BREAKING CHANGE: 1. In @taquito/remote-signer KeyNotFoundError renamed to PublicKeyNotFoundError. 2. In @taquito/remote-signer PublicKeyMismatch renamed to PublicKeyVerificationError. 3. In @taquito/remote-signer SignatureVerificationFailedError renamed to SignatureVerificationError

Patch:
diff --git a/packages/taquito-ledger-signer/src/error.ts b/packages/taquito-ledger-signer/src/error.ts
deleted file mode 100644
index 59e8c1aa8..000000000
--- a/packages/taquito-ledger-signer/src/error.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- *  @category Error
- *  @description Error that indicates an invalid or unparseable ledger response
- */
-export class InvalidLedgerResponseError extends Error {
-  public name = 'InvalidLedgerResponseError';
-  constructor(public message: string) {
-    super(message);
-  }
-}
-
-/**
- *  @category Error
- *  @description Error that indicates a failure when trying to retrieve a Public Key from Ledger signer
- */
-export class PublicKeyRetrievalError extends Error {
-  public name = 'PublicKeyRetrievalError';
-  constructor() {
-    super(`Unable to retrieve Public Key from Ledger`);
-  }
-}
-
-/**
- *  @category Error
- *  @description Error that indicates a failure when trying to retrieve a Public Key Hash from Ledger signer
- */
-export class PublicKeyHashRetrievalError extends Error {
-  public name = 'PublicKeyHashRetrievalError';
-  constructor() {
-    super(`Unable to retrieve Public Key Hash from Ledger`);
-  }
-}
diff --git a/packages/taquito-ledger-signer/src/errors.ts b/packages/taquito-ledger-signer/src/errors.ts
new file mode 100644
index 000000000..ef64e350c
--- /dev/null
+++ b/packages/taquito-ledger-signer/src/errors.ts
@@ -0,0 +1,48 @@
+import { ParameterValidationError, TaquitoError } from '@taquito/core';
+
+/**
+ *  @category Error
+ *  @description Error indicates an invalid or unparseable ledger response
+ */
+export class InvalidLedgerResponseError extends TaquitoError {
+  constructor(public message: string) {
+    super();
+    this.name = 'InvalidLedgerResponseError';
+  }
+}
+
+/**
+ *  @category Error
+ *  @description Error indicates a failure when trying to retrieve a Public Key from Ledger signer
+ */
+export class PublicKeyRetrievalError extends TaquitoError {
+  constructor(public cause: any) {
+    super();
+    this.name = 'PublicKeyRetrievalError';
+    this.message = `Unable to retrieve Public Key from Ledger`;
+  }
+}
+
+/**
+ *  @category Error
+ *  @description Error indicates a failure when trying to retrieve a Public Key Hash from Ledger signer
+ */
+export class PublicKeyHashRetrievalError extends TaquitoError {
+  constructor() {
+    super();
+    this.name = 'PublicKeyHashRetrievalError';
+    this.message = 'Unable to retrieve Public Key Hash from Ledger';
+  }
+}
+
+/**
+ *  @category Error
+ *  @description Error indicates an invalid derivation type being passed or used
+ */
+export class InvalidDerivationTypeError extends ParameterValidationError {
+  constructor(public derivationType: string) {
+    super();
+    this.name = 'InvalidDerivationTypeError';
+    this.message = `Invalid derivation type ${derivationType} expecting one of the following: DerivationType.ED25519, DerivationType.SECP256K1, DerivationType.P256 or DerivationType.BIP32_ED25519`;
+  }
+}
diff --git a/packages/taquito-ledger-signer/src/taquito-ledger-signer.ts b/packages/taquito-ledger-signer/src/taquito-ledger-signer.ts
index 02d4fef22..e1f62030a 100644
--- a/packages/taquito-ledger-signer/src/taquito-ledger-signer.ts
+++ b/packages/taquito-ledger-signer/src/taquito-ledger-signer.ts
@@ -19,7 +19,8 @@ import {
   PublicKeyHashRetrievalError,
   PublicKeyRetrievalError,
   InvalidLedgerResponseError,
-} from './error';
+  InvalidDerivationTypeError,
+} from './errors';
 import { InvalidDerivationPathError, ProhibitedActionError } from '@taquito/core';
 
 export { InvalidDerivationPathError } from '@taquito/core';
@@ -33,18 +34,7 @@ export enum DerivationType {
   BIP32_ED25519 = 0x03, // tz1 BIP32
 }
 
-/**
- *  @category Error
- *  @description Error that indicates an invalid derivation type being passed or used
- */
-export class InvalidDerivationTypeError extends Error {
-  public name = 'InvalidDerivationTypeError';
-  constructor(public derivationType: string) {
-    super(
-      `The derivation type ${derivationType} is invalid. The derivation type must be DerivationType.ED25519, DerivationType.SECP256K1, DerivationType.P256 or DerivationType.BIP32_ED25519`
-    );
-  }
-}
+export { InvalidDerivationTypeError } from './errors';
 
 export const HDPathTemplate = (account: number) => {
   return `44'/1729'/${account}'/0'`;
@@ -155,7 +145,7 @@ export class LedgerSigner implements Signer {
       );
       return responseLedger;
     } catch (error) {
-      throw new PublicKeyRetrievalError();
+      throw new PublicKeyRetrievalError(error);
     }
   }
 
@@ -178,7 +168,9 @@ export class LedgerSigner implements Signer {
       signature = ledgerResponse.slice(0, ledgerResponse.length - 2).toString('hex');
     } else {
       if (!validateResponse(ledgerResponse)) {
-        throw new InvalidLedgerResponseError('Cannot parse ledger response');
+        throw new InvalidLedgerResponseError(
+          'Invalid signature return by ledger unable to parse the response'
+        );
       }
       const idxLengthRVal = 3; // Third element of response is length of r value
       const rValue = extractValue(idxLengthRVal, ledgerResponse);
diff --git a/packages/taquito-remote-signer/package.json b/packages/taquito-remote-signer/package.json
index 01bab20ab..98b27be26 100644
--- a/packages/taquito-remote-signer/package.json
+++ b/packages/taquito-remote-signer/package.json
@@ -63,6 +63,7 @@
   "dependencies": {
     "@stablelib/blake2b": "^1.0.1",
     "@stablelib/ed25519": "^1.0.3",
+    "@taquito/core": "^16.2.0",
     "@taquito/http-utils": "^16.2.0",
     "@taquito/taquito": "^16.2.0",
     "@taquito/utils": "^16.2.0",
diff --git a/packages/taquito-remote-signer/src/errors.ts b/packages/taquito-remote-signer/src/errors.ts
index 21ac38a50..bf91a348b 100644
--- a/packages/taquito-remote-signer/src/errors.ts
+++ b/packages/taquito-remote-signer/src/errors.ts
@@ -1,48 +1,67 @@
+import { PermissionDeniedError, TaquitoError } from '@taquito/core';
+
+/**
+ *  @category Error
+ *  @description Error indicates a failure in grabbing the public key
+ */
+export class PublicKeyNotFoundError extends TaquitoError {
+  constructor(public pkh: string, public cause: any) {
+    super();
+    this.name = 'KeyNotFoundError';
+    this.message = `Public key not found of this address "${pkh}".`;
+  }
+}
+
 /**
  *  @category Error
- *  @description Error that indicates a failure in grabbing the public key
+ *  @description Error indicates an unauthorized operation being attempted
  */
-export class KeyNotFoundError extends Error {
-  public name = 'KeyNotFoundError';
-  constructor(public message: string, public innerException: any) {
-    super(message);
+export class OperationNotAuthorizedError extends PermissionDeniedError {
+  constructor(public message: string, public cause: any) {
+    super();
+    this.name = 'OperationNotAuthorized';
   }
 }
 
 /**
  *  @category Error
- *  @description Error that indicates an unauthorized operation being attempted
+ *  @description Error indicates bad signing data
  */
-export class OperationNotAuthorizedError extends Error {
-  public name = 'OperationNotAuthorized';
-  constructor(public message: string, public innerException: any) {
-    super(message);
+export class BadSigningDataError extends TaquitoError {
+  constructor(public cause: any, public bytes: string, watermark?: Uint8Array) {
+    super();
+    this.name = 'BadSigningData';
+    this.message = watermark
+      ? `Invalid signing data with watermark`
+      : `Invalid signing data: "${bytes}"`;
   }
 }
 
 /**
  *  @category Error
- *  @description Error that indicates bad signing data
+ *  @description Error indicates a mismatch between the initialized and the requested public key
  */
-export class BadSigningDataError extends Error {
-  public name = 'BadSigningData';
-  constructor(public message: string, public innerException: any, public readonly data: any) {
-    super(message);
+export class PublicKeyVerificationError extends TaquitoError {
+  constructor(
+    public requestedPk: string,
+    public requestedPkh: string,
+    public initializedPkh: string
+  ) {
+    super();
+    this.name = 'PublicKeyVerificationFailedError';
+    this.message = `Requested pk "${requestedPk}" has pkh "${requestedPkh}" deesn't match initialized pkh "${initializedPkh}."`;
   }
 }
 
 /**
  *  @category Error
- *  @description Error that indicates a mismatch between the initialized and the requested public key
+ *  @description Error
  */
-export class PublicKeyMismatch extends Error {
-  public name = 'PublicKeyMismatch';
-  constructor(public requested: string, initialized: string) {
-    super(
-      `Requested public key hash does not match the initialized public key hash: {
-        requested: ${requested},
-        initialized: ${initialized}
-      }`
-    );
+export class SignatureVerificationError extends TaquitoError {
+  public name = 'SignatureVerificationFailedError';
+  constructor(public bytes: string, public signature: string) {
+    super();
+    this.name = 'SignatureVerificationFailedError';
+    this.message = `Invalid signature of bytes failed verification agaisnt public key.`;
   }
 }
diff --git a/packages/taquito-remote-signer/src/taquito-remote-signer.ts b/packages/taquito-remote-signer/src/taquito-remote-signer.ts
index 578b9d709..a1e43d5a0 100644
--- a/packages/taquito-remote-signer/src/taquito-remote-signer.ts
+++ b/packages/taquito-remote-signer/src/taquito-remote-signer.ts
@@ -20,32 +20,14 @@ import { hash } from '@stablelib/blake2b';
 import toBuffer from 'typedarray-to-buffer';
 import {
   BadSigningDataError,
-  KeyNotFoundError,
+  PublicKeyNotFoundError,
   OperationNotAuthorizedError,
-  PublicKeyMismatch,
+  PublicKeyVerificationError,
+  SignatureVerificationError,
 } from './errors';
 import { Signer } from '@taquito/taquito';
 import { InvalidSignatureError, InvalidKeyHashError, ProhibitedActionError } from '@taquito/core';
 
-/**
- *  @category Error
- *  @description Error
- */
-export class SignatureVerificationFailedError extends Error {
-  public name = 'SignatureVerificationFailedError';
-  constructor(public bytes: string, public signature: string) {
-    super(
-      `
-        Signature failed verification against public key:
-        {
-          bytes: ${bytes},
-          signature: ${signature}
-        }
-      `
-    );
-  }
-}
-
 interface PublicKeyResponse {
   public_key: string;
 }
@@ -116,7 +98,7 @@ export class RemoteSigner implements Signer {
     } catch (ex) {
       if (ex instanceof HttpResponseError) {
         if (ex.status === STATUS_CODE.NOT_FOUND) {
-          throw new KeyNotFoundError(`Key not found: ${this.pkh}`, ex);
+          throw new PublicKeyNotFoundError(this.pkh, ex);
         }
       }
       throw ex;
@@ -159,7 +141,7 @@ export class RemoteSigner implements Signer {
       await this.verifyPublicKey(pk);
       const signatureVerified = verifySignature(watermarkedBytes, pk, signature);
       if (!signatureVerified) {
-        throw new SignatureVerificationFailedError(watermarkedBytes, signature);
+        throw new SignatureVerificationError(watermarkedBytes, signature);
       }
 
       return {
@@ -171,14 +153,11 @@ export class RemoteSigner implements Signer {
     } catch (ex) {
       if (ex instanceof HttpResponseError) {
         if (ex.status === STATUS_CODE.NOT_FOUND) {
-          throw new KeyNotFoundError(`Key not found: ${this.pkh}`, ex);
+          throw new PublicKeyNotFoundError(this.pkh, ex);
         } else if (ex.status === STATUS_CODE.FORBIDDEN) {
           throw new OperationNotAuthorizedError('Signing Operation not authorized', ex);
         } else if (ex.status === STATUS_CODE.BAD_REQUEST) {
-          throw new BadSigningDataError('Invalid data', ex, {
-            bytes,
-            watermark,
-          });
+          throw new BadSigningDataError(ex, bytes, watermark);
         }
       }
       throw ex;
@@ -191,7 +170,7 @@ export class RemoteSigner implements Signer {
 
     const publicKeyHash = b58cencode(hash(_publicKey, 20), pref[curve].pkh);
     if (publicKeyHash !== this.pkh) {
-      throw new PublicKeyMismatch(publicKeyHash, this.pkh);
+      throw new PublicKeyVerificationError(publicKey, publicKeyHash, this.pkh);
     }
   }
 }


Test patch:
diff --git a/packages/taquito-remote-signer/test/taquito-remote-signer.spec.ts b/packages/taquito-remote-signer/test/taquito-remote-signer.spec.ts
index 6d9a1572b..64a9977cf 100644
--- a/packages/taquito-remote-signer/test/taquito-remote-signer.spec.ts
+++ b/packages/taquito-remote-signer/test/taquito-remote-signer.spec.ts
@@ -1,5 +1,9 @@
 import { HttpResponseError, STATUS_CODE } from '@taquito/http-utils';
-import { BadSigningDataError, KeyNotFoundError, OperationNotAuthorizedError } from '../src/errors';
+import {
+  BadSigningDataError,
+  PublicKeyNotFoundError,
+  OperationNotAuthorizedError,
+} from '../src/errors';
 import { RemoteSigner } from '../src/taquito-remote-signer';
 
 /**
@@ -149,7 +153,7 @@ describe('RemoteSigner test', () => {
         signer.sign(
           '0365cac93523b8c10346c0107cfea5e12ff3c759459020e532f299e2f41082f7cb6d0000f68c4abfa21dfc0c9efcf588190388cac85d9db60f81d6038b79d8030000000000b902000000b405000764045b0000000a2564656372656d656e74045b0000000a25696e6372656d656e740501035b0502020000008503210317057000010321057100020316072e020000002b032105700002032105710003034203210317057000010321057100020316034b051f020000000405200002020000002b0321057000020321057100030342032103170570000103210571000203160312051f0200000004052000020321053d036d0342051f020000000405200003000000020000'
         )
-      ).rejects.toThrowError(/Signature failed verification against public key/);
+      ).rejects.toThrowError(/Invalid signature of bytes failed verification agaisnt public key/);
 
       done();
     });
@@ -240,7 +244,7 @@ describe('RemoteSigner test', () => {
       try {
         await signer.publicKey();
       } catch (error: any) {
-        expect(error).toBeInstanceOf(KeyNotFoundError);
+        expect(error).toBeInstanceOf(PublicKeyNotFoundError);
       }
       done();
     });