Repository: ecadlabs/taquito (ts)

Issue:
Provide appropriate error based on Taquito context when the RPC returns an exception
_Follow up of https://github.com/ecadlabs/taquito/issues/160_ as described in https://hackmd.io/@tca0T8LZT3au8LG6hSX20g/r1zTRTlfj_

Task 5: Pay specific attention to the error returned by the RPC. Sometimes, the error is vague and not informative enough according to the context. We should provide a custom error with additional information in those cases. 

TODO: use Taquito in different ways to generate failure from the RPC, inspect the error coming from the RPC, identify error messages that would benefit from clarification, and make the appropriate changes. Here are two examples that need to be addressed: 
- When trying to fetch a big map value using a key of the wrong type (for example, using X where a nat was expected), an error from the node bubble up and is hard to diagnose: 
```
    Uncaught (in promise) HttpResponse: Http error response: (400) Failed to parse the request body: No case matched:
      Unhandled error (Invalid_argument "Z.of_substring_base: invalid digit")
      Missing object field string
      Missing object field bytes
      Unexpected object instead of array
      Missing object field prim
        at HttpBackend.<anonymous> (http://localhost:3030/node_modules/.vite/deps/chunk-V4G7R2DB.js?v=4243d8f3:1552:17)
        at Generator.throw (<anonymous>)
        at rejected (http://localhost:3030/node_modules/.vite/deps/chunk-V4G7R2DB.js?v=4243d8f3:1390:32)
```
    
- An error comes from the RPC when estimating an operation using an account with a balance too low (https://github.com/ecadlabs/taquito/issues/363). TODO: validate if the behavior is still the same; the issue was opened in 2020.
```
    HttpResponseError {
      message: 'Http error response: (400) Failed to parse the request body: No case matched:\n' +
        '  At /kind, unexpected string instead of endorsement\n' +
        '  At /kind, unexpected string instead of seed_nonce_revelation\n' +
        '  At /kind, unexpected string instead of double_endorsement_evidence\n' +
        '  At /kind, unexpected string instead of double_baking_evidence\n' +
        '  At /kind, unexpected string instead of activate_account\n' +
        '  At /kind, unexpected string instead of proposals\n' +
        '  At /kind, unexpected string instead of ballot\n' +
        '  At /kind, unexpected string instead of reveal\n' +
        '  At /destination:\n' +
        '    Unhandled error (Failure "Invalid contract notation.")\n' +
        '  At /kind, unexpected string instead of origination\n' +
        '  At /kind, unexpected string instead of delegation',
      status: 400,
      statusText: 'Bad Request',
      body: 'Failed to parse the request body: No case matched:\n' +
        '  At /kind, unexpected string instead of endorsement\n' +
        '  At /kind, unexpected string instead of seed_nonce_revelation\n' +
        '  At /kind, unexpected string instead of double_endorsement_evidence\n' +
        '  At /kind, unexpected string instead of double_baking_evidence\n' +
        '  At /kind, unexpected string instead of activate_account\n' +
        '  At /kind, unexpected string instead of proposals\n' +
        '  At /kind, unexpected string instead of ballot\n' +
        '  At /kind, unexpected string instead of reveal\n' +
        '  At /destination:\n' +
        '    Unhandled error (Failure "Invalid contract notation.")\n' +
        '  At /kind, unexpected string instead of origination\n' +
        '  At /kind, unexpected string instead of delegation',
      name: 'HttpResponse'
    }
```





Pull request description:
1996 rpc 2
closes #1996 

Thank you for your contribution to Taquito.

Before submitting this PR, please make sure:

- [ ] Your code builds cleanly without any errors or warnings
- [ ] 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
- [ ] 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.__


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7e11d2603..6e22b083c 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -43,8 +43,6 @@ jobs:
     strategy:
       matrix:
         include:
-          - testnet: mumbainet
-            testnet_uppercase: MUMBAINET
           - testnet: nairobinet
             testnet_uppercase: NAIROBINET
     steps:
@@ -71,14 +69,10 @@ jobs:
     strategy:
       matrix:
         include:
-          - protocol: Mumbai
-            testnet: mumbainet
-            testnet_uppercase: MUMBAINET
-            flextesa_docker_image: oxheadalpha/flextesa:20230313
           - protocol: Nairobi
             testnet: nairobinet
             testnet_uppercase: NAIROBINET
-            flextesa_docker_image: oxheadalpha/flextesa:20230502
+            flextesa_docker_image: oxheadalpha/flextesa:20230607
     steps:
     - uses: actions/checkout@v3
     - uses: actions/setup-node@v3
@@ -119,19 +113,19 @@ jobs:
           --protocol-kind ${{ matrix.protocol }}
     - run: npm ci
     - run: npm run build
-    - run: npm -w integration-tests run test:${{ matrix.testnet }}-secret-key sandbox-ballot-operation.spec.ts
+    - run: npm -w integration-tests run test:originate-known-contracts && npm -w integration-tests run test:${{ matrix.testnet }}-secret-key -- --testPathIgnorePatterns ledger-signer-failing-tests.spec.ts ledger-signer.spec.ts contract-estimation-tests.spec.ts rpc-get-protocol-constants.spec.ts sandbox-ballot-operation.spec.ts
       env:
         RUN_${{ matrix.testnet_uppercase }}_WITH_SECRET_KEY: true
-        SECRET_KEY: edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq
+        SECRET_KEY: edsk3S8mG2sSBmSRbikAcZVLCz4SrCq4DjmsQRic6MGktqNFijfrS2
         TEZOS_RPC_${{ matrix.testnet_uppercase }}: http://localhost:20000
         POLLING_INTERVAL_MILLISECONDS: 100
         RPC_CACHE_MILLISECONDS: 0
         TEZOS_BAKER: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
-    - run: npm -w integration-tests run test:originate-known-contracts && npm -w integration-tests run test:${{ matrix.testnet }}-secret-key -- --testPathIgnorePatterns ledger-signer-failing-tests.spec.ts ledger-signer.spec.ts contract-estimation-tests.spec.ts rpc-get-protocol-constants.spec.ts sandbox-ballot-operation.spec.ts
+    - run: npm -w integration-tests run test:${{ matrix.testnet }}-secret-key sandbox-ballot-operation.spec.ts
       env:
         RUN_${{ matrix.testnet_uppercase }}_WITH_SECRET_KEY: true
-        SECRET_KEY: edsk3S8mG2sSBmSRbikAcZVLCz4SrCq4DjmsQRic6MGktqNFijfrS2
+        SECRET_KEY: edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq
         TEZOS_RPC_${{ matrix.testnet_uppercase }}: http://localhost:20000
         POLLING_INTERVAL_MILLISECONDS: 100
         RPC_CACHE_MILLISECONDS: 0
-        TEZOS_BAKER: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
+        TEZOS_BAKER: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
\ No newline at end of file
diff --git a/cspell.json b/cspell.json
index 7d3daae4f..288f3c363 100644
--- a/cspell.json
+++ b/cspell.json
@@ -1,100 +1,108 @@
 {
-    "words": [
-        "airgap",
-        "annots",
-        "Annot",
-        "bignumber",
-        "beaconwallet",
-        "bigmap",
-        "BIGMAPCONTRACT",
-        "bigmaps",
-        "Bjorsell",
-        "Boissonneault",
-        "camelcase",
-        "crios",
-        "dailynet",
-        "delphinet",
-        "ecad",
-        "ecadlabs",
-        "edonet",
-        "Ejara",
-        "entrypoints",
-        "FAILWITH",
-        "florencenet",
-        "fxios",
-        "ghostnet",
-        "granadanet",
-        "Guarda",
-        "Hackathon",
-        "inmemory",
-        "IPFS",
-        "ithacanet",
-        "jakartanet",
-        "Kernaghan",
-        "kukai",
-        "lambda",
-        "Letourneau",
-        "Ligo",
-        "micheline",
-        "Micheline",
-        "michelsonmap",
-        "mligo",
-        "mondaynet",
-        "mumbainet",
-        "mutez",
-        "nairobinet",
-        "precommit",
-        "println",
-        "Protofire",
-        "PtNairobi",
-        "rollups",
-        "Roxane",
-        "SAPLINGCONTRACT",
-        "Sawali",
-        "SCORU",
-        "smartcontracts",
-        "staker",
-        "taquito",
-        "tezbridge",
-        "tezos",
-        "ticketer",
-        "toolbelt",
-        "TORU",
-        "typecheck",
-        "typeof",
-        "tzip",
-        "unopt",
-        "Umami",
-        "UNPAIR"
-    ],
-    "files": [
-        "**/*.ts",
-        "**/*.tsx",
-        "**/*.toml",
-        "**/*.md",
-        "**/*.mdx",
-        "**/*.yml",
-        "**/*.yaml",
-        "**/*.sh",
-        "**/*.svelte"
-    ],
-    "ignorePaths": [
-        "**/node_modules/**",
-        "**/dist/**",
-        "**/coverage/**",
-        "**/docs/**",
-        "**/build/**",
-        "**/scripts/**",
-        "**/examples/**",
-        "**/packages/**"
-    ],
-    "ignoreRegExpList": [
-        "KT1[a-zA-Z0-9]{33}",
-        "o[a-zA-Z0-9]{50}",
-        "tz[1-4][a-zA-Z0-9]{33}",
-        "tx[a-zA-Z0-9]{35}",
-        "B[LMK][a-zA-Z0-9]{49}",
-        "[\"':][a-zA-Z0-9]{54}[\"':]",
-        "[\"':][a-zA-Z0-9]{51}[\"':]"
-    ]
-}
+  "words": [
+      "airgap",
+      "Annot",
+      "annots",
+      "beaconwallet",
+      "bigmap",
+      "BIGMAPCONTRACT",
+      "bigmaps",
+      "bignumber",
+      "Bjorsell",
+      "Boissonneault",
+      "camelcase",
+      "crios",
+      "dailynet",
+      "delphinet",
+      "ecad",
+      "ecadlabs",
+      "edonet",
+      "edsk",
+      "Ejara",
+      "entrypoints",
+      "FAILWITH",
+      "flextesa",
+      "florencenet",
+      "fxios",
+      "ghostnet",
+      "granadanet",
+      "Guarda",
+      "Hackathon",
+      "inmemory",
+      "IPFS",
+      "ithacanet",
+      "jakartanet",
+      "Kernaghan",
+      "kukai",
+      "lambda",
+      "Letourneau",
+      "Ligo",
+      "micheline",
+      "Micheline",
+      "michelsonmap",
+      "mligo",
+      "mondaynet",
+      "mumbainet",
+      "mutez",
+      "nairobibox",
+      "nairobinet",
+      "octez",
+      "oxheadalpha",
+      "precommit",
+      "println",
+      "Protofire",
+      "PtNairobi",
+      "rollups",
+      "Roxane",
+      "SAPLINGCONTRACT",
+      "Sawali",
+      "SCORU",
+      "smartcontracts",
+      "staker",
+      "taquito",
+      "tcli",
+      "tezbridge",
+      "tezos",
+      "ticketer",
+      "toolbelt",
+      "TORU",
+      "typecheck",
+      "typeof",
+      "tzip",
+      "Umami",
+      "unopt",
+      "UNPAIR"
+  ],
+  "files": [
+      "**/*.ts",
+      "**/*.tsx",
+      "**/*.toml",
+      "**/*.md",
+      "**/*.mdx",
+      "**/*.yml",
+      "**/*.yaml",
+      "**/*.sh",
+      "**/*.svelte"
+  ],
+  "ignorePaths": [
+      "**/node_modules/**",
+      "**/dist/**",
+      "**/coverage/**",
+      "**/docs/**",
+      "**/build/**",
+      "**/scripts/**",
+      "**/examples/**",
+      "**/packages/**"
+  ],
+  "ignoreRegExpList": [
+      "KT1[a-zA-Z0-9]{33}",
+      "o[a-zA-Z0-9]{50}",
+      "tz[1-4][a-zA-Z0-9]{33}",
+      "tx[a-zA-Z0-9]{35}",
+      "B[LMK][a-zA-Z0-9]{49}",
+      "Pt[a-zA-Z0-9]{49}",
+      "edsk[a-zA-Z0-9]{50}",
+      "[\"':][a-zA-Z0-9]{54}[\"':]",
+      "[\"':][a-zA-Z0-9]{51}[\"':]"
+  ]
+}
\ No newline at end of file
diff --git a/packages/taquito-core/src/errors.ts b/packages/taquito-core/src/errors.ts
index 16db5b734..b6116aeb0 100644
--- a/packages/taquito-core/src/errors.ts
+++ b/packages/taquito-core/src/errors.ts
@@ -72,6 +72,18 @@ export class InvalidBlockHashError extends ParameterValidationError {
   }
 }
 
+/**
+ * @category Error
+ * @description Error indicates an invalid amount of tez being passed as a parameter
+ */
+export class InvalidAmountError extends ParameterValidationError {
+  constructor(public amount: string) {
+    super();
+    this.name = 'InvalidAmountError';
+    this.message = `Invalid amount "${amount}"`;
+  }
+}
+
 /**
  *  @category Error
  *  @description Error indicates an invalid derivation path being passed or used
diff --git a/packages/taquito/src/batch/rpc-batch-provider.ts b/packages/taquito/src/batch/rpc-batch-provider.ts
index d7d16a8b8..593c298da 100644
--- a/packages/taquito/src/batch/rpc-batch-provider.ts
+++ b/packages/taquito/src/batch/rpc-batch-provider.ts
@@ -30,7 +30,12 @@ import { OpKind } from '@taquito/rpc';
 import { ContractMethodObject } from '../contract/contract-methods/contract-method-object-param';
 import { validateAddress, validateKeyHash, ValidationResult, invalidDetail } from '@taquito/utils';
 import { EstimationProvider } from '../estimate/estimate-provider-interface';
-import { InvalidAddressError, InvalidKeyHashError, InvalidOperationKindError } from '@taquito/core';
+import {
+  InvalidAddressError,
+  InvalidKeyHashError,
+  InvalidOperationKindError,
+  InvalidAmountError,
+} from '@taquito/core';
 import { Provider } from '../provider';
 import { PrepareProvider } from '../prepare';
 
@@ -63,6 +68,9 @@ export class OperationBatch extends Provider {
    */
   withTransfer(params: TransferParams) {
     const toValidation = validateAddress(params.to);
+    if (params.amount < 0) {
+      throw new InvalidAmountError(params.amount.toString());
+    }
     if (toValidation !== ValidationResult.VALID) {
       throw new InvalidAddressError(params.to, invalidDetail(toValidation));
     }
diff --git a/packages/taquito/src/contract/rpc-contract-provider.ts b/packages/taquito/src/contract/rpc-contract-provider.ts
index d3b9c7f75..f6297658c 100644
--- a/packages/taquito/src/contract/rpc-contract-provider.ts
+++ b/packages/taquito/src/contract/rpc-contract-provider.ts
@@ -24,7 +24,11 @@ import {
   validateContractAddress,
   ValidationResult,
 } from '@taquito/utils';
-import { InvalidAddressError, InvalidContractAddressError } from '@taquito/core';
+import {
+  InvalidAddressError,
+  InvalidContractAddressError,
+  InvalidAmountError,
+} from '@taquito/core';
 import { OperationBatch } from '../batch/rpc-batch-provider';
 import { Context } from '../context';
 import { DelegateOperation } from '../operations/delegate-operation';
@@ -366,7 +370,9 @@ export class RpcContractProvider extends Provider implements ContractProvider, S
     if (params.source && sourceValidation !== ValidationResult.VALID) {
       throw new InvalidAddressError(params.source, invalidDetail(sourceValidation));
     }
-
+    if (params.amount < 0) {
+      throw new InvalidAmountError(params.amount.toString());
+    }
     const publicKeyHash = await this.signer.publicKeyHash();
     const estimate = await this.estimate(params, this.estimator.transfer.bind(this.estimator));
 
@@ -393,9 +399,9 @@ export class RpcContractProvider extends Provider implements ContractProvider, S
     if (destinationValidation !== ValidationResult.VALID) {
       throw new InvalidAddressError(params.destination, invalidDetail(destinationValidation));
     }
-    const srouceValidation = validateAddress(params.source ?? '');
-    if (params.source && srouceValidation !== ValidationResult.VALID) {
-      throw new InvalidAddressError(params.source, invalidDetail(srouceValidation));
+    const sourceValidation = validateAddress(params.source ?? '');
+    if (params.source && sourceValidation !== ValidationResult.VALID) {
+      throw new InvalidAddressError(params.source, invalidDetail(sourceValidation));
     }
 
     const publicKeyHash = await this.signer.publicKeyHash();
@@ -483,6 +489,9 @@ export class RpcContractProvider extends Provider implements ContractProvider, S
    * @param params increasePaidStorage operation parameter
    */
   async increasePaidStorage(params: IncreasePaidStorageParams) {
+    if (params.amount < 0) {
+      throw new InvalidAmountError(params.amount.toString());
+    }
     const publicKeyHash = await this.signer.publicKeyHash();
     const estimate = await this.estimate(
       params,
diff --git a/packages/taquito/src/estimate/rpc-estimate-provider.ts b/packages/taquito/src/estimate/rpc-estimate-provider.ts
index 790c9418b..9fe6b220c 100644
--- a/packages/taquito/src/estimate/rpc-estimate-provider.ts
+++ b/packages/taquito/src/estimate/rpc-estimate-provider.ts
@@ -28,7 +28,7 @@ import { ContractMethod, ContractMethodObject, ContractProvider } from '../contr
 import { Provider } from '../provider';
 import { PrepareProvider } from '../prepare/prepare-provider';
 import { PreparedOperation } from '../prepare';
-import { InvalidAddressError } from '@taquito/core';
+import { InvalidAddressError, InvalidAmountError } from '@taquito/core';
 
 // RPC requires a signature but does not verify it
 const SIGNATURE_STUB =
@@ -173,6 +173,9 @@ export class RPCEstimateProvider extends Provider implements EstimationProvider
     if (rest.source && srouceValidation !== ValidationResult.VALID) {
       throw new InvalidAddressError(rest.source, invalidDetail(srouceValidation));
     }
+    if (rest.amount < 0) {
+      throw new InvalidAmountError(rest.amount.toString());
+    }
     const preparedOperation = await this.prepare.transaction({
       fee,
       storageLimit,
@@ -365,6 +368,9 @@ export class RPCEstimateProvider extends Provider implements EstimationProvider
    * @param params increasePaidStorage operation parameters
    */
   async increasePaidStorage({ fee, storageLimit, gasLimit, ...rest }: IncreasePaidStorageParams) {
+    if (rest.amount < 0) {
+      throw new InvalidAmountError(rest.amount.toString());
+    }
     const protocolConstants = await this.context.readProvider.getProtocolConstants('head');
     const preparedOperation = await this.prepare.increasePaidStorage({
       fee,


Test patch:
diff --git a/integration-tests/README.md b/integration-tests/README.md
index 844659f20..e6667b784 100644
--- a/integration-tests/README.md
+++ b/integration-tests/README.md
@@ -157,7 +157,7 @@ docker run --rm --name flextesa_sandbox --detach -e block_time=8 -p 20000:20000
 The idea behind Flextesa is to be able to use block times of 1 second. However, the tests as presently written do not always support that rate.
 
 
-Flextesa is the "Flexible Tezos Sandbox" and effectively enables you to run a local copy of the blockchain. Please find [more information about Flextesa here](https://tezos.gitlab.io/flextesa/). Several options are available for controlling block timings.
+Flextesa is the "Flexible Tezos Sandbox" and effectively enables you to run a local emulation of the blockchain. Please find [more information about Flextesa here](https://tezos.gitlab.io/flextesa/). Several options are available for controlling block timings.
 
 
 ### 3. Run the integration tests
@@ -256,8 +256,8 @@ PASS  ./contract-manager-scenario.spec.ts (6.167 s)
 
 ## Testing Baking and Governance Operations with Flextesa
 
-We provide a shell script `integration-tests/sandbox-bakers.sh` for setting up and runnng a mini net of bakers with Flextesa.
-The default vaues for the sandbox include
+We provide a shell script `integration-tests/sandbox-bakers.sh` for setting up and running a mini net of bakers with Flextesa.
+The default values for the sandbox include
   - blocks_per_voting_period=12 
   - extra_dummy_proposals_batch_size=2 
   - extra_dummy_proposals_batch_level=2,4 
diff --git a/integration-tests/contract-estimation-tests.spec.ts b/integration-tests/contract-estimation-tests.spec.ts
index 00040c274..f3bf34503 100644
--- a/integration-tests/contract-estimation-tests.spec.ts
+++ b/integration-tests/contract-estimation-tests.spec.ts
@@ -1,9 +1,10 @@
-import { DEFAULT_FEE, MANAGER_LAMBDA, TezosToolkit } from "@taquito/taquito";
-import { Contract } from "@taquito/taquito";
-import { CONFIGS } from "./config";
-import { originate, originate2, transferImplicit2 } from "./data/lambda";
-import { ligoSample } from "./data/ligo-simple-contract";
-import { managerCode } from "./data/manager_code";
+import { DEFAULT_FEE, MANAGER_LAMBDA, TezosToolkit } from '@taquito/taquito';
+import { Contract } from '@taquito/taquito';
+import { CONFIGS } from './config';
+import { originate, originate2, transferImplicit2 } from './data/lambda';
+import { ligoSample } from './data/ligo-simple-contract';
+import { managerCode } from './data/manager_code';
+import { InvalidAmountError } from '@taquito/core';
 
 CONFIGS().forEach(({ lib, setup, knownBaker, createAddress, rpc }) => {
   const Tezos = lib;
@@ -21,9 +22,9 @@ CONFIGS().forEach(({ lib, setup, knownBaker, createAddress, rpc }) => {
         const transfer = await Tezos.contract.transfer({ to: pkh, mutez: true, amount: amt });
         await transfer.confirmation();
         const op = await Tezos.contract.originate({
-          balance: "1",
+          balance: '1',
           code: managerCode,
-          init: { "string": pkh },
+          init: { 'string': pkh },
         });
         contract = await op.contract();
         contract = await LowAmountTez.contract.at(contract.address);
@@ -64,7 +65,7 @@ CONFIGS().forEach(({ lib, setup, knownBaker, createAddress, rpc }) => {
 
     it('Verify .estimate.originate simple contract', async (done) => {
       const estimate = await LowAmountTez.estimate.originate({
-        balance: "1",
+        balance: '1',
         code: ligoSample,
         storage: 0,
       });
@@ -157,6 +158,13 @@ CONFIGS().forEach(({ lib, setup, knownBaker, createAddress, rpc }) => {
       await op2.confirmation();
       done();
     });
+
+    it('should throw error when trying to estimate transfer with negative amount in param', async (done) => {
+      expect(async () => {
+        const est = await LowAmountTez.estimate.transfer({ to: await Tezos.signer.publicKeyHash(), amount: -1 });
+      }).rejects.toThrowError(InvalidAmountError);
+      done();
+    });
   });
 
 
@@ -217,7 +225,7 @@ CONFIGS().forEach(({ lib, setup, knownBaker, createAddress, rpc }) => {
 
     it('Estimate origination with insufficient balance to pay storage', async (done) => {
       await expect(LowAmountTez.estimate.originate({
-        balance: "0",
+        balance: '0',
         code: ligoSample,
         storage: 0,
       })).rejects.toEqual(
diff --git a/integration-tests/contract-simple-transaction.spec.ts b/integration-tests/contract-simple-transaction.spec.ts
index 478c2d153..d53ef5ba6 100644
--- a/integration-tests/contract-simple-transaction.spec.ts
+++ b/integration-tests/contract-simple-transaction.spec.ts
@@ -1,4 +1,5 @@
 import { CONFIGS } from './config';
+import { InvalidAmountError } from '@taquito/core';
 
 CONFIGS().forEach(({ lib, rpc, setup }) => {
   const Tezos = lib;
@@ -20,5 +21,18 @@ CONFIGS().forEach(({ lib, rpc, setup }) => {
       expect(op.status).toEqual('applied');
       done();
     });
+
+    it('should throw an error when trying to send negative amount', async (done) => {
+      expect(async () => {
+        const op = await Tezos.contract.transfer({
+          amount: -1,
+          to: 'tz4HQ8VeXAyrZMhES1qLMJAc9uAVXjbMpS8u'
+        });
+
+        await op.confirmation();
+      }).rejects.toThrowError(InvalidAmountError);     
+
+      done();
+    })
   });
 });